为什么这个会返回3和1?

5
我理解后面的代码,我们在foo对象上调用警报,该对象有另一个名为baz的对象作为其属性,baz对象又有一个名为bar的方法,该方法返回x的值。由于词法作用域(我想是这样的:)), JS编译器/解释器会沿着链条向上查找,在baz中找到x并返回1。
我猜当被分配给变量go,然后从全局作用域调用时,你得到3? 只是想了解后台发生了什么。任何帮助都将不胜感激!

var x = 3;

var foo = {
    x: 2,
    baz: {
        x: 1,
        bar: function() {
            return this.x;
        }
    }
}

var go = foo.baz.bar;

alert(go());
alert(foo.baz.bar());


1
全都是因为执行上下文...第一个警报显示“3”,因为“this”指的是全局上下文,因此“this.x”返回3...而1就很明显了。 - Rakesh_Kumar
go()将返回未定义...它不是一个函数,而是一个变量! - kasper Taeymans
5个回答

7
当您执行以下操作时:

var go = foo.baz.bar;
go();

你会发现在调用bar()之前,go已经失去了对foo.baz的引用。它只是一个指向bar函数的指针,不再与它所附加到的对象有任何关联。这意味着当执行bar方法时,this将不再是foo.baz
这正是.bind()被开发出来的原因。你可以像这样使用它:
var go = foo.baz.bar.bind(foo.baz);
go();

而且,它将为您工作。您也可以手动执行相同的操作:

var go = function() {return foo.baz.bar();}
go();

但是现在语言中已经内置了.bind()来帮助你解决这种问题。


谢谢解释!我唯一的问题是,为什么go会失去对foo.baz的引用?这是因为赋值一个对象并不等同于显式地向对象添加属性吗?这就是为什么bind会有用的原因吗? - Antonio Pavicevac-Ortiz
1
@AntonioOrtiz - 在这种情况下设置this,你必须调用obj.method()。如果你做了x = obj.method;然后做x(),你得到的只是一个普通的函数调用method(),而this没有设置为相关对象。这就是JS的工作原理。 - jfriend00

3

首先,你需要声明一个函数表达式并将其赋值给名为go的变量。如果你执行函数gothis指向全局对象,在那里变量x的值为3,这就是为什么alert(go())会弹出3的原因。另一方面,你执行方法foo.baz.bar()。在这里,this指向对象(foo),而x的值为1。所以它会弹出1

var x = 3;

 var foo = {
     x: 2,
     baz: {
       x: 1,
       bar: function() {
         return this.x;
       }
     }
   }
 //here you save a function expression
 //to a variablewith name go
 var go = foo.baz.bar;

 //you execute go but this refer to the global object
 //and x has the value of 3 in the global object
 console.log(go());//this will output 3

 //this refer to the object foo where x has
 //the value of 1
 console.log(foo.baz.bar());//this will output 3


2

go函数在窗口中运行,foo.baz.bar函数在对象内部运行。 为了使其返回内部x,请改用以下方法:

var go = function(){ return foo.baz.bar(); };

编辑: 一个好的经验法则:函数在声明它们的作用域中运行。
同样的事情也可以在.toString()中看到。
Number(123).toString()

给出的结果与

不同。

Number(456).toString()

即使调用相同的函数,实际上该函数位于Number.prototype对象中。

0
在这种情况下,它只涉及执行上下文。函数始终相同,唯一变化的是函数内'this'对象的含义。
您可以使用.apply()指定上下文,并且还可以使用.bind()永久强制特定上下文。
您可以在我版本的代码中看到它:
var x = 3;
var foo = {
  x: 2,
  baz: {
    x: 1,
    bar: function() {
      return this.x;
    }
  }
}

var go = foo.baz.bar;   

alert(go.apply(this));    
alert(go.apply(foo));
alert(go.apply(foo.baz));

var goBinded = foo.baz.bar.bind(foo.baz);
alert(goBinded());
alert(goBinded.apply(foo));

0

由于您将一个函数提取到变量中,因此您实际上是将该函数转移到全局范围内。

例如,如果您的对象bar也有一个名为foo()的函数,并且您提取了foo.baz.bar并且bar包含return this.foo();,那么它会出错。由于您仅分配了该函数而不是原始所属对象,因此foo()不再存在。也就是说,它不是按引用传递的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接