做了一个javascript小实验,需要你的帮助才能理解意外的结果



我在浏览器控制台中做了一个JavaScript实验

首先,我创建了一个新的对象foo,如下

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

现在,当我在控制台中再次运行以下代码时,它会返回"number"

  (function(){
    return typeof foo.bar();
  })();

但当我运行以下匿名函数时,它会返回"undefined"

  (function(){
    return typeof arguments[0]();
  })(foo.bar);

据我所知,上面函数中的arguments[0]返回foo.bar(下面的例子也证明了这一点),那么为什么上面的代码返回"undefined"而不是"number"呢?


当我运行时

      (function(){
        return arguments[0];
      })(foo.bar);

返回function () { return this.baz; }


还有

  (function(){
    return typeof arguments[0];
  })(foo.bar());

返回"number",为什么不返回

  (function(){
    return typeof arguments[0]();
  })(foo.bar);

返回相同的?JavaScript中有什么基本定律在这里起作用吗?

this取决于如何调用函数。当使用点表示法时,JavaScript将this的上下文设置为接收器。在您的情况下,没有接收器,而是对函数的引用,因此您丢失了上下文。你必须明确通过:

arguments[0].call(foo);

arguments[0]是与foo.bar相同的函数对象,但this的值是动态的。在foo.bar()中,this被分配给foo,因为您使用了foo.(点表示法)。但在arguments[0]()中没有点(没有接收器),因此this是默认值,或window

它是相同的函数,但调用不同。

让我简化这个问题。。。

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

var ref = foo.bar;
console.log(typeof ref()); // undefined
console.log(typeof foo.bar()); // number

这是因为函数对象"bar"中的"this"并不总是引用"foo"。它会改变你调用它的方式。如果你从一个对象而不是foo调用它,它将显示未定义。注意,ref不是foo,而是foo.bar。

现在,如果您将foo更改为以下内容,它将在这两种情况下都给出"number"输出。。。

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

注意

console.log(ref === foo.bar); // true

但是foo.bar()不等于ref()(在第一种情况下),因为当您调用foo.bar)时,javascript引擎将foo传递为"this",但当您调用最初是window.ref()的ref()时,它将窗口对象(或非浏览器环境下的其他全局对象)传递为"this"

关于elclans答案的精化。

CCD_ 21是一种方法;关于如何做某事的指示。在面向对象编程语言中,唯一可以使用该指令的对象是foo或与foo相关的对象;但在JavaScript中,如果代码要求,任何人都可以尝试运行它。

在第二个匿名函数中,您将获得一个要运行的方法,运行它,并返回结果的类型。然而,运行此功能的不是foo;它是匿名的,所以window是运行它的对象。window运行foo,它试图返回this.baz;但是,window没有baz,所以这就是为什么您没有定义。

要进一步测试它,请尝试设置window.baz,看看是否得到了正确的结果,还可以尝试elclans建议的使用call()方法,以确保从foo的作用域调用它。

编辑

你是对的,arguments[0]foo.bar的类型是相同的;它们都是"功能"。我认为混淆的地方在于JavaScript将函数视为一级对象。

如果你熟悉Java或C++等面向对象语言,那么当你调用bar时,它总是由具有baz属性的foo调用;然而,JavaScript中的情况并非如此。任何函数都可以由Any对象调用,这意味着结果可能有意义,也可能没有意义。

正如elclans所说,两者之间的区别在于.。假设我制作了自己的对象phew = {baz: 44}。我可以有效地从foo"窃取"一个方法,如下所示:phew.myMethod = foo.bar。现在,phew知道bar中包含的指令,如果我调用phew.myMethod(),我会得到44作为我的结果,因为phew正在调用该方法,而phewbaz是44;方法定义在哪里并不重要!所有重要的是方法说要做什么bar说返回调用它的人的baz

现在,回到您的代码,您正在调用arguments[0]()。看起来应该是一样的,但因为函数是一级对象,所以当您将其作为参数传递时,您真正传递的只是一个名为bar的函数。当您调用arguments[0]()时,它就像调用bar(),这与调用foo.bar()不同,就像它与调用phew.myMethod()不同一样,尽管它们都是完全相同的函数

有关更多信息,请尝试此SO post

相关内容

最新更新