从use strict看JS(二):函数传参模式与arguments

系列

系列列表:
从use strict看JS(一):this与箭头函数
从use strict看JS(二):函数传参模式与arguments

use strict 的 arguments

上一篇说到,use strict对arguments做了以下限定

arguments。不允许对arguments赋值。禁止使用arguments.callee。arguments不再追踪参数的变化

这是为什么呢?如果你明白下面两个例子就不用看这篇文章了~

arguments传的是“值”

function notChangeName(obj){
    obj="change";
}
function changeName(obj){
    obj.name="change";
}
var dog={
    name:"dog"
};
notChangeName(dog);
//输出Object {name: "dog"},没改变
console.log(dog);
changeName(dog);
//输出Object {name: "change"},改了
console.log(dog);

//经典例子,JavaScript高级程序设计的例子
function setName(obj){
    obj.name="doge";
    obj=new Object();
    obj.name="cat";
}
var person={};
setName(person);
//doge
console.log(person.name);

严格模式下arguments会保持对同一“值”的引用

function notChangeName(name){
    "use strict";
    name="cat";
    // 严格模式输出dog,非严格输出cat
    console.log(arguments[0]);
}
function changeName(obj){
    "use strict";
    obj.name="cat";
    // 输出cat
    console.log(arguments[0].name);
}
notChangeName("dog");
changeName({name:"dog"});

function dog(){
    // "use strict";
    //严格模式SyntaxError,非严格输出"a"
    arguments="a";
    console.log(arguments);
}
dog();

arguments & js的变量、值、类型

js的变量、值、类型

js一个变量对应一个值,一个值对应一种类型;而一种类型对应多个值,一个值对应多个变量。
字符串的值不能改变,但能让变量指向不同的值

举两个栗子:
no1.

var dog={
}
var cat=dog;
dog.name="doge";
var third={
    name:"the third man"
}
// 输出doge
console.log(cat.name);

上述例子中,dog和cat是不同的变量,但指向同一个值,所以dog改变会反应到cat上。third是另外一个变量,指向不同的值,而这两个值有相同的类型。如下图

no2.

var str="doge";
// 输出o
console.log(str[1]);
str[1]="c";
// 输出o,字符串的值不能改变
console.log(str[1]);
str="cat";
// 输出c,变量指向了不同的值
console.log(str[0]);

arguments传的是值

所以,在前面的例子中,notChangeName函数内部将变量obj指向不同的值,外部的dog指向同一个值且值未发生变化。因而不能改变dog的name。
changeName函数中,obj和dog指向同一个值,更改obj的name就等于更改了dog的name。
JavaScript高级程序设计的例子同理。如下:

function notChangeName(obj){
    obj="change";
}
function changeName(obj){
    obj.name="change";
}
var dog={
    name:"dog"
};
notChangeName(dog);
//输出Object {name: "dog"},没改变
console.log(dog);
changeName(dog);
//输出Object {name: "change"},改了
console.log(dog);

//经典例子,JavaScript高级程序设计的例子
function setName(obj){
    obj.name="doge";
    obj=new Object();
    obj.name="cat";
}
var person={};
setName(person);
//doge
console.log(person.name);

use strict下的arguments会保持对“值”的引用,因而arguments变量是不能被重新赋值的,如果强制赋值呢?比如arguments=”a”,那就强制报错,没得商量。
也是因为保持对“值”的引用,arguments不再追踪参数的变化,但如果参数是obj且改变的是“值”,而不是重新赋值,那就不一样了。如下:

function notChangeName(name){
    "use strict";
    name="cat";
    // 严格模式输出dog,非严格输出cat
    console.log(arguments[0]);
}
function changeName(obj){
    "use strict";
    obj.name="cat";
    // 输出cat
    console.log(arguments[0].name);
}
notChangeName("dog");
changeName({name:"dog"});

function dog(){
    // "use strict";
    //严格模式SyntaxError,非严格输出"a"
    arguments="a";
    console.log(arguments);
}
dog();

面试会考:arguments不是数组

这就是一个点而已,arguments不是数组,是个类数组对象,没有数组的push、pop、map那些方法,那如何把它变为数组呢?循环遍历一遍也是可以的,arguments有长度,但有更简单的方法,如下:

function argToArr(index){
    var newArg=Array.prototype.slice.apply(arguments);
    newArg.push(2);
}
argToArr(1);

为何不直接

arguments=Array.prototype.slice.apply(arguments);

因为现在很多都用use strict了,这个会报错,还不知道为何会报错的再看一遍文章哈,自觉点

最后谈谈arguments.callee

这个用来干啥呢,它能在函数内部调用自己,常用来解耦和。比如写个斐波那契数列函数

function fib(n){
    if(n===1||n===2){
        return 1;
    }
    return fib(n-1)+fib(n-2);
}

这个问题在哪呢,耦合性太强,改了外层的fib,内部函数名称也得改。可以用arguments.callee改进

function fibArg(n){
    if(n===1||n===2){
        return 1;
    }
    return arguments.callee(n-1)+arguments.callee(n-2);
}

问题是这个在严格模式下不能运行。不绕了,来个终极方法

var fib=(function f(n){
    "use strict";
    if(n===1||n===2){
        return 1;
    }
    return f(n-1)+f(n-2);
})

这种模式之后还会涉及,跟立即执行函数类似,jQuery这些也用了,以后再聊~

等等

好像没谈为何要这样限定arguments!

夜已深,就不继续悟道了。