4 回答

TA貢獻1843條經驗 獲得超7個贊
函數聲明時設置的默認參數值是在函數調用時計算賦值的,而不是在函數聲明時賦值
我們可以看下下面的例子
function foo2(a, b = (function() { console.log(c); return function(){} })()) {
b();
}
foo2();
調用 foo2();
控制臺將輸出:
Uncaught ReferenceError: c is not defined
at b (<anonymous>:1:47)
at foo2 (<anonymous>:2:5)
at <anonymous>:1:1
而不掉用 foo2();
控制臺將不報錯
以上例子說明了函數參數的默認值是在調用是賦值的,而不是在聲明時。
對于問題的代碼,還有注意一點,y默認值函數聲明中的x是綁定為函數聲明中的參數x變量而不是foo外層作用域中的變量x
function foo(x, y = function() { x = 2; }) {
console.log("x1:"+x);
y();
console.log("x2:"+x);
}
foo();
輸出
x1:undefined
x2:2
那么下面的代碼輸出2就好理解了
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo(); // 2

TA貢獻1783條經驗 獲得超4個贊
看到了很多答案,在大家的提示下 我去查了很多資料現在已經理解了這個結果。
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x // 1
以上的代碼 作用域鏈如下:
以上的圖涉及一個問題,那就是為什么foo的參數居然是自成一個作用域鏈?
這是因為es6規(guī)定:
一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。這種語法行為,在不設置參數默認值時,是不會出現的。
也就是說y中的x=2,因為y中是沒有x變量的,所以向上一級查找,他的上一級就是傳入的參數x,因此y函數的作用改變的是參數x
而當foo內部var x=3時,這時內部的x已經覆蓋掉了參數x,
console.log查找的是它的上一層foo的內部變量作用域x,這個x是等于3的,因此foo執(zhí)行為3,一下貼出作用域鏈的一個概念,覺得解釋的非常好
作用域鏈:作用域鏈是一個對象列表或者鏈表,這組對象定義了這段代碼’作用關于中’的變量。當javascript需要查找變量x的值得時候(變量解析),他會從鏈中的第一個對象開始查找,如果這個對象有一個名為x的屬性,則會直接使用這個屬性的值,如果第一個對象中不存在名為x的屬性,javascript會繼續(xù)查找鏈上的下一個對象。如果第二個對象依然沒有名為x的屬性,則會繼續(xù)查找下一個對象,以此類推。如果作用域鏈上沒有任何一個對象含有屬性x,那么久認為這段代碼的作用域鏈上不存在x,并最終爆出一個引用錯誤異常。(犀牛書P59)
而第二段代碼:
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
作用域鏈如下:
foo中的console.log(x)中的x會查到他的上一級傳入的參數x,因為傳入的參數被y函數改為2,所以輸出2
而第一個例子中的x上一級作用域鏈直接是新定義的x=3所以輸出3
添加回答
舉報