• 彻底明白 js 中的 this 指向


    文章源于 coderwhy ”js高级视频“ 总结

    全局作用域的this

    • 游览器:指向window
    • Node环境:指向{}
      • 把文件看作module -> 加载编译 -> 放到一个函数中 -> 通过 apply({})/call({}) 执行这个函数

    调用方式不同,this指向不同

    在大多情况下,this出现在函数中。this指向什么,跟函数定义的位置无关,跟函数调用方式有关(绑定规则相关)

    function foo(){
    	console.log(this);
    }
    
    var obj = {
    	foo: foo
    }
    //不同的调用方式this的指向不同,不同的调用方式对应着不同的绑定规则
    foo();//window
    obj.foo();//obj
    foo.call("123");//String{"abc"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    this的绑定规则

    1. 默认绑定:函数独立调用方式,this指向window
    2. 隐式绑定:通过对象调用方法,会隐式的将this指向当前对象
    3. 显示绑定:通过函数的apply、call、bind方法改变函数中this的指向
      • apply/call:会自动调用,第一个参数都是设置this的指向,apply第二个参数为数组,call后面传入为参数列表。
      • bind:不会自动调用,会返回一个已经绑定好this的函数,传参与call一样
    4. new绑定 :通过new关键字调用函数,new 函数()
      1. 创建一个全新的对象
      2. 这个新对象会被执行prototype连接
      3. 执行构造函数中的代码,为新对象添加属性(this绑定在这个步骤完成)
      4. 如果函数没有返回其他对象,表达式会返回这个新对象

    this绑定优先级(权重)

    • 显示绑定(bind>call/apply) > 隐式绑定 > 默认绑定

      • function fn() {
          console.log(this);
        }
        fn.bind(123).apply(321);
        
        • 1
        • 2
        • 3
        • 4
    • new 绑定 > 隐式绑定 > 默认绑定

    • new 绑定 > 显示绑定(通过bind绑定this的函数,可以被new关键字改变this指向)

      • function Fn(){
          console.log(this);
        }
        fn.bind("132");
        var obj = new Fn();
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • new关键字不能和call/apply一起使用,因为new会主动调用函数,call/apply也会主动调用函数,这样会产生冲突报错

    绑定优先级:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

    this规则之外

    忽略显示绑定

    apply/call/bind:当传入null/undefined时,自动将this绑定成全局对象

    function fn(){
      console.log(this);
    }
    fn.apply(null);
    fn.apply(undefined);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    间接函数引用
    var obj1 = {
    	foo: function(){
    		console.log(this);
    	}
    }
    var obj2 = {}
    
    ;(obj2.bar = obj1.foo)()//括号中包含赋值表达式,属于独立函数调用,是默认绑定,指向window
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    箭头函数

    • 将头函数不会绑定this,arguments属性
    • 箭头函数不能作为构造函数来使用(不能new对象)
    简写
    1. 简写一:当参数只有一个,可以省略括号

    2. 简写二:当函数体只有一行,可以省略大括号,并且会将这一行代码的返回值return

      • var nums = [0, 1, 2, 3, 4, 5];
        var result = nums
          .filter(item => item % 2 === 0)
          .map(item => item * 10)
          .reduce((preVal, curVal) => preVal + curVal, 0);
        console.log(result);
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
    3. 简写三:如果只有一个行,且需要返回一个对象,那么要将对象用括号包裹。因为省略"{}“后,不知道将对象的”{}"如何解析

      • var foo = () => ({ name: "foo", age: 18 });//这样解析时会将对象当作一个整体
        
        • 1

    箭头函数的this绑定

    箭头函数不遵循this的四种标准规则,箭头函数内部没有绑定this,也不能通过call/apply/bind绑定this,箭头函数的this指向根据外层作用域决定

    var obj = {
      getData() {
        setTimeout(function () {
          console.log(this);
        }, 200);
      },
    };
    //setTimeout中的函数属于独立调用,所以指向的是window
    //如果想让this指向obj对象,setTimeout中使用箭头函数
    obj.getData();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    this相关面试题

    面试题一:
    var name = "window";
    var person = {
      name: "person",
      sayName: function () {
        console.log(this.name);
      }
    };
    function sayName() {
      var sss = person.sayName;
      sss(); 
      person.sayName(); 
      (person.sayName)(); 
      (b = person.sayName)(); //包含赋值表达式,属于间接函数引用
    }
    sayName();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    答案:window(默认绑定)、person(隐式绑定)、person(隐式绑定)、window(间接函数引用)

    面试题二:
    var name = 'window'
    var person1 = {
      name: 'person1',
      foo1: function () {
        console.log(this.name)
      },
      foo2: () => console.log(this.name),
      foo3: function () {
        return function () {
          console.log(this.name)
        }
      },
      foo4: function () {
        return () => {
          console.log(this.name)
        }
      }
    }
    
    var person2 = { name: 'person2' }
    
    person1.foo1(); 
    person1.foo1.call(person2); 
    
    person1.foo2();
    person1.foo2.call(person2);
    
    person1.foo3()();
    person1.foo3.call(person2)();
    person1.foo3().call(person2);
    
    person1.foo4()();
    person1.foo4.call(person2)();
    person1.foo4().call(person2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    答案解析:

    person1.foo1(); //person1:隐式绑定
    person1.foo1.call(person2); //person2:显示绑定
    
    person1.foo2();//window:箭头函数的this指向上层作用域的this
    person1.foo2.call(person2);//window:箭头函数的this指向上层作用域的this,且不能改变箭头函数指向
    
    person1.foo3()();//window:先执行person1.foo3(),然后拿到返回的函数再调用,属于独立调用,所以指向window
    person1.foo3.call(person2)();//window:先执行person1.foo3.call(person2),拿到返回的函数再调用,属于独立调用
    person1.foo3().call(person2);//person2:先执行person1.foo3(),拿到返回的函数通过call调用this指向了person2
    
    person1.foo4()(); //person1:拿到返回的箭头函数后,箭头函数没有this,根据上层作用域决定,上层foo4函数指向的是person1对象(foo4是被person1直接调用的),所以内部箭头函数也指向peroson1
    person1.foo4.call(person2)(); //person2:给foo4绑定this为person2,箭头函数调用this指向上层作用域,上层foo4的this被显示绑定为了person2,那么内部的箭头函数也是指向person2
    person1.foo4().call(person2); //person1:call调用箭头函数,依然去上一层找,所以依然是person1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    面试题三:
    var name = 'window'
    function Person (name) {
      this.name = name
      this.foo1 = function () {
        console.log(this.name)
      },
      this.foo2 = () => console.log(this.name),
      this.foo3 = function () {
        return function () {
          console.log(this.name)
        }
      },
      this.foo4 = function () {
        return () => {
          console.log(this.name)
        }
      }
    }
    var person1 = new Person('person1')
    var person2 = new Person('person2')
    
    person1.foo1()
    person1.foo1.call(person2)
    
    person1.foo2()
    person1.foo2.call(person2)
    
    person1.foo3()()
    person1.foo3.call(person2)()
    person1.foo3().call(person2)
    
    person1.foo4()()
    person1.foo4.call(person2)()
    person1.foo4().call(person2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    答案与解析:

    person1.foo1() //person1
    person1.foo1.call(person2) //person2
    
    person1.foo2() //person1:箭头函数,指向上层作用域的this,上层作用域是“Person构造函数”
    person1.foo2.call(person2) //person1::箭头函数,指向上层作用域的this,不能被显示绑定
    
    person1.foo3()() //window:拿到返回的函数后调用,属于独立调用
    person1.foo3.call(person2)() //window:拿到返回的函数后调用,属于独立调用
    person1.foo3().call(person2) //person2:通过call显示绑定
    
    person1.foo4()() //person1:返回的箭头函数去上层作用域找,上层foo的this指向person1
    person1.foo4.call(person2)()//person2:返回的箭头函数去上层作用域找,上层this通过call指向了person2
    person1.foo4().call(person2)//person1:箭头函数的this不能被显示绑定
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    面试题四:

    var name = 'window'
    function Person (name) {
      this.name = name
      this.obj = {
        name: 'obj',
        foo1: function () {
          return function () {
            console.log(this.name)
          }
        },
        foo2: function () {
          return () => {
            console.log(this.name)
          }
        }
      }
    }
    var person1 = new Person('person1')
    var person2 = new Person('person2')
    
    person1.obj.foo1()()
    person1.obj.foo1.call(person2)()
    person1.obj.foo1().call(person2)
    
    person1.obj.foo2()()
    person1.obj.foo2.call(person2)()
    person1.obj.foo2().call(person2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    答案与解析:

    person1.obj.foo1()() //window:独立调用
    person1.obj.foo1.call(person2)() //window:独立调用
    person1.obj.foo1().call(person2) //person2:call显示绑定
    
    person1.obj.foo2()() //obj:返回的是箭头函数,this由上层作用域foo2函数决定,foo2的this指向obj(foo2是被obj直接调用的),所以返回的箭头函数this也是指向obj
    person1.obj.foo2.call(person2)()//person2:箭头函数上层作用域this被call指向person2,所以箭头函数也是指向person2
    person1.obj.foo2().call(person2)//obj:箭头函数this不能被显示绑定
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    什么是独立IP,独立IP主机怎么样?
    R语言ggplot2可视化:使用ggpubr包的ggdotplot函数可视化点阵图(dot plot)、设置add参数添加均值和标准差竖线
    MVCC和BufferPool缓存机制
    Linux系统基本使用 与 程序部署 - JavaEE初阶 - 细节狂魔
    jdbc连接oracle数据库
    Kubernetes Service/Pod DNS 记录的添加时机
    深度学习论文: ISTDU-Net:Infrared Small-Target Detection U-Net及其PyTorch实现
    Python绘图系统22:实现系统菜单
    算法-版本号升级
    开发环境和准备工作
  • 原文地址:https://blog.csdn.net/m0_46217225/article/details/125446972