2015-06-17 43 views
11

所以...ES6¹(發生幾個小時前被標準化)帶來默認參數爲類似於PHP,Python的等等。我可以做的東西一樣的功能:`this`如何在默認參數下工作?

function foo (bar = 'dum') { 
    return bar; 
} 

foo(1); // 1 
foo(); // 'dum' 
foo(undefined); // 'dum' 

MDN說,該參數的默認值在通話時進行評估。這意味着每次我調用該函數時,都會再次評估表達式'dum'(除非實現做了一些我們不關心的奇怪優化)。

我的問題是,this怎麼玩這個?

let x = { 
    foo (bar = this.foo) { 
    return bar; 
    } 
} 

let y = { 
    z: x.foo 
} 

x.foo() === y.z(); // what? 

巴貝爾transpiler目前evaluates²它作爲false,但我不明白這一點。如果他們在通話時間真的評估,你看這個:

let x = 'x from global'; 

function bar (thing = x) { 
    return thing; 
} 

function foo() { 
    let x = 'x from foo'; 
    return bar(); 
} 

bar() === foo(); // what? 

巴貝爾transpiler目前evaluates³它作爲true,但我不明白這一點。爲什麼barfoo內部調用時不能使用foo中的x

1 - 是的,我知道這是ES2015。
2 - Example A
3 - Example B

+0

董事會是否接受了最終的ES6草案? –

+1

@squint規範是住在http://www.ecma-international.org/ecma-262/6.0/index.html :) –

+0

好的,謝謝! –

回答

11

我的問題是,this怎麼玩到這?我不明白。他們是否真的在通話時進行評估?

是的,參數初始值設定項會在通話時進行評估。 It's complicated,但步驟基本如下:

  1. 一個new execution context在調用函數
  2. 如果有必要的「關閉範圍」與new environment建立在棧上,
    ,這是thisBinding is initialised
  3. Declarations are instantiated
    1. 的參數名稱易變的綁定創建
    2. 如果NE cessary,一個arguments對象被創建的綁定
    3. 該從參數列表bindings are iteratively initialised(包括所有destructurings等)
      在此過程中,initialisers are evaluated
    4. 如果任何封閉參與,一個新的環境被插入
    5. 在函數體中聲明的變量可變綁定創建(如果尚未通過參數名來實現),並與undefined
    6. 綁定let和函數體const變量初始化創建
    7. 爲函數(由身體函數聲明)的綁定被初始化與實例化的功能
  4. 最後body of the function is evaluated

所以參數initialisers就可以訪問this和呼叫的arguments,以前初始化等參數,那就是在他們的「上」詞法範圍的一切。它們不受函數體中聲明的變量的影響(雖然它們受所有其他參數影響,即使在它們的臨時死區中)。

你看這個:

function bar (thing = x) {} 
{ 
    let x = 'x from foo'; 
    return bar(); 
} 

我不明白這一點。爲什麼barfoo內部調用 時不能使用foo中的x

因爲xbar無法訪問的局部變量。我們很幸運,他們是not dynamically scoped!參數初始化程序不在呼叫站點進行評估,而是在被調用函數的範圍內進行評估。在這種情況下,x標識符將解析爲全局變量x

+0

如果有人關心,我們在[聊天](http://chat.stackoverflow.com/rooms/17/conversation/continued-discussion-for-how-does-this-work-in-default-參數)如果你想讀:) –

0

當他們說 「在通話時間評估」,我想他們指的是的call-by-名表達。這裏是巴貝爾如何輸出你的第三個例子:

'use strict'; 

var x = 'x from global'; 

function bar() { 
    var thing = arguments[0] === undefined ? x : arguments[0]; 

    return thing; 
} 

function foo() { 
    var x = 'x from foo'; 
    return bar(); 
} 

bar() === foo(); // what? 

由於var xbar從全球範圍內的詞法範圍內的繼承,也就是在其使用範圍。

現在,考慮以下因素:

let i = 0; 

function id() { 
    return i++; 
} 

function bar (thing = id()) { 
    return thing; 
} 

console.info(bar() === bar()); // false 

哪個transpiles到

"use strict"; 

var i = 0; 

function id() { 
    return i++; 
} 

function bar() { 
    var thing = arguments[0] === undefined ? id() : arguments[0]; 

    return thing; 
} 

console.info(bar() === bar()); // false 

注意如何在這裏,id被稱爲的功能,而不是被緩存,並在時間memoized的功能是定義爲,因此是按名稱而不是按值調用。

因此的行爲實際上在第二個例子中是正確的。沒有y.foo既然this在Javascript動態範圍的(即其變化的基礎上給定函數調用的接收器),當y.z()查找this.foo,它會尋找它在y,所以y.z()將返回undefined,而x.foo()只會返回foo函數本身。

如果你想要綁定到接收器,你可以綁定foox當你分配它。然後它應該按預期工作。

對不起,如果有任何不清楚;讓我知道在評論中,我很樂意澄清! :)

+0

_「,因爲這是動態範圍在JavaScript中_,這就是我的問題。不應該特意使用'this.something',因爲'this.something'不是神奇的,只有'this'是。我會添加一個清晰的例子來澄清這個問題,謝謝你的答案! –