`this` 在默认参数中如何工作?
How does `this` work in default parameters?
所以... ES6¹(刚好在几个小时前被标准化)为类似于 PHP、[=44= 中的函数带来了 默认参数 ] 等。我可以做类似的事情:
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?
babel 转译器目前将其计算为 false
,但我不明白。如果他们真的在调用时被评估,那么这个呢:
let x = 'x from global';
function bar (thing = x) {
return thing;
}
function foo () {
let x = 'x from foo';
return bar();
}
bar() === foo(); // what?
babel 转译器当前评估³它为 true
,但我不明白。为什么 bar
在 foo
内部调用时不从 foo
获取 x
?
当他们说 "evaluated at call time" 时,我认为他们指的是 call-by-name 表达式。下面是 babel 如何输出你的第三个例子:
'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 x
是在bar
的词法范围内从全局范围继承的,也就是它的使用范围。
现在,考虑以下问题:
let i = 0;
function id() {
return i++;
}
function bar (thing = id()) {
return thing;
}
console.info(bar() === bar()); // false
转译为
"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
是如何在 内部 函数中被调用的,而不是在 定义函数时被缓存和记忆的 ,因此是按名称调用而不是按值调用。
所以在您的第二个示例中 的行为 实际上是正确的。没有 y.foo
并且由于 this
在 Javascript 中是 动态范围 (即它根据给定函数调用的接收者而变化),当y.z()
查找 this.foo
,它将在 y
中查找它,因此 y.z()
将 return undefined
,而 x.foo()
将只是 return foo
函数本身。
如果您想要绑定到接收器,您可以在分配时将foo
绑定到x
。然后它应该按预期工作。
抱歉,如果有任何不清楚的地方;在评论中让我知道,我很乐意澄清! :)
My question is, how does this
play into this? I don't get it. Are they are really evaluated at call time?
是的,参数初始值设定项在调用时计算。 It's complicated,不过步骤基本如下:
- 栈上建立了一个new execution context,
在被调用函数的 "closure scope" 中带有 new environment
- 如果需要的话,就是
thisBinding
is initialised
- Declarations are instantiated:
- 已创建参数名称的可变绑定
- 如有必要,将创建一个
arguments
对象并绑定
- 参数列表中的 bindings are iteratively initialised(包括所有解构等)
在此过程中,initialisers are evaluated
- 如果涉及任何闭包,则会插入一个新环境
- 创建函数体中声明的变量的可变绑定(如果尚未通过参数名称完成)并使用
undefined
初始化
- 函数体中
let
和 const
变量的绑定已创建
- 函数的绑定(来自主体中的函数声明)是用实例化函数初始化的
- 终于body of the function is evaluated.
因此参数初始化器确实可以访问调用的 this
和 arguments
,可以访问先前初始化的其他参数,以及它们 "upper" 词法范围内的所有内容。它们不受函数体中声明的变量的影响(尽管它们受所有其他参数的影响,即使在它们的暂时死区中)。
what about this:
function bar (thing = x) {}
{
let x = 'x from foo';
return bar();
}
I don't get it. Why does bar
not take the x
from foo
when called
inside foo
?
因为x
是bar
无权访问的局部变量。我们很幸运,他们 not dynamically scoped!参数初始值设定项不在调用站点评估,而是在被调用函数的范围内评估。在这种情况下,x
标识符被解析为全局 x
变量。
所以... ES6¹(刚好在几个小时前被标准化)为类似于 PHP、[=44= 中的函数带来了 默认参数 ] 等。我可以做类似的事情:
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?
babel 转译器目前将其计算为 false
,但我不明白。如果他们真的在调用时被评估,那么这个呢:
let x = 'x from global';
function bar (thing = x) {
return thing;
}
function foo () {
let x = 'x from foo';
return bar();
}
bar() === foo(); // what?
babel 转译器当前评估³它为 true
,但我不明白。为什么 bar
在 foo
内部调用时不从 foo
获取 x
?
当他们说 "evaluated at call time" 时,我认为他们指的是 call-by-name 表达式。下面是 babel 如何输出你的第三个例子:
'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 x
是在bar
的词法范围内从全局范围继承的,也就是它的使用范围。
现在,考虑以下问题:
let i = 0;
function id() {
return i++;
}
function bar (thing = id()) {
return thing;
}
console.info(bar() === bar()); // false
转译为
"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
是如何在 内部 函数中被调用的,而不是在 定义函数时被缓存和记忆的 ,因此是按名称调用而不是按值调用。
所以在您的第二个示例中 的行为 实际上是正确的。没有 y.foo
并且由于 this
在 Javascript 中是 动态范围 (即它根据给定函数调用的接收者而变化),当y.z()
查找 this.foo
,它将在 y
中查找它,因此 y.z()
将 return undefined
,而 x.foo()
将只是 return foo
函数本身。
如果您想要绑定到接收器,您可以在分配时将foo
绑定到x
。然后它应该按预期工作。
抱歉,如果有任何不清楚的地方;在评论中让我知道,我很乐意澄清! :)
My question is, how does
this
play into this? I don't get it. Are they are really evaluated at call time?
是的,参数初始值设定项在调用时计算。 It's complicated,不过步骤基本如下:
- 栈上建立了一个new execution context,
在被调用函数的 "closure scope" 中带有 new environment - 如果需要的话,就是
thisBinding
is initialised - Declarations are instantiated:
- 已创建参数名称的可变绑定
- 如有必要,将创建一个
arguments
对象并绑定 - 参数列表中的 bindings are iteratively initialised(包括所有解构等)
在此过程中,initialisers are evaluated - 如果涉及任何闭包,则会插入一个新环境
- 创建函数体中声明的变量的可变绑定(如果尚未通过参数名称完成)并使用
undefined
初始化
- 函数体中
let
和const
变量的绑定已创建 - 函数的绑定(来自主体中的函数声明)是用实例化函数初始化的
- 终于body of the function is evaluated.
因此参数初始化器确实可以访问调用的 this
和 arguments
,可以访问先前初始化的其他参数,以及它们 "upper" 词法范围内的所有内容。它们不受函数体中声明的变量的影响(尽管它们受所有其他参数的影响,即使在它们的暂时死区中)。
what about this:
function bar (thing = x) {} { let x = 'x from foo'; return bar(); }
I don't get it. Why does
bar
not take thex
fromfoo
when called insidefoo
?
因为x
是bar
无权访问的局部变量。我们很幸运,他们 not dynamically scoped!参数初始值设定项不在调用站点评估,而是在被调用函数的范围内评估。在这种情况下,x
标识符被解析为全局 x
变量。