2017-07-11 60 views
3

我目前正在学习JavaScript,通过下面的书“你不知道JS”系列。什么是允许`p.foo = o.foo`返回对函数`foo`的引用的JavaScript机制/规则?

在“此&对象原型”节中,讨论“到功能间接引用”,作者状态

function foo() { 
    console.log(this.a); 
} 

var a = 2; 
var o = { a: 3, foo: foo }; 
var p = { a: 4 }; 

o.foo(); // 3 
(p.foo = o.foo)(); // 2 

赋值表达式p.foo的结果值时= o.foo是一个 引用只是基础功能对象。因此, 有效的呼叫站点只是foo(),而不是p.foo()或o.foo(),因为您可能需要 。根据上述规则,默认绑定规则适用。

显然,(p.foo = o.foo)返回对函数foo的引用。但是允许(p.foo = o.foo)返回对函数foo的引用的机制/规则是什么?换句话说,为什么简单的赋值返回对foo函数的引用?

+1

因为赋值评估为右侧的*值*。而不是属性引用,因为方法调用'o.foo()'中的'o.foo'会做。 – Bergi

回答

2

当我想了解这样的事情时,我发现将它逐步分解是有帮助的。

  1. o.foo着眼于o对象,并发现了一个名为foo属性。它返回一个引用属性,不管它可能是什么。在这种情况下,o.foo属性是对函数foo的引用。
  2. p.foo = o.foo取自上面的结果(对函数foo的引用),在p对象中创建一个属性,该对象也被命名为foo。所以现在p.foo也是foo函数的参考,与o.foo完全一样。
  3. 该表达式包含在圆括号中,因此现在您可以在=符号的左侧或p.foo的左侧找到它,这是(作为提醒)仍然是对foo函数的引用。
  4. 现在我们在最后找到()。这会调用我们现有的功能。这是foo函数。特别请注意,我们是而不是调用p.foo()。这将是一个方法调用的功能,p.foo是一个参考,所以在该功能中,this将被设置为p。但我们没有这样做。我们只是调用(p.foo = o.foo)返回的任何函数。与以前一样,这与foo函数的功能相同,但我们现在已经失去了与o对象或p对象之间的任何连接。
  5. 因此,当我们在最后打电话时,我们只是调用foo函数,而不将this设置为任何特定对象。因此,当我们拨打电话时,this设置为undefined
  6. 但我们不是在strict模式下运行,所以JavaScript的“有益”不希望给我们一个未定义this,因此它设置this在浏览器中的window对象或节点的global对象。我们之前做过var a = 2;。因此windowglobal对象实际上现在具有名为a的属性,并且该属性的值为2
  7. 因此,现在当我们做console.log(this.a)时,我们从windowglobal对象中选取a属性。该值为2

如果所有这些代码没有在全局级运行,而是在一个函数内呢?那么会发生什么?

function test() { 
 
    function foo() { 
 
    console.log(this.a); 
 
    } 
 
    
 
    var a = 2; 
 
    var o = { a: 3, foo: foo }; 
 
    var p = { a: 4 }; 
 

 
    o.foo(); // 3 
 
    (p.foo = o.foo)(); // was 2, but now is undefined 
 
} 
 

 
test();

现在,当我们调用console.log(this.a);foothis还是指windowglobal对象。但是,当我们设置var a = 2;时,我们不再设置全球财产。我们只是创建一个局部变量。 window.aglobal.aundefined(除非其他代码先前设置了它)。

严格模式避免了一些这种古怪。如果我们将'use strict';置于代码的顶部,它将以严格模式编译。现在,当我们在最后调用foo函数(又不是方法!)的最后一个函数调用时,现在将this设置为undefined而不是windowglobal。因此,当我们尝试拨打console.log(this.a)时,代码失败,因为thisundefined相同,而undefined不具有(也不可能)具有a属性。

让我们试一下:

'use strict'; 
 

 
function foo() { 
 
    console.log(this.a); 
 
} 
 

 
var a = 2; 
 
var o = { a: 3, foo: foo }; 
 
var p = { a: 4 }; 
 

 
o.foo(); // 3 
 
(p.foo = o.foo)(); // was 2 in the original, but now throws an exception

底线,至少在这个特殊的例子:总是用严格的方式!这是你的朋友。

+1

我刚刚发布了一个不完整的答案,基本上来自你的第8步。感谢您理解我无法解决的问题。我知道这是'窗口',但不知道为什么。 Upvoted :-) –

+2

实际上,在第5步中,'this'具体作为'undefined'传递。第8步是无关紧要的 - 它与这种呼叫无关。这是第6步,马虎模式函数将'this'值转换为对象,并用全局对象替换'null'或'undefined'。 – Bergi

+0

谢谢!这就是我所做的“意识流”概览。 :-)我用你的更正更新了它,希望这次更接近它! (对于阅读评论的人,我拿出第8步,所以9是新的8 ...) –

1

赋值运算符=是JavaScript中的一个表达式,它产生(返回)指定的值。因为它是一个表达式,所以可以在任何允许使用表达式的地方使用,例如在括号内。

例如:

let test = (a = b = c = { name: 'test' }) 

上面的代码将首先评估在括号中的表达和指向变量cb,并a到测试对象(以该顺序),那么它会指向test从这个表达式产生的价值。执行该行后,a,b,ctest都将指向同一个对象。

同样,

(p.foo = o.foo) 

会产生o.foo回(在技术上它会产生什么o.foo指向,这是功能foo)。

至于

(p.foo = o.foo)() 

通过添加parenths后附加(),我们告诉我们要调用任何表达(p.foo = o.foo)最终生产发动机。因此我们最终调用foo功能。 Similar patterns is used in IIFEs

一个有用的改写将是思而行的上面这样做:

let produced = (p.foo = o.foo) 
produced() 

Further reading about statements vs expressions

+0

这并不能解释为什么'(p.foo = o.foo)()'''log's 2' –

+0

@FredGandt但这不是问题?问:“但是允许(p.foo = o.foo)返回对函数foo的引用的机制/规则是什么?“ – nem035

+0

公平点(审查);也许它应该是; - ) –

相关问题