真的被javascript es6 arrow函数的“封闭范围”搞糊涂了

问题描述 投票:5回答:4
  • 我在网上做了很多研究,阅读了很多帖子,包括MDN等。
  • 我理解,对于传统的定义函数,函数中的“this”由调用/调用它们的对象(以及几个不同的情况,对象文字,新构造函数,事件处理程序等)定义。
  • 我理解对于箭头函数,“this”是通过封闭的上下文/范围而不是通过调用它们的对象来词法定义的(虽然我们可以使用传统定义的函数(比如说A)来包装箭头函数(比方说,B)和因此首先将对象引用传递给A,然后传递给B)

这里有问题:

  1. 究竟什么是封闭的背景?这是进一步复杂的,因为ES6允许{}成为块/范围/上下文。 {}作为分隔符足以定义“封闭上下文”,或者它必须在函数范围内。
  2. 一个具体的例子: let EventEmitter = require('events').EventEmitter; class Person extends EventEmitter { constructor(name) { super(); this.name = name; } } let mary = new Person('mary'); mary.on('speak', (said) => { console.log(${this.name}: ${said}); }); mary.emit('speak', 'you may delay, but time will not');

它只是设置自定义事件,并在触发自定义事件时添加回调函数。为什么这里的箭头功能不起作用?

“mary”是调用“on”函数的对象,该函数应将“on”中的“this”设置为“mary”。最重要的是,箭头函数在其参数位置的“on”函数中定义(在词法上足够,对吧?),为什么箭头函数不能从其“封闭的上下文”中得到“this”值,也就是说,这里的“开”功能???

  1. 同样的例子,使用传统的函数定义: let EventEmitter = require('events').EventEmitter; class Person extends EventEmitter { constructor(name) { super(); this.name = name; } } let mary = new Person('mary'); mary.on('speak', function(s) { console.log(this); }); mary.emit('speak', 'you may delay, but time will not');

现在它有效。我理解,鉴于旧的函数定义方式,console.log(this)现在可以动态绑定到调用它的对象。但等等,“mary”是对象,“on”是被调用的直接函数。不应该“on”形成其中的匿名函数的闭包吗?而且我记得嵌套函数中的“this”无法访问其闭包的“this”(封闭的上下文,再次,呵呵),因此不应该得到“mary”引用。它为什么在这里工作?

  1. 当我们在函数中讨论某些事物(例如,A)时,它是否意味着A必须在函数的{}中,或者A也可以在参数/参数区域中?也就是说,function(){A}与function(A){}。
  2. 类似地,如果箭头函数作为函数(()=>()){}之类的参数传递,外部函数是否考虑其封闭范围?或者这种情况下的封闭范围是外部函数的外部?

以上可能听起来很愚蠢。非常感谢你的帮助。

javascript ecmascript-6 this arrow-functions
4个回答
3
投票

我可能在这里没有精确地使用“范围”一词,但基本上只是将范围视为变量名称的映射到它们所指的内存中的位置;嵌套作用域的名称/变量将封闭(又称父作用域)中具有相同名称的阴影(覆盖)关联配对。

function foo() { // this is the "enclosing scope" of bar
  var a = 4    <-----------+
                           |
  var b = a // refers to --+

  function bar() {
    var a = 7    <-----------+
                             |
    var c = a // refers to --+
  }
}

this的行为与上面例子中的a完全相同。

function范围隐式定义了this的引用,但ES2015 lambda范围和块范围没有。如果它们是明确的,那么这些定义会是什么样子:

function foo() { // this is the enclosing scope of baz and the block below
  var this = ...  <-----------+--+
                              |  |
  var b = this // refers to --+  |
                                 |
  {                              |
    var q = this // refers to ---+
  }

  function bar() { // this is the enclosing scope of baz
    var this = ...  <-----------+--+
                                |  |
    var c = this // refers to --+  |
                                   |
    var baz = () => {              |
      var d = this // refers to ---+
    }
  }
}

this在特定范围内引用的内存位置的实际值不是词法定义的;它在运行时设置为调用函数的对象的(内存位置)。但是,另一个引用的阴影总是在词法上定义。


0
投票

箭头函数的this是最近的函数范围,意味着使用function关键字定义的函数的范围。

即使箭头函数以某种方式嵌套,这也适用,如

function scope() {
    const arrow = a => b => this.foo(a, b);
}

这里的thisthisscope

“mary”是调用“on”函数的对象,该函数应将“on”中的“this”设置为“mary”。最重要的是,箭头函数在其参数位置的“on”函数中定义(在词法上足够,对吧?),为什么箭头函数不能从其“封闭的上下文”中得到“this”值,也就是说,这里的“开”功能???

是的,this内的onmary。然而,这并不意味着on的实现将其mary的回调称为this,或任何其他特定的this - 由on的实现定义。此外,管理其“this”的箭头函数的“上下文”不是on,也不是整个mary.on语句,也不是一些封闭的{}块,而是其中发生的函数范围。


0
投票

我很难解密这个概念,但最近却把头脑包裹起来。

所以要理解这一点,我们应该首先了解什么是范围?范围是引擎在查找变量时所遵循的一组规则。所以在实践中,从You don't know JS这本书中可以很好地描述它作为引擎和范围之间的对话。如果你考虑一个简单的左手任务,如let a = 1;,谈话将是这样的:

  • 引擎:嘿范围,有什么叫“a”吗?
  • 范围:不
  • 引擎:很酷,请你记住,从现在开始有一个名为“a”的成员帮我一个忙,请指定它的值“1”;
  • 范围:将是一个编译器

现在想象一下这样的场景:console.log(a)

  • 引擎:嘿范围,听说过控制台
  • 范围:为什么我有,这是一个对象
  • 引擎:很酷,但你也听说过它的属性日志
  • 范围:是的,是的,我做到了
  • 引擎:非常感谢范围,我看到它是一个功能。我有另一个问题,你知道这个名为“a”的成员,它的价值如何?
  • 范围:我也知道,它的价值是1
  • 引擎:好的范围,我已经准备好了。现在调用日志,我正在通过它

在JavaScript中,有不同的方法来定义范围。但是为了简单回答这个问题,我们来谈谈功能范围。 (您也可以阻止定义范围)

// global scope
function foo(a) {
    //foo scope
    var b = a * 2;

    function bar(c) {
        // bar scope
        console.log( a, b, c );
    }

    bar(b * 3);
}

foo( 2 ); // 2 4 12

从上面的例子中我可以很容易地发现2个范围,foo范围和bar范围。

你可以看到在foo中调用bar并且它打印了3个值,ab,它们在fooc的范围内,这是bar的范围。它可以这样做,因为范围查找不会仅在当前范围内发生,它会继续检查它的父范围(封闭范围),直到它到达全局范围。

所以在层次结构中我们会看到类似的东西

  • 全球范围
  • - > foo范围
  • || - >条形范围

在这种情况下,foo范围是bar的封闭范围,因为它实际上包含了内部的条形。


-1
投票

除非你这样做,否则传递给事件发射器回调的函数不会绑定到发射器的作用域,无论它是传统函数还是胖箭头函数。

当他们说胖箭头函数绑定到词法范围时,他们意味着它不会创建自己的新范围。

考虑:

function Bob(){
  this.a = 5;

  function getA() { return this.a;}

  this.getAReal = () => this.a 
}

const bob = new Bob();

bob.getA(); // undefined
bob.getAReal(); // 5
© www.soinside.com 2019 - 2024. All rights reserved.