函數(shù)傳參
值和引用
JavaScript 中沒有指針,引用的工作機(jī)制也不盡相同。在 JavaScript 中變量不可能成為指向 另一個變量的引用。
JavaScript 引用指向的是值。如果一個值有 10 個引用,這些引用指向的都是同一個值,它 們相互之間沒有引用 / 指向關(guān)系。
JavaScript 對值和引用的賦值 / 傳遞在語法上沒有區(qū)別,完全根據(jù)值的類型來決定。
var a = 2;
var b = a; // b是a的值的一個副本 b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // d是[1,2,3]的一個引用 d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
上例中 2 是一個標(biāo)量基本類型值,所以變量 a 持有該值的一個復(fù)本,b 持有它的另一個復(fù) 本。b 更改時,a 的值保持不變。
c 和 d 則分別指向同一個復(fù)合值 [1,2,3] 的兩個不同引用。請注意,c 和 d 僅僅是指向值 [1,2,3],并非持有。所以它們更改的是同一個值(如調(diào)用 .push(4)),隨后它們都指向更 改后的新值 [1,2,3,4]。
由于引用指向的是值本身而非變量,所以一個引用無法更改另一個引用的指向。
var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// 然后
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
b=[4,5,6] 并不影響 a 指向值 [1,2,3],除非 b 不是指向數(shù)組的引用,而是指向 a 的指針, 但在 JavaScript 中不存在這種情況!
函數(shù)參數(shù)就經(jīng)常讓人產(chǎn)生這樣的困惑:
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[1,2,3,4],不是[4,5,6,7]
我們向函數(shù)傳遞 a 的時候,實(shí)際是將引用 a 的一個復(fù)本賦值給 x,而 a 仍然指向 [1,2,3]。 在函數(shù)中我們可以通過引用x來更改數(shù)組的值(push(4)之后變?yōu)閇1,2,3,4])。但x = [4,5,6] 并不影響 a 的指向,所以 a 仍然指向 [1,2,3,4]。
我們不能通過引用 x 來更改引用 a 的指向,只能更改 a 和 x 共同指向的值。如果要將 a 的值變?yōu)?[4,5,6,7],必須更改 x 指向的數(shù)組,而不是為 x 賦值一個新的數(shù)組。
傳參中有this隱式丟失
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函數(shù)別名!
var a = "oops, global"; // a是全局對象的屬性 bar(); // "oops, global"
雖然 bar 是 obj.foo 的一個引用,但是實(shí)際上,它引用的是 foo 函數(shù)本身,因此此時
的 bar() 其實(shí)是一個不帶任何修飾的函數(shù)調(diào)用,因此應(yīng)用了默認(rèn)綁定。
一種更微妙、更常見并且更出乎意料的情況發(fā)生在傳入回調(diào)函數(shù)時:
var foo = {
bar: function() { return this.baz; },
baz: 1
};
var baz = 2;
(function(fn){
// fn其實(shí)引用的是foo
return fn(); // <-- 調(diào)用位置! fn和foo.bar指向同一塊內(nèi)存函數(shù)。
})(foo.bar); // 2
參數(shù)傳遞其實(shí)就是一種隱式賦值,因此我們傳入函數(shù)時也會被隱式賦值,所以結(jié)果和上一 個例子一樣。并且因?yàn)閠his是動態(tài)作用域所以調(diào)用時候決定值,打印出2
var foo = {
bar: function() { alert(baz); },
baz: 1
};
var baz = 2;
(function(fn){
// fn其實(shí)引用的是foo
var baz = 3;
return fn(); // <-- 調(diào)用位置!
})(foo.bar); // 2 這里不是this所以是詞法作用域解析時候就決定了這里打印2而不是3
使用arguments改變參數(shù)指向環(huán)境
var foo = {
bar: function() { return this.baz; },
baz: 1
};
var baz = 2;
(function(){
arguments.baz = 3;
return arguments[0](); // arguments是一個結(jié)構(gòu)可以轉(zhuǎn)換為數(shù)組的對象。所以這里 arguments[0] 把this上下文指向了 arguments。 不是foo 也不是window。
})(foo.bar); // 3