当您将“this”作为参数传递时?

IT技术 javascript this bind
2021-01-11 12:37:39

我正在尝试了解this,这让我有点困惑:

var randomFunction = function(callback) {
    var data = 10;
    callback(data);
};

var obj = {
    initialData:  20,
    sumData: function(data) {
        var sum = this.initialData + data;
        console.log(sum);
    },
    prepareRandomFunction: function() {
        randomFunction(this.sumData.bind(this));
    }
};

obj.prepareRandomFunction();

this设计用来为自己在那里的代码首先呈现?

例如,在我的示例中,我成功地使用它来引用obj并将函数绑定到obj,但是由于this作为回调函数传递,是什么阻止它被设置为randomFunction(即阻止它从字面上传递“this .sumData.bind(this)" 以便从那里调用它时this设置为randomFunction)?

更新

我并不是在问这通常是如何工作的(我不认为)。我主要想知道为什么this在我将其定义为我的randomFunction调用参数的地方设置,而不是callbackrandomFunction.

我可能是错的,但如果我要this.sumData.bind(this)callback(data)我目前拥有的交换我想我会得到不同的结果。是不是因为callback是一个参考this.sumData.bind(this),当它被首先定义(何thisobj)?


我想我已经通过这个this在执行时设置的场景学习了它不会作为参数传递,以便稍后在该参数被调用时进行设置。

4个回答

this在函数调用内部根据函数的调用方式设置。this设置的主要方式有六种

  1. 普通函数调用: 在普通函数调用中,例如foo(),this设置为全局对象(window在浏览器或globalnodejs 中)或 to undefined(在 JavaScript 的严格模式下)。

  2. 方法调用: 如果一个方法被调用,例如obj.foo()该方法foo是使用function关键字的普通方法声明或使用 a 的常规方法声明语法class,则在函数内部this设置为obj

  3. .apply() 或 .call(): 如果使用.apply().call(),则this根据传递给.apply()or 的内容进行设置.call()例如,您可以为该特定函数调用dofoo.call(myObj)和 causethis设置为myObjinside of foo()

  4. 使用 new: 如果使用new诸如调用函数new foo(),则会创建一个新对象,并foo使用thisset 为新创建的对象调用构造函数

  5. 使用 .bind(): 当使用.bind()新的存根函数时,该调用会返回内部用于.apply()设置this传递给指针的函数.bind()仅供参考,这并不是真正的不同情况,因为.bind()可以使用.apply().

  6. 使用ES6 Fat Arrow 函数在 ES6+ 中通过箭头语法定义一个函数将绑定当前的词法值this到它。因此,无论在其他地方如何调用该函数(使用之前的任何调用方式),this解释器都会将该值设置this为定义该函数时的这与所有其他函数调用完全不同。

有第七种方法,通过回调函数,但它实际上并不是它自己的方案,而是调用回调的函数使用上述方案之一,并确定this调用回调时的您必须查阅调用函数的文档或代码,或者自己测试以确定this将在回调中设置的内容。


在 JavaScript 中理解的重要一点是 JavaScript 中的每个函数或方法调用都为this. 并且,设置哪个值取决于函数的调用方式。

因此,如果您将方法作为普通回调传递,默认情况下该方法不会被调用obj.method(),因此不会this为其设置正确的值您可以使用.bind()来解决该问题。

了解某些回调函数(例如 DOM 事件处理程序)使用this调用回调函数的基础结构设置的特定值 也很有用在内部,他们都使用.call()或者.apply()所以这不是一个新的规则,而是一个需要注意的。回调函数的“契约”可能包括它如何设置 的值this如果它没有明确设置 的值this,那么它将根据规则#1 设置。

在 ES6 中,通过箭头函数调用函数,维护 的当前词法值this这是维护this MDN词法的数组函数示例

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

您的示例obj.prepareRandomFunction();是上面的规则 #2,因此this将设置为obj.

你的例子randomFunction(this.sumData.bind(this))是上面的规则#1,所以thisinside ofrandomFunction将被设置为全局对象或undefined(如果在严格模式下)。

由于randomFunction正在呼叫它本身使用的回调函数.bind(),则值this时它被称为将被设置为的值的回调函数内this传递给.bind()this.sumData.bind(this)经由上述规则#5。 .bind()实际上创建了一个新函数,它的工作是在设置自定义值后调用原始函数this


以下是有关该主题的其他一些参考资料:

如何避免“this”引用DOM元素,并引用对象

更好地理解这一点

“this”关键字如何工作?


请注意,通过使用.apply()or.call().bind(),您可以创建各种有点奇怪的东西,有时甚至是非常有用的东西,这些东西在C++ 之类的东西中永远无法完成您可以使用世界上的任何函数或方法,并像调用其他对象的方法一样调用它。

例如,这通常用于将arguments对象中的项目复制到数组中:

var args = Array.prototype.slice.call(arguments, 0);

或类似:

var args = [].slice.call(arguments, 0);

这需要数组的.slice()方法并调用它,但为它提供一个参数对象作为this指针。arguments对象(虽然不是实际数组)具有足够的类似数组的功能,该.slice()方法可以对其进行操作,并且最终将arguments项目的副本复制到实际数组中,然后可以直接使用实际数组操作对其进行操作。这种诡计不能随意完成。如果数组.slice()方法依赖于arguments对象上不存在的其他数组方法,那么这个技巧将不起作用,但由于它只依赖于[].length,这两个arguments对象都有,所以它确实有效。

因此,这个技巧可用于从任何对象“借用”方法并将它们应用到另一个对象,只要您应用它们的对象支持该方法实际使用的任何方法或属性。这不能在 C++ 中完成,因为方法和属性在编译时是“硬绑定”的(甚至 C++ 中的虚拟方法都绑定到在编译时建立的特定 v-table 位置),但可以在 JavaScript 中轻松完成,因为属性和方法在运行时通过它们的实际名称实时查找,因此任何包含正确属性和方法的对象都可以与对这些进行操作的任何方法一起使用。

增加了一些有关的一些很酷的应用更多的参考和笔记.apply().call()
2021-03-17 12:37:39
很好。由于选项 2,我觉得自己像个傻瓜,总是使用 'this',但我想这是 Python 编程带来的。
2021-04-08 12:37:39

一步步。

  1. this里面prepareRandomFunctionobj

     obj.prepareRandomFunction()
    
  2. randomFunction 接受一个函数:

     randomFunction(this.sumData);
    
  3. 该函数被调用:

     callback(data);
    

    注意callback调用时不带点,这意味着它没有值this,这意味着this是全局对象(或undefined在严格模式下)。

  4. sumData 被称为:

     var sum = this.initialData + data;
    

    this是全局对象,initialData不存在,你添加undefineddata. 意想不到的结果。

    解决方案:this永久绑定

     randomFunction(this.sumData.bind(this));
    
  5. sumData运行,thisobjobj.initialData20有用。

这是否旨在将自身设置在代码中首次呈现的位置?

不。它由函数的调用方式或使用bind 设置

例如,在我的示例中,我成功地使用它来引用 obj

因为该函数是使用以下方法调用的:

obj.prepareRandomFunction();

它将函数中的this设置obj

并将函数绑定到 obj

在:

var obj = {
  ...
  prepareRandomFunction: function() {
      randomFunction(this.sumData.bind(this));
  }
};

因为prepareRandomFunction被称为与OBJ这个,那么valueobj.sumData设置为OBJ,并调用randomFunction有效地解析为:

  randomFunction(obj.sumData.bind(obj));

但由于这是作为回调函数传递的,是什么阻止它被设置为 randomFunction

您所设定的事实,OBJ通话。如果您希望randomFunction,则需要将其设置为。randomFunction对于this没有内在value,它由函数的调用方式(或bind 的使用)设置。

(即是什么阻止它从字面上传递“this.sumData.bind(this)”以便将其设置为randomFunction)?

因为在执行该代码时,不是randomFunction

所以:

obj.prepareRandomFunction();

调用prepareRandomFunction设置为OBJ,然后做(更换OBJ

randomFunction(obj.sumData.bind(obj));

randomFunction 中this尚未设置,因此默认为全局对象(实际上无关紧要,因为此处未使用它)。然后:

var randomFunction = function(callback) {
    var data = 10;
    callback(data);
};

它创建了一个值为 10的局部变量data,然后调用sumData并将其this设置为obj并传递data的值然后:

sumData: function(data) {
    var sum = this.initialData + data;
    console.log(sum);
},

由于obj,因此赋值有效地解析为:

  var sum = obj.initialData + 10;

这是 30。

是的,很抱歉回答这么长,但我没有时间写一个较短的答案。;-)
2021-04-05 12:37:39
谢谢。“因为当执行该代码时,'this' 不是 randomFunction。” 是我真正要寻找的。即,'this' 不会作为稍后确定的参数传递。它总是在执行 'this' 时设置。
2021-04-10 12:37:39

这是一个对象的自引用。当您创建一个对象时,无论是通过使用 new 运算符,将其声明为对象字面量,还是通过调用 JavaScript 的内置函数之一,都会将 this 分配给它。

但是由于 JavaScript 的作用域是动态的并且是非瞬态的,因此您通常不能过分依赖 this 对象。这就是为什么您经常看到诸如oThis : this 或 var o 之类的结构的原因