JavaScript 函数绑定(this 关键字)在赋值后丢失

IT技术 javascript
2021-03-01 20:42:52

这是 JavaScript 中最神秘的特性之一,将对象方法分配给其他变量后,绑定(this 关键字)丢失

var john = {
  name: 'John',
  greet: function(person) {
    alert("Hi " + person + ", my name is " + this.name);
  }
};

john.greet("Mark"); // Hi Mark, my name is John

var fx = john.greet;  
fx("Mark"); // Hi Mark, my name is 

我的问题是:

1)任务背后发生了什么?var fx = john.greet; 这是按值复制还是按引用复制?fx 和 john.greet 指向两个不同的函数,对吗?

2) 由于 fx 是一个全局方法,作用域链只包含全局对象。这个属性在 Variable 对象中的值是多少?

5个回答

john.greet("Mark")实际上调用了一个函数。当您这样做时var fx = john.greet;,您将获得对该函数的引用。因此,当您调用它时,this不会绑定到john. 你实际上做的是window.fx("Mark")thiswindow对象。当你说它是在全球范围内时,你是在正确的轨道上。在这个特定的例子中,全局对象是windowfx实际上也是window.fx

当你有一个函数引用时,你应该使用call或者apply如果你想设置this. 尝试这样做:

fx.call(john, "Mark");

callor 中的第一个参数applythis函数调用上下文中使用的值

编辑

有些人提到这里的真正问题可能是围绕对象文字与对象实例的混淆。您正在创建一个对象文字,它的行为也有点像单例。您不能创建该对象的新实例。在这种情况下john是对该对象文字的引用。在那种情况下,this在函数greet中指的是对象文字本身。因此,当您调用 时john.greet("Mark")this必然会john.

当您仅获取对john.greet自身的引用并将其分配给全局变量时,您实际上是在执行以下操作:

var fx = function(person) {
   alert("Hi " + person + ", my name is " + this.name);
}

在这种情况下,thisis window,因为fx基本上是window.fx(因为这里的全局对象是window。假设这段代码被包装在另一个函数中,那么全局对象将引用该函数。

如果你想创建一个对象的多个实例,你可以这样做:

var Person = function(name) {
    var self = this; //maintains a reference to the instance

    this.name = name;
    this.greet = function(name) {
        alert("Hi " + name + ", my name is " + self.name);
    }
}

var john = new Person("John");
john.greet("Mark"); // alerts "Hi Mark, my name is John"

var fx = john.greet;
fx("Mark"); // also alerts "Hi Mark, my name is John"

在这里,self变量(函数的局部变量)维护对实际实例的引用,因为您在this创建对象时将其绑定到

在 Javascript 中有许多与 OOP 相关的最佳实践。你可以谷歌搜索一下(有很多链接)。我特别推荐阅读 Douglas Crockford 的文章。

@sky:冷静点,伙计。我相信你是这里的超级专业人士,每个人都知道你在回答问题方面有多棒。
2021-04-16 20:42:52
是的,很好地提到了 .call,它实际上可以解决问题 8)
2021-04-21 20:42:52
我意识到这里的主要问题是对象文字与对象实例。但这不是 OP 所要求的。我将编辑我的答案并将其添加到那里,因为我认为这是有用的信息。
2021-04-22 20:42:52
@funkatron - 这个问题比调用方法的方式更根深蒂固。按书面形式回答问题是一种很好的做法。能够确定提问者何时不理解自己的问题或问题也很好。
2021-04-27 20:42:52
@funk - 不知道你是怎么知道我不冷静的。我只是想指出,通过寻找解决方法来帮助某人以错误的方式做某事可能无济于事。在某些情况下。在我看来。但不是在星期二。在我看来,今天不是星期二。
2021-04-30 20:42:52

1)fxjohn.greet引用相同的函数对象,对象的赋值操作,通过引用工作

为原始值,比如StringNumberBoolean undefinednull,该值的副本将被制成。

2) 该this值指的是全局对象。

this值不是变量对象的属性,作用域链无关,是一个特殊的保留字,在调用函数时隐式确定(您也可以通过call显式设置它apply)。

JavaScript 内部处理 a Reference type,它由两个组件组成,基础对象属性名称,当调用函数时,this通过获取基础对象(通过内部GetValue操作)隐式确定

最后,this隐式设置where 的最后一种情况是当您使用new运算符调用函数时this关键字将引用新创建的对象。

所以,简单地说,这里是如何this工作的含蓄

1- 当函数作为方法被调用时(函数作为对象的成员被调用):

obj.method(); // 'this' inside method will refer to obj

2- 一个正常的函数调用:

myFunction(); // 'this' inside the function will refer to the Global object
// or 
(function () {})();

3- 使用new运算符时:

var obj = new MyObj(); // 'this' will refer to a newly created object.

据我了解,您只是将该方法分配给变量“fx”。john 对象的上下文并没有随之而来。

在我的脑海中,fx 上下文中的“this”将指代全局对象,在浏览器的上下文中(我相信)相当于您的 window 对象。

(编辑以澄清全局对象。排序)

this 将指代调用该方法的任何对象,无论是隐式还是显式,都使用callapply
2021-04-23 20:42:52
是的,因为全局对象是window,所以不合格foo总是意味着window.foo(除非您使用 更局部地限定了它var)。
2021-05-12 20:42:52

因为您只是将 fx 设置为 greet 方法而不是整个 john 对象,所以它没有它的父对象的概念并且成为全局范围的。所以本质上,它是按值传递的,因为它只复制了方法。

由于该函数现在是全局范围的,因此“this”成为 Window 对象。

如果您改为将 fx 设置为 john,则会得到预期的结果。

var john = {
  name: 'John',
  greet: function(person) {
    alert("Hi " + person + ", my name is " + this.name);
  }
};

john.greet("Mark"); // Hi Mark, my name is John

var fx = john;  
fx.greet("Mark"); // Hi Mark, my name is John

受到@Vivin Paliath 回答的启发,实际上我提出了一些新的东西。对我来说,我总是尽我最大的努力使 javascript 编程与 java 一样,尤其是在 OOP 中。

所以我的建议是在我们第一次这样做时尽可能避免使用

var self = this;

我们应该在所有函数(原型函数,无论如何)中使用 self 而不是 this ,但是如果我们写这样的东西:

function MyObject = {
     var self = this;
};

MyObject.prototype = {
     method1 = function(data){
         self.data = data;
     }
}

这是不是要去工作,因为原型是在为MyObject对象,它不能访问私有成员的自由的MyObject拥有。我对此的解决方案很简单:

function MyObject = {
     var self = this;
     MyObject.prototype.method1 = function(data){
         self.data = data;
     };
}

这利用了原型的效率,而且我们不必关心所有这些问题。虽然我们要输入很多MyObject.prototype.xxxx 的东西。

如果这有助于你的球员,请给我一些竖起大拇指,这样我就可以收集15声誉大拇指别人,谢谢。