• JS进阶this机制


    this机制是js中很重要的知识点,它和作用域查询是两个容易混淆的知识点。本文将详细介绍this的4种绑定规则。

    默认绑定

    1. 全局环境中,this指向window。
    2. 独立函数调用时,this指向window。严格模式下指向undefined。
    3. 作为对象的方法调用时,this指向原对象
    // 示例1
    console.log(window === this) // true
    
    // 示例2
    function foo() {
      console.log(window === this) // true
    }
    foo()
    
    // 示例3
    var obj = {
      foo: function() {
      console.log(this === obj) // true
     
      // 注意,这里是独立调用
       function foo2() {
        console.log(this === window) // true
       }
       foo2()
      }
    }
    obj.foo()
    

    隐式绑定

    被直接对象所包含的函数调用时,this隐式绑定到该直接对象。

    function foo() {
      console.log(this.a)
    }
    
    var o1 = {
      a: 1,
      foo: foo,
      o2: {
       a: 2,
       foo: foo
      }
    }
    
    // foo()函数的直接对象是o1,this隐式绑定到o1
    console.log(o1.foo()) // 1
    
    // foo()函数的直接对象是o2,this隐式绑定到o2
    console.log(o1.o2.foo()) // 2
    

    隐式丢失

    隐式丢失是指被隐式绑定的函数丢失绑定对象,进而默认绑定到window对象。

    函数别名

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    var obj = {
      a: 1,
      foo: foo
    }
    
    var fn = obj.foo;
    fn() // 0
    

    示例中造成隐式丢失的原因是在赋值时,只把foo()函数赋给了fn,而fn与obj对象之间没有任何联系。

    // 等价于
    var a = 0;
    var fn = function foo(){
      console.log(this.a)
    }
    fn() // 0
    

    参数传递

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    var obj = {
      a: 1,
      foo: foo
    }
    
    function foo2(fn) {
      fn()
    }
    foo2(obj.foo) // 0
    

    和上面的原因类似,把函数作为参数传递给foo2时,只是把foo函数赋给了fn,fn和obj之间没有联系。

    // 等价于
    var a = 0;
    function foo2(fn) {
      fn()
    }
    foo2(function foo() {
      console.log(this.a)
    })
    

    内置函数

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    var obj = {
      a: 1,
      foo: foo
    }
    
    setTimeout(obj.foo, 100) // 0
    

    内置函数也会造成隐式丢失,原因同上。

    // 相当于
    var a = 0;
    setTimeout(function foo() {
      console.log(this.a)
    }, 100)
    

    间接引用

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    var o1 = {a: 1, foo:foo}
    var o2 = {a: 2}
    // 将o1.foo函数赋值给o2.foo函数后再调用,相当于立即调用foo函数
    ;(o2.foo = o1.foo)() // 0
    
    
    var a = 0;
    function foo() {
    	console.log(this.a)
    }
    var o1 = {a: 1, foo:foo}
    var o2 = {a: 2}
    o2.foo = o1.foo
    // 将o1.foo函数赋值给o2.foo函数后再调用,是作为o2对象的方法调用
    o2.foo() // 2
    
    

    在JavaScript引擎内部,对象o1和方法o1.foo存储在两个不同的内存地址,所以只有o1.foo()这样调用时,this指向o1。

    显示绑定

    通过bind()、apply()、bind()方法把this绑定到对象上,叫做显示绑定。对于被调用的函数来说叫做间接调用。

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    
    var obj = {
      a:1
    }
    
    foo.call(obj) // 1
    

    非严格模式下null或undefined值会被转换为全局对象,严格模式下this值是指定值。

    // 非严格模式
    var a = 0;
    function foo() {
      console.log(this.a)
    }
    
    var obj = {
      a:1
    }
    foo.call(null) // 0
    
    // 严格模式
    'use strict'
    var a = 0;
    function foo() {
      console.log(this.a)
    }
    
    var obj = {
      a:1
    }
    
    foo.call(null) // Uncaught TypeError
    

    硬绑定

    硬绑定是显示绑定的一种,使this不能再修改。

    var a = 0;
    function foo() {
      console.log(this.a)
    }
    var obj = {
      a: 1
    }
    
    var foo2 = function() {
      foo.call(obj)
    }
    
    foo2() // 1
    setTimeout(foo2, 100) // 1
    foo2.call(window) // 1
    

    new绑定

    当函数作为构造函数调用时,函数中的this绑定被称作new绑定。

    var obj = {
      a: 1
    }
    
    function foo() {
      this.a = 2
    }
    
    var test = new foo()
    console.log(test) // {a: 2}
    

    当使用构造函数方式调用对象的方法时,this不再指向原始对象。

    var o = {
      m: function(){
        console.log(this === o) // false
        console.log(this === obj) // false
      }
    }
    var obj = new o.m();
    

    优先级

    this的四种绑定机制,如果同时存在两种以上的绑定规则,它们会遵循下面的绑定顺序:

    1. 是否是new绑定?如果是,this绑定的是新创建的对象
    var fn = new foo();
    
    1. 是否是显式绑定?如果是,this绑定的是指定的对象
    var fn = foo.call(obj2);
    
    1. 是否是隐式绑定?如果是,this绑定的是属于的对象
    var fn = obj1.foo(); 
    
    1. 如果都不是,则使用默认绑定
    var fn = foo();
    

    结语

    this的四种绑定规则:默认绑定、隐式绑定、显式绑定和new绑定,分别对应函数的四种调用方式:独立调用、方法调用、间接调用和构造函数调用。

    日常开发中很多的错误都是由于this的隐式丢失造成的,平常多观察多思考可以减少错误的发生。

    优秀文章首发于聚享小站,欢迎关注!
  • 相关阅读:
    Jmeter(二十七) 从入门到精通 Jmeter Http协议录制脚本(详解教程)
    Jmeter(二十六) 从入门到精通 搭建开源论坛JForum(详解教程)
    [Erlang0003][OTP] Efficiency Guide User's Guide > Common Caveats
    [Erlang0008][OTP] 高效指南 表和数据库(ets mnesia)
    [Erlang0004][OTP] 高效指南 二进制的构造和匹配(1)
    [Erlang0002][OTP] Efficiency Guide User's Guide > The Eight Myths of Erlang Performance
    [Erlang0010][News]OTP 技术委员会 影响R16的决策 (OTP Technical Board Decisions affecting R16 翻译)
    [Erlang0007][OTP] 高效指南 函数
    [Erlang0005][OTP] 高效指南 二进制的构造和匹配(2)
    [Erlang0001][OTP] Efficiency Guide User's Guide>Introduction
  • 原文地址:https://www.cnblogs.com/yesyes/p/15352003.html
Copyright © 2020-2023  润新知