THIS

Author Avatar
LI Liangmin 10月 24, 2016

我给自己总结一下,this。“this“是一种动态灵活的机制。它在函数调用时确定,与调用方式有关,而非词法作用域。我这里使用DC的说法对应的另一种说法。即函数的this绑定分为隐式绑定、显式绑定、默认绑定、new绑定。下分别说明:

默认绑定

就是不加任何修饰的使用函数。 this在非严格模式下绑定在window上,严格模式下绑定于undefined。当然这要在函数体中使用”use strict”;才管用。

       var  foo = function () {
              console.log(this.a); //global
       }; 

       var a = "global value";

隐式绑定

这种方式是在函数作为对象方法的时候this变为这个对象,但是会出现绑定丢失的情况,因为安置传递后变为了默认绑定的形式。

       var  foo = function () {
              console.log(this.a); //global
       }; 
       var a = "global value";

       var obj = {
           a: "object",
           foo: foo
       };

       obj.foo();   //"object"

       //绑定丢失

       var bar = obj.foo;
       bar();    //"global value";

       //函数传递参数也是按值传递的,从而也会丢失绑定
       setTimeout(obj.foo, 1000) //"global value";

显式绑定

我直接指定函数调用的this值,当然直接使用apply、call没有问题,若使用bind方法后还是可以被new绑定改变this值。那么如何不让它改变,采用所谓的硬绑定来定。即外加一个包装函数。一些api可直接指定函数调用的this。

  var  foo = function () {
         console.log(this.a); //global
  }; 
  var a = "global value";

  var obj = {
      a: "object",
      foo: foo
  };
  foo.apply(obj)  //"object"

  //丢失的例子
  foo = function (v) {
      this.value = v; 
  };
  var boo = foo.bind(obj);
  var bar = new boo("text");
  bar.value === "text"   //true
  obj.value === undefined //true

  //加上包装函数使得不可变

  var hardbind = function () {    //无论如何调用外层函数均不会改变
      foo.apply(obj);
  };                   

new绑定(略过)

有趣的问题

有时我们想借用某个方法,但又不在乎this值那可以使用null、undefined作为参数,但实际上却被绑定在全局对象上,这是始料未及的。见例子。

  function foo (a) {
        this.a = a;
  }
  foo.apply(null, "where");  //这时的window.a为"where".  

那我不想改变任何有用的对象怎么办呢?见例子:

      var π = Object.create(null); //我将this定于这里之后,不会对其他任何对象有影响。

我想要一种绑定方式:在默认下不会绑定在undefined或是window,同时又可以在其它绑定下修改this值

            (!Function.prototype.softbind) {
                  Function.prototype.softbind = function (obj) {
                        var fn = this;
                        var curried =Array.prototype.slice.call(arguments, 1);
                        var bound = function () {
                               return fn.apply((!this || this === window) ? obj : this, 
                               curried.concat.apply(curried, arguments));      //关键步骤
                        };
                        bound.prototype = fn.prototype; 
                        return bound; 
                  };
            }

            var obj1 = {name: "obj1"};
            var obj2 = {name: "obj2"};
            var foo = function () {
                   console.log(this.name);
            };
            var ins = foo.softbind(obj1);
            ins();  //"obj1";
            obj1.foo = ins;
            obj1.foo();  //"obj1";
            obj2.foo = obj1.foo;
            obj2.foo();    //"obj2", 因为外层函数执行的this是obj2,这时函数执行时的this不变,为obj2。

            setTimeout(obj2.foo, 1000);  //"obj1",原因是:obj2.foo传入后变为了默认绑定,bound函数执行的this原本应是window(这里没有使用严格模式),关键步骤中将对应的this替换为预先
                                         //的obj1。