this

111 阅读6分钟

关于this

this关键字,被自动定义在所有函数的作用域中。

1.箭头函数
var foo = a =>{
	console.log(a);
}
foo(2);

如果不使用this,需要给函数显式的传递一个上下文对象

this提供了一种方式:隐式的传递一个对象引用。

函数能够自动的引用合适的上下文对象非常重要。

根据外层(函数或者全局)作用域来决定this,箭头函数的绑定无法修改。

箭头函数最常用于回调函数中,例如事件处理器或者定时器。

self = this

2.误解
  1. 指向自身

    javaScript中的所有函数都是对象。this并非指向函数自身。

    如果要从函数对象内部引用它自身,一般来说需要通过一个指向函数对象的词法标识符(变量)(具名函数的函数名)来引用它。

    使用arguments.callee(已经被弃用)来引用当前正在运行的函数对象。这是唯一一种可以从匿名函数对象内部引用自身的方法。然而,更好的方式是避免使用匿名函数,至少在需要自引用时,使用具名函数(表达式)。

  2. 它的作用域

    this在任何情况下都不指向函数的词法作用域。

    不能使用this来引用一个词法作用域内部的东西。

    不能把this和词法作用域的查找混合使用。

3.this机制
  • this是在运行时进行绑定的,并不是在编写时绑定。他的上下文取决于函数调用时的各种条件。
  • this的绑定和函数声明的位置没有关系,只取决于函数的调用方式,调用位置。

this既不指向函数自身也不指向函数的词法作用域

  1. 调用位置

    函数在代码中被调用的位置(而不是声明的位置)。

    调用栈:为了到达当前执行位置所调用的所有函数。

    调用位置:在当前正在执行的函数的前一个调用中。

    函数调用链,可以使用浏览器的调试工具来查看调用栈(栈中第二个元素,就是真正的调用位置)。

  2. 绑定规则

    1. 默认绑定--独立函数调用(不带任何修饰的函数引用进行调用)

      严格模式下,this的绑定与调用位置无关。

    2. 隐式绑定

      调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

      对象属性引用链中只有最顶层或者最后一层会影响调用位置。

      隐式丢失:被隐式绑定的函数会丢失绑定对象,也就是说他会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。

    3. 显式绑定

      call(对象,)

      apply(对象,)

      如果传入一个原始值(字符串类型、布尔类型或者数字类型)来当作this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..) new Boolean(..) new Number(..))这通常被称为装箱。

      显示绑定仍然无法解决之前提出的丢失绑定的问题。

      硬绑定

      var bar = functuon(){
      	foo.call(obj);
      };
      bar();//硬绑定的bar不可能再修改它的this
      

      硬绑定的典型应用场景:创建一个包裹函数,传入所有的参数并返回接收到的所有值。

      ES5内置方法:Function.prototype.bind

      bind会返回一个硬编码的新函数,他会把参数设置为this的上下文并调用原始函数。

      API调用的上下文

      forEach()

    4. new绑定

      JavaScript中new的机制实际上和面向类的语言完全不同。

      在JavaScript中,构造函数只是一些使用new操作符时被调用的函数。他们并不会属于某个类,也不会实例化一个类。实际上,他们甚至都不能说是一种特殊的函数类型,他们只是被new操作符调用的普通函数而已。

      不存在所谓的构造函数,只有对于函数的构造调用。

      使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作

      • 创建(或者说构造)一个全新的对象。
      • 这个新对象会被执行 原型 链接。
      • 这个新对象会绑定到函数调用的this
      • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

    new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。

    优先级:

    1. 函数是否在new中调用(new绑定)?如果是的话,new绑定的是新创建的对象。

      var bar = new foo();
      
    2. 函数是否通过call,apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。

      var bar = foo.call(obj2);
      
    3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。

      var bar = obj1.foo()
      
    4. 如果都不是的话,使用默认绑定。如果在严格模式下就绑定到undefined,否则就绑定到全局对象。

      var bar = foo();
      
      4.绑定例外
      1. 被忽略的this

        如果把null、或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际上应用的是默认的绑定规则。

        使用apply(...)来展开一个数组,并当作参数传入一个函数

        类似的,bind可以对参数进行柯里化(预先设置一些参数)

        更安全的this

        传入一个特殊的对象(DMZ),把this绑定到这个对象不会对你的程序产生任何副作用

        在JavaScript中创建一个空对象最简单的方法都是Object.create(null)和{}很像,但是不会创建Object.proyotype这个委托,所以他比{}更空。

      2. 间接引用

        创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。

        间接引用最容易在赋值时发生。

        对于默认绑定来说,决定this绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this会被绑定到undefined;否则this会被绑定到全局对象。

      3. 软绑定

        给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显示绑定来修改this的能力。