跳到主要内容

普通函数

this 绑定原则

  • this 的绑定和定义的位置(编写的位置)没有关系
  • this 的绑定和调用方式以及调用的位置有关系
  • this 是在运行时被绑定的

this 绑定规则


默认绑定

默认绑定独立函数调用(函数没有被绑定到某个对象上进行调用)

隐式绑定

隐式绑定 就是通过某个对象发起的函数调用

隐式绑定的前提条件

  • 必须在调用的对象内部有一个对函数的引用
  • 如果没有这样的引用,在进行调用时,会找不到该函数报错
  • 通过这个引用,间接的将 this 绑定到了这个对象上

显示绑定

我们不希望在对象的内部包含这个函数的引用,同时又希望在这个对象上强制调用,该怎么做呢?

  • call

    function foo(){
    console.log('测试 call');
    }

    foo.call({name:'obj'});
  • apply

    function foo(){
    console.log('测试 call');
    }

    foo.apply({name:'obj'});
  • bind

    function foo(){
    console.log('测试 call');
    }

    var b = foo.bind({name:'obj'});
    b();

new 绑定

new 绑定 的工作如下:

  • 创建一个全新的对象
  • 这个新对象会进行 prototype 连接
  • 这个新对象会绑定到函数调用的 this 上
  • 如果函数没有返回其他对象,表达式会返回这个新对象

this 绑定规则优先级


  1. 默认规则的优先级最低
  2. 显示绑定优先级高于隐式绑定
  3. new绑定优先级高于隐式绑定
  4. new绑定优先级高于bind
  • new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
  • new绑定可以和bind一起使用,new绑定优先级更高

内置函数中的 this 绑定


DOM 事件中的 this 绑定

  • 通过onclick绑定事件: 事件函数通过box隐式绑定,指向box

    <div class="box"></div>
    <script>
    const box = document.querySelector('.box');
    box.onclick = function(e){
    console.log(e);
    console.log(this); // 指向 box 元素
    }
    </script>
  • 通过addEventListener绑定事件: 事件函数通过box隐式绑定,指向box

    <div class="box"></div>
    <script>
    const box = document.querySelector('.box');
    box.addEventListener('click',function(e){
    console.log(e);
    console.log(this); // 指向 box 元素
    });
    box.addEventListener('click',function(e){
    console.log(e);
    console.log(this); // 指向 box 元素
    });
    box.addEventListener('click',function(e){
    console.log(e);
    console.log(this); // 指向 box 元素
    });

setTimeout 中的 this 绑定

  • 普通函数: 该函数独立调用,this 绑定到了全局执行上下文 window

    setTimeout(function(){
    console.log(this); // 该函数独立调用,this 绑定到了全局执行上下文 window
    },200);
  • 箭头函数: 该函数创建环境所在的上层作用域为全局,所以 this 指向为 window

    setTimeout(() => {
    console.log(this); // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
    }, 200);

JS 数组方法中的 this 绑定

  • 普通函数: array.forEach() 内部callback() , 回调函数独立调用,this 指向 window

    const array = ['a','b','c'];
    array.forEach(function(value,index,arr){
    console.log(value,index,arr); // a 0 (3) ['a', 'b', 'c'] 循环遍历 arr 指向数组
    console.log(this); // this 指向 window
    });
  • 箭头函数: array.forEach() 内部callback.call(thisArg) , 通过 call 显示绑定了 array, this 指向 array

    const array = ['a','b','c'];
    array.forEach(function(value,index,arr){
    console.log(value,index,arr); // a 0 (3) ['a', 'b', 'c'] 循环遍历 arr 指向数组
    console.log(this); // this 指向 array
    },array);

this 面试常考问题


  • 根据问题,输出结果--forEach 改变 this 指向

    function foo (value){
    console.log(value,this.name);
    }
    const obj = {
    name:'柏拉文'
    };
    [1,2,3].forEach(foo,obj);
    // 结果为:
    // 1 '柏拉文'
    // 2 '柏拉文'
    // 3 '柏拉文'
  • 根据问题,输出结果,并改正--对象属性为函数,且函数里面嵌套函数

    • this 指向 window

      const obj = {
      data:[],
      getData:function(){
      setTimeout(function(){
      this.data = [1,2,3]; // setTimeout 中的 callback 回调函数是 window 调用的,所以回调函数中的 this 指向 window , this.data === window.data
      console.log(this,this.data); // window window.data = [1,2,3]
      },100);
      }
      }

      obj.getData();
    • 更改 this 指向方案一: 将 this 赋值给另一个变量保存指向

      const obj = {
      data:[],
      getData:function(){ // getData 函数被 obj 对象调用,所以 getData 函数中的 this 指向 obj
      const that = this; // 将 this 赋值给 that , that 指向 obj
      setTimeout(function(){
      that.data = [1,2,3];
      console.log(that,that.data);
      },100);
      }
      }

      obj.getData();
    • 更改 this 指向方案二 将自身调用的函数通过 bind 绑定指向

      const obj = {
      data:[],
      getData:function(){
      setTimeout(function(){
      this.data = [1,2,3];
      console.log(this,this.data);
      }.bind(this),100);
      }
      }

      obj.getData();
    • 更改 this 指向方案三 将自身调用的函数使用箭头函数

      const obj = {
      data:[],
      getData:function(){
      setTimeout(()=>{
      this.data = [1,2,3];
      console.log(this,this.data);
      },100);
      }
      }

      obj.getData();
  • 根据问题,说出输出结果--对象函数相互嵌套之对象属性

    var name = "windowName";
    var person = {
    name: "person",
    sayName: function () {
    console.log(this.name);
    },
    };
    function sayName() {
    var sss = person.sayName;
    sss(); // 自身独立函数调用,this 指向为 window ====> 结果为 windowName
    person.sayName(); // person 隐式调用, this 指向为 person ====> 结果为 person
    (person.sayName)(); // person 隐式调用, this 指向为 person ====> 结果为 person
    (b = person.sayName)(); // 自身独立函数调用, this 指向为 window ====> 结果为 windowName
    }
    sayName();
  • 根据问题,说出输出结果--对象函数相互嵌套之 call 与 this

    var name = "windowName";
    var person1 = {
    name:'person1Name',
    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:"person2Name"};
    person1.foo1(); // person1 隐式绑定,this 指向为 person1 ====> 结果为 person1Name
    person1.foo1.call(person2); // person1 隐式绑定 、 person2 显示绑定 ,且显示绑定优先级高于隐式绑定,this 指向为 person2 ====> 结果为 person2Name
    person1.foo2(); // person1 调用 foo2 ,但是 foo2 为箭头函数,不绑定 this ,所以 foo2 中的 this 指向根据外层作用域决定, foo2 的上层作用域为 window (注意对象不是作用域,作用域只包括全局、函数、块级),所以 this 指向为 window ====> 结果为 windowName
    person1.foo2.call(person2); // person1 调用 foo2 ,且 通过 call 指向了 person 2 。但是 foo2 为箭头函数,不绑定 this ,所以 foo2 中的 this 指向根据上层作用域决定, foo2 的上层作用域为 window (注意对象不是作用域,作用域只包括全局、函数、块级),所以 this 指向为 window ====> 结果为 windowName
    person1.foo3()(); // person1.foo3() 返回一个函数,并且自身独立调用 ===> 结果为 windowName
    person1.foo3.call(person2)(); // person1.foo3.call(person2) 相当于 person1.foo3() 且里面的 this 指向为 person2 ,但是 person1.foo3()() 还是自身独立调用 ===> 结果为 windowName
    person1.foo3().call(person2); // person1.foo3() 返回一个函数,这个返回的函数通过 call 将自身的 this 指向了 person2 ===> 结果为 person2Name
    person1.foo4()(); // person1.foo4() 返回一个箭头函数,并且自身独立调用但是这个箭头函数的上层作用域为 person.foo4() ,所以该箭头函数调用时,this 指向的是上层作用域 person.foo4() 里面的 this,而 person.foo4() 里面的 this 指向 person1 ===> 结果为 person1Name
    person1.foo4.call(person2)(); // person1.foo4.call(person2) 相当于 person1.foo4() 且里面的 this 指向为 person2, person1.foo4.call(person2) 返回一个箭头函数,箭头函数独立调用,但是箭头函数不绑定 this,由上层作用域决定,箭头函数的上层作用域为 person1.foo4(),此时 person1.foo4() 中的 this 通过 call 指向了 person2 ===> 结果为 person2Name
    person1.foo4().call(person2); // person1.foo4() 返回了一个箭头函数,并且通过 call 改变this但是箭头函数不绑定 this ,所以该箭头函数调用时,this 指向的是上层作用域 person.foo4() 里面的 this,而 person.foo4() 里面的 this 指向 person1 ===> 结果为 person1Name
  • 根据问题,说出输出结果--对象函数相互嵌套之 new、call 与 this

    var name = 'windowName';
    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('person1Name');
    var person2 = new Person('person2Name');

    person1.foo1();// person1 调用 foo1() ,隐式绑定 this,this 指向 person1 ===> 结果为 person1Name
    person1.foo1.call(person2);//person1 调用 foo1(), 通过 call 显示绑定 this 到 person2 ,this 指向 person2 ===> 结果为 person2Name
    person1.foo2();//person1 调用 foo2() , 隐式绑定 this 。但是 foo2 为箭头函数,箭头函数不绑定 this ,里面的 this 由上层作用域决定,foo2 箭头函数的上层作用域为 Person ,而 foo2 是通过 person1 调用的,所以 此时 Person 的 this 指向 person1 ==> 结果为 person1Name
    person1.foo2.call(person2);//person1 调用 foo2(), 通过 call 显示绑定 this。但是 foo2 为箭头函数,箭头函数不绑定 this ,里面的 this 由上层作用域决定,foo2 箭头函数的上层作用域为 Person, 而 foo2 是通过 person1 调用的,所以此时 Person 的 this 指向person1 ==> 结果为 person1Name
    person1.foo3()();// person1.foo3() 返回一个普通函数,普通函数自身独立调用 , this 指向 window ===> 结果为 windowName
    person1.foo3.call(person2)();// person1.foo3 函数通过 call 改变了 person1.foo3 中的 this 指向,返回了一个普通函数,普通函数自身独立调用 , this 指向 window ===> 结果为 windowName
    person1.foo3().call(person2);// perso1.foo3 函数返回了一个普通函数,这个普通函数通过 call 显示绑定自身 this 指向为 person2 ===> 结果为 person2Name
    person1.foo4()();// person1.foo4() 函数返回了一个箭头函数,箭头函数自身独立调用。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 指向 person1,所以该箭头函数 this 也指向 person1 ===> 结果为 person1Name
    person1.foo4.call(person2)();// person1.foo4 函数通过 call 显示绑定自身 this 指向为 person2,并且返回一个箭头函数,箭头函数自身独立调用。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 通过 call 显示绑定了 person2,所以该箭头函数 this 也指向 person2 ===> 结果为 person2Name
    person1.foo4().call(person2);// person。foo4 函数返回一个箭头函数,箭头函数通过 call 显示绑定 this 指向为 person2。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 指向为 person1,所以该箭头函数 this 也指向 person1 ===> 结果为 person1Name
  • 根据问题,说出输出结果--对象函数相互嵌套之 new、call 与 this 加强版

    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 函数,返回一个普通函数。普通函数自身独立调用,this 指向 window ===> 结果为 window
    person1.obj.foo1.call(person2)();// person1.obj 通过 call 显示绑定 foo1 函数中的 this 指向为 person2,返回一个普通函数。普通函数自身独立调用,this 指向 window ===> 结果为 window
    person1.obj.foo1().call(person2);// person1.obj 调用 foo1 函数,返回一个普通函数。普通函数通过 call 显示绑定 自身this 指向为 person2 ===> 结果为 person2
    person1.obj.foo2()();// person1.obj 调用 foo2,返回一个箭头函数。箭头函数自身独立调用,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 指向为 obj,所以该箭头函数的 this 指向为 obj ==> 结果为 obj
    person1.obj.foo2.call(person2)();// person.obj 通过 call 显示绑定 foo2 函数中 this 指向为 person2,返回一个箭头函数。箭头函数自身独立调用,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 通过 call 显示绑定为 person2,所以该箭头函数的 this 指向为 person2 ==> 结果为 person2
    person1.obj.foo2().call(person2);// person.obj 调用 foo2 函数,返回一个箭头函数。箭头函数通过 call 显示绑定 this 为 person2,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 指向为 obj,所以该箭头函数的 this 指向为 obj ==> 结果为 obj