问题 `this`如何在默认参数中工作?


所以...ES6¹(几小时前恰好标准化)带来了 默认参数 对于类似于PHP,Python等的函数,我可以做类似的事情:

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 不要拿 x 从 foo 在里面打电话 foo

1 - 是的我知道它是ES2015。
2 - 例A
3 - 例B


6426
2018-06-17 20:20


起源

董事会是否接受最终ES6草案的公告?
@squint规范是活的 ecma-international.org/ecma-262/6.0/index.html :) - Awal Garg
很好,谢谢!..
您的比较基本上评估为 x.foo === y.foo 这很清楚 false,因为在这两种情况下你都在调用 foo 功能,但在第一种情况下, this === x 在第二种情况下, this === y。问题似乎真的是,为什么本质上 let x = {foo(){ return this; }}; let y = {z: x.foo}; y.foo() === y。答案是因为 y.foo() 和做的一样 y.foo.call(y)。就是那样 this 被定义为。 - loganfsmyth


答案:


我的问题是,怎么做 this 玩这个?我不明白。他们是否真的在通话时评估?

是的,参数初始值设定项在调用时进行评估。 这很复杂,但步骤基本如下:

  1. 一个 新的执行上下文 在堆栈上建立,
    用一个 新环境 在被调用函数的“闭包范围”中
  2. 如有必要,它就是 thisBinding 初始化
  3. 声明被实例化
    1. 创建参数名称的可变绑定
    2. 如果有必要的话 arguments 对象创建一个绑定
    3. 绑定被迭代初始化 来自参数列表(包括所有解构等)
      在此过程中, 评估初始化者
    4. 如果涉及任何闭包,则插入新环境
    5. 创建函数体中声明的变量的可变绑定(如果尚未通过参数名称完成)并使用初始化 undefined
    6. 绑定 let 和 const 创建函数体中的变量
    7. 函数的绑定(来自正文中的函数声明)使用实例化函数进行初始化
  4. 最后 评估函数的主体

因此参数初始化者可以访问 this 和 arguments 调用,先前初始化的其他参数,以及处于“上限”词法范围内的所有内容。它们不受函数体中声明的变量的影响(尽管它们受所有其他参数的影响,即使它们的临时死区也是如此)。

那这个呢:

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

我不明白。为什么 bar 不要拿 x 从 foo 什么时候叫   内 foo

因为 x 是一个局部变量 bar 没有权限。我们很幸运,他们是 不动态范围!参数初始化程序不在调用站点评估,而是在被调用函数的作用域内评估。在这种情况下, x标识符已解析为全局 x 变量。


11
2018-06-17 21:38



如果有人关心,我们进一步讨论了这一点 聊 如果你想读:) - Awal Garg


当他们说“在通话时评估”时,我认为他们指的是一个 调用按姓名 表达。以下是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() 将返回 undefined,而 x.foo() 只会归还 foo 功能本身。

如果你  想绑定到你可以绑定的接收器 foo 至 x 当你分配它。然后它应该按预期工作。

对不起,如果有任何不清楚的话;请在评论中告诉我,我很乐意澄清! :)


0
2018-06-17 20:47



“因为这是动态范围的Javascript”这就是我的问题所在。运用 this.something 不应该特别采取,因为 this.something 只是不神奇 this 是。我将添加一个清晰的示例来提问以澄清这一点,谢谢你的回答! - Awal Garg