從use strict看JS(二):函數傳參模式與arguments

系列

系列列表:
從use strict看JS(一):this與箭頭函數
從use strict看JS(二):函數傳參模式與argumentsgit

use strict 的 arguments

上一篇說到,use strict對arguments作了如下限定github

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一個變量對應一個值,一個值對應一種類型;而一種類型對應多個值,一個值對應多個變量。
字符串的值不能改變,但能讓變量指向不一樣的值app

舉兩個栗子:函數

no1.this

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

上述例子中,dog和cat是不一樣的變量,但指向同一個值,因此dog改變會反應到cat上。third是另一個變量,指向不一樣的值,而這兩個值有相同的類型。以下圖spa

clipboard.png

no2.prototype

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!

夜已深,就不繼續悟道了。

相關文章
相關標籤/搜索