简体   繁体   English

分配后,JavaScript函数绑定(此关键字)将丢失

[英]JavaScript function binding (this keyword) is lost after assignment

this is one of most mystery feature in JavaScript, after assigning the object method to other variable, the binding (this keyword) is lost 这是JavaScript中最神秘的功能之一,在将对象方法分配给其他变量后,绑定(此关键字)将丢失

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 

my question is: 我的问题是:

1) what is happening behind the assignment? 1)作业背后发生了什么? var fx = john.greet; var fx = john.greet; is this copy by value or copy by reference? 是按价值复制还是按参考复制? fx and john.greet point to two diferent function, right? fx和john.greet指向两个不同的功能,对吧?

2) since fx is a global method, the scope chain contains only global object. 2)因为fx是一个全局方法,所以范围链只包含全局对象。 what is the value of this property in Variable object? Variable对象中属性的值是多少?

john.greet("Mark") actually calls a function. john.greet("Mark")实际上调用了一个函数。 When you do var fx = john.greet; 当你做var fx = john.greet; , you're getting a reference to the function. ,你得到了这个功能的参考。 So when you call it, this is not bound to john . 所以当你调用它时, this并不是john约束。 What you're actually doing is window.fx("Mark") and so this is the window object. 你实际做的是window.fx("Mark") ,所以thiswindow对象。 You were on the right track when you said that it was in the global context. 当你说它处于全球范围内时,你走在了正确的轨道上。 In this particular instance, the global object is window , and so fx is actually window.fx . 在这个特定的实例中,全局对象是window ,因此fx实际上是window.fx

When you have a function reference you should use call or apply if you want to set the value of this . 当你有一个函数引用,你应该使用callapply ,如果你想设置的值this Try doing this: 试着这样做:

fx.call(john, "Mark");

The first argument in call or apply is the value used for this in the context of the function call. 在第一个参数callapply是用于值this函数调用的上下文。

EDIT 编辑

Some people mentioned that the real issue here might be confusion surrounding an object literal vs. an instance of an object. 有些人提到这里的真正问题可能是围绕对象文字与对象实例的混淆。 You're creating an object literal which also behaves kind of like a singleton. 您正在创建一个对象文字,其行为也类似于单身。 You cannot create a new instance of that object. 您无法创建该对象的新实例。 In this case john is a reference to that object literal. 在这种情况下, john是对该对象文字的引用。 In that context, this in the function greet refers to the object literal itself. 在该上下文中,函数greet this指的是对象文字本身。 Hence when you call john.greet("Mark") , this is bound to john . 因此,当你打电话给john.greet("Mark")thisjohn约束。

When you grab a reference to john.greet just by itself and assigning it to a global variable, you're essentially doing this: 当您john.greet获取对john.greet的引用并将其分配给全局变量时,您实际上是这样做的:

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

In this scenario, this is window , because fx is basically window.fx (since the global object here is window . Assuming this code was wrapped inside another function, then the global object would refer to that function. 在这种情况下, thiswindow ,因为fx基本上是window.fx (因为这里的全局对象是window 。假设这个代码被包装在另一个函数中,那么全局对象将引用该函数。

If you want to create multiple instances of an object, you can do something like this: 如果要创建对象的多个实例,可以执行以下操作:

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"

Here, the self variable (which is local to the function) maintains a reference to the actual instance because you're binding it to this when you create the object. 这里, self变量(函数的本地变量)维护对实际实例的引用,因为在创建对象时将其绑定this实例。

There are many best practices associated with OOP in Javascript. 在Javascript中有许多与OOP相关的最佳实践。 You can Google and find out (there are many links). 你可以谷歌找出(有很多链接)。 I recommend reading stuff from Douglas Crockford especially. 我建议特别是读道格拉斯克罗克福德的东西。

1) fx and john.greet are referring to the same function object, the assignment operation for objects, works by reference . 1) fxjohn.greet指的是相同的函数对象,对象的赋值操作, 通过引用工作

For primitive values, like String , Number , Boolean undefined or null , a copy of the value will be made. 对于原始值,如StringNumberBoolean undefinednull ,将生成值的副本。

2) The this value refers to the global object. 2) this值指的是全局对象。

The this value is not a property of the Variable Object and it has nothing to do with the scope chain , is a special reserved word, and it is determined implicitly when a function is called (you can also set it explicitly via call or apply ). this值不是变量对象的属性,它与范围链无关,是一个特殊的保留字,并且在调用函数时隐式确定(您也可以通过callapply显式设置) 。

JavaScript internally handles a Reference type , which consists of two components, the base object and the property name , when a function is invoked, the this value is determined implicitly by getting the base object (by the internal GetValue operation). JavaScript在内部处理一个Reference type ,它由两个组件组成,即基础对象属性名称 ,当调用一个函数时,通过获取基础对象 (通过内部GetValue操作)隐式确定this值。

And finally, the last case where this is set implicitly is when you invoke a function with the new operator, the this keyword will refer to a newly created object. 最后,在最后一种情况下this是设置隐式是当你调用与函数new运营商,在this关键字将引用新创建的对象。

So in brief, here is how this works implicitly : 所以,简单地说,这里是如何this工作含蓄

1- When a function is called as a method (the function is invoked as member of an object): 1-当函数作为方法调用时(该函数作为对象的成员调用):

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

2- A normal function call: 2- 正常函数调用:

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

3- When the new operator is used: 3-使用new操作员时:

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

As I understand it, you're only assigning that method to the variable "fx." 据我了解,您只是将该方法分配给变量“fx”。 The context of the john object doesn't come along with it. john对象的上下文并没有随之而来。

Off the top of my head, "this" in the context of fx will refer to the global object, which in the context of a browser is (I believe) equivalent to your window object. 在我的头脑中,fx上下文中的“this”将引用全局对象,在浏览器的上下文中(我相信)等同于您的窗口对象。

(editing to clarify global object. Sort of) (编辑澄清全局对象。排序)

Because you're only setting fx to the greet method and not the entire john object, it has no concept of it's parent and becomes globally scoped. 因为你只是将fx设置为greet方法而不是整个john对象,所以它没有它的父级概念并且变为全局范围。 So in essence, it's passing by value in that in only copies the method. 所以从本质上说,它只是通过复制方法来传递价值。

Since the function is now globally scoped, "this" becomes the Window object. 由于该函数现在是全局范围的,因此“this”成为Window对象。

If you instead set fx to john, you get what's expected. 如果你改为将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

inspired by @Vivin Paliath answer, actually I come out something new. 受到@Vivin Paliath回答的启发,实际上我想出了一些新的东西。 As to me, I always try my best to make javascript programming the same way as java, especially in OOP. 至于我,我总是尽力使javascript编程与java相同,尤其是在OOP中。

So my suggestion is to avoid using this as possible as we can , when we first do 所以我的建议是尽可能避免在我们第一次使用时尽可能地使用

var self = this;

we should use self instead of this in all function (prototype function, whatsoever), but if we write something like this: 我们应该在所有函数(原型函数,无论如何)中使用self而不是this,但是如果我们写这样的东西:

function MyObject = {
     var self = this;
};

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

This is not gonna work, because prototype is an object in MyObject, It can not access private member self owned by MyObject. 这不会起作用,因为prototype是MyObject中的一个对象,它无法访问MyObject 自己拥有的私有成员。 My solution for this is simple: 我的解决方案很简单:

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

This takes the advantage of prototype's efficiency and also we do not have to care about all the this issues. 这样可以充分利用原型的效率,而且我们也不必关心所有这些问题。 Though we gonna type a lot of MyObject.prototype.xxxx thing. 虽然我们会输入很多MyObject.prototype.xxxx的东西。

If this helpful to your guys, please give me some thumb up , so I can gather 15 reputation to thumb up others, thanks. 如果这对你的家伙有帮助,请给我一些大拇指 ,所以我可以收集15个声望赞美其他人,谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM