当你通过这个'作为一个论点

时间:2015-01-19 01:11:57

标签: javascript this bind

我正在尝试了解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)交换,我想我会得到不同的结果。这是因为callbackthis.sumData.bind(this)在首次定义时的引用(以及thisobj的位置)吗?


我想我已经通过这种情况了解到this在执行时已经设置好了。它不会作为参数传递,以便稍后在该行调用参数时设置。

4 个答案:

答案 0 :(得分:19)

函数调用中的

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

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

  2. 方法调用:如果调用某个方法,例如obj.foo(),则this会在函数内设置为obj

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

  4. 使用new:如果使用new调用某个函数,例如new foo(),则会创建一个新对象,构造函数foo调用this设置为新创建的对象。

  5. 使用.bind():使用.bind()时,会从内部使用.apply()设置this的调用返回新的存根函数传递给.bind()的指针。仅供参考,这不是一个不同的案例,因为.bind()可以.apply()实施。

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

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


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

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

    知道某些回调函数(例如DOM事件处理程序)的调用具有由调用回调函数的基础结构设置的特定值this也很有用。在内部,他们都使用.call().apply(),所以这不是一个新规则,但需要注意。 "合同"对于回调函数,可以包括它如何设置this的值。如果未明确设置this的值,则将根据规则#1进行设置。

    在ES6中,通过箭头函数调用函数,保持当前词汇值this。以下是保持MDN词汇this的数组函数示例:

    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,因此this内的randomFunction将设置为全局对象或undefined(如果处于严格模式)。

    由于randomFunction正在调用一个本身使用.bind()的回调函数,因此调用回调函数时this内的值将被设置为this的值。通过上面的规则#5传递到.bind()中的this.sumData.bind(this).bind()实际上创建了一个新功能,即在设置自定义值this之后调用原始功能。


    以下是关于该主题的其他几个参考资料:

    How to avoid "this" refering to the DOM element, and refer to the object

    A better understanding of this

    How does the "this" keyword work?


    请注意,使用.apply().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对象上不存在的其他数组方法,那么这个技巧将不起作用,但因为它只依赖于[]和{{1}这两个.length对象都有,它确实有效。

    所以,这个技巧可以用来"借用"来自任何对象的方法并将它们应用于另一个对象,只要您应用它们的对象支持该方法实际使用的任何方法或属性。这不能在C ++中完成,因为方法和属性是"硬约束"在编译时(即使C ++中的虚拟方法绑定到在编译时建立的特定v表位置),但可以在Javascript中轻松完成,因为属性和方法在运行时通过其实际名称实时查找,因此任何包含的对象正确的属性和方法适用于对其进行操作的任何方法。

答案 1 :(得分:3)

一步一步。

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不存在,您将undefined添加到data。出乎意料的结果。

解决方案:永久绑定this

randomFunction(this.sumData.bind(this));

4)sumData运行,thisobjobj.initialData20。它有效。

答案 2 :(得分:2)

  

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

没有。它是通过如何调用函数或使用 bind 来设置的。

  

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

因为使用以下函数调用该函数:

obj.prepareRandomFunction();

将函数中的 this 设置为 obj

  

并将函数绑定到obj

在:

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

因为 prepareRandomFunction 已使用 obj 作为调用,所以中的 this 的值obj.sumData 设置为 obj ,对 randomFunction 的调用有效地解析为:

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

但是因为这是作为回调函数传递的,所以阻止它被设置为randomFunction

您已在呼叫中将设置为 obj 。如果您希望 randomFunction ,则需要将其设置为。 randomFunction this 没有内在价值,它是通过如何调用函数(或使用 bind )设置的。

  

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

因为执行该代码时不是 randomFunction

所以:

obj.prepareRandomFunction();

调用 prepareRandomFunction 并将设置为 obj ,然后执行此操作(将替换为 obj

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

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

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

创建值为10的局部变量 data ,然后调用 sumData ,并将设置为 obj 并传递 data 的值。然后:

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

由于 obj ,因此作业有效地解析为:

  var sum = obj.initialData + 10;

这是30。

答案 3 :(得分:-1)

这是一个对象的自我引用。当你创建一个对象时,要么使用new - 运算符,将它声明为一个对象 - 文字,要么通过调用其中一个内置的JS,这将被分配给它。
但是,由于JS的范围是动态的而且非瞬态的,所以通常 不能过分依赖这个 - 对象。这就是为什么你经常看到oThis:this或var o

这样的结构