• 函数在调用时,JavaScripts会默认给this绑定一个值;
  • this的绑定和定义的位置无关;
  • this的绑定和调用方式以及调用的位置有关;
  • this是在运行时被绑定的;

this的绑定方式:

  1. 默认绑定:通过函数进行调用,是独立函数,则this指向全局(global/window)
  2. 隐式绑定:通过方法进行调用,则this绑定到了所调用的对象上
  3. 显示绑定:可以指定绑定对象(bind call apply)
  4. 箭头函数(Arrow Function)
  5. 构造函数(new)

默认绑定


function foo() {
    console.log(this)
}

foo()//window

隐式绑定

var obj = {
    name: "obj",
    foo: foo
}
function foo() {
    console.log(this)
}

obj.foo()//obj

foo()绑定在obj对象上,this指向obj

Example Code 1

function app(){
  console.log(this)
  this.appVaribale = 'hello'
}

app()                          //Window                   
console.log(appVaribale)       //hello
console.log(app.appVaribale)   //underfind ,appVaribale绑定到了window上
console.log(this)              //window

this-1 调用app函数时,this指向window,运行 console.log(appVaribale) 代码时window增加了appVaribale对象,且值为 “hello”。与app函数无关,所以运行 console.log(app.appVaribale) 这行代码时JS引擎在app函数里面找不到appVaribale对象,则返回 undefined

显示绑定

  • bind: function.bind(thisArg[, arg1[, arg2[, ...]]])

  • call: function*.call(thisArg, arg1, arg2, ...)

  • apply: func*.apply(thisArg, [argsArray])

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

function add(c, d) {
  console.log(this.a + this.b + c + d);
}
var num = {a: 1, b: 3};

add.call(num, 5, 7); // 16
add.apply(num, [10, 20]); // 34

add.call(undefined, 5, 7); // 16
add.apply(null, [10, 20]); // 34

箭头函数

箭头函数没有自己的this,都是从ParentScope继承的

var title = "window"
const video = {
  title: 'a',
  play: function() {
    return () => {
      console.log(this.title)
    }
  }
};

var fn1 = video.play();
fn1()//a

var fn2 = video.play;
fn2()()//window
  • 调用video.play函数赋值给了fn1,fn1是一个箭头函数
    () => { console.log(this.title) } ,这个箭头函数的this是从ParentScope 处继承来的,video.tilte = “a”。
  • 引用video.plya函数,fn2是一个匿名函数调用后返回一个箭头函数
    () { return () => { console.log(this.title) } },调用箭头函数后,this指向window,因为它从上层作用域 play 继承了this。

构造函数(new)

this 被绑定到正在构造的新对象

function Video(title) {
  console.log(this)
  this.title = title
}

var video = new Video("a")

绑定优先级

new绑定 > 显示绑定(bind()>call() / apply()) > 隐式绑定(obj.foo()) >默认绑定(独立函数调用)

练习题

习题来自于: 跟着coderwhy学习JavaScript高级(四) - 掘金 (juejin.cn)

var name = "window"
var person = {
    name: "person",
    sayName: function () {
        console.log(this.name)  
    }
};

function sayName() {
    var sss = person.sayName;
    sss()                       //window
    person.sayName();           //person
    (person.sayName)();         //My Mistake  === person.sayName();
    (b = person.sayName)();     //window
}

sayName()
  • sss() : person.sayNamefunction () {console.log(this.name)} 赋值给 sss ,当调用 sss 函数时, 相当于函数独立调用(默认绑定), 指向window
  • person.sayName() : 通过person调用(隐式绑定)
  • (person.sayName)() : 等价于person.sayName() ,与sss() 不同的是, person.sayName 赋值给了新的变量sss .
  • (b = person.sayName)() : 等价于sss()

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
person1.foo1.call(person2)      //person2

person1.foo2();                 //window,上层作用域是全局
person1.foo2.call(person2)      //window foo2为箭头函数优先级>call/bind/apply

person1.foo3()();         //window独立函数调用,(person.foo3())()
person1.foo3.call(person2)()    //window
person1.foo3().call(person2)    //person2

person1.foo4()();               //person1,返回的箭头函数指向上层foo4函数指向person1
person1.foo4.call(person2)()    //person2
person1.foo4().call(person2)    //person1

person1.foo1() : 隐式绑定
person1.foo1.call(person2) : 显示绑定, 通过 call() 绑定到了person2上

person1.foo2() : 箭头函数要注意, 箭头函数本身没有 this 值, this值是从上层作用域(parentScope)继承而来的, foo2的上层作用域是window.

💡 注意person1 是一个对象而非函数, 所以foo2 的上层作用域不是person1

person1.foo2.call(person2) : 箭头函数优先级 > 显示绑定( call/bind/apply )

person1.foo3()() : 等价于(person1.foo3())() , 独立函数调用(默认绑定) person1.foo3.call(person2)() : 等价于 (person1.foo3.call(person2))() , 独立函数调用(默认绑定)
person1.foo3().call(person2) : 最后调用的函数绑定到了person2(显示绑定)

person1.foo4()() : 返回的箭头函数指向上层 foo4 函数指向person1 person1.foo4.call(person2)() : 返回的箭头函数指向上层foo4函数指向person2 person1.foo4().call(person2) : 返回的箭头函数指向上层foo4函数指向person1

💡 箭头函数自身没有 this 值, this值是从上层作用域(parentScope)继承而来的


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()() //window
    person1.obj.foo1.call(person2)() //window
    person1.obj.foo1().call(person2) //person2

    person1.obj.foo2()() //obj
    person1.obj.foo2.call(person2)() //person2
    person1.obj.foo2().call(person2) //obj
  • person1.obj.foo1()() : 独立函数(默认绑定)

  • person1.obj.foo1.call(person2)() : 相当于 (person1.obj.foo1.call(person2))() 也是独立函数

  • person1.obj.foo1().call(person2) : 显示绑定, 绑定到了person2 上

  • person1.obj.foo2()() : 相当于 (person1.obj.foo2())() , 返回了一个箭头函数后调用它, 箭头函数的 this 继承于上层作用域 foo2 .

  • person1.obj.foo2.call(person2)() : 与person1.obj.foo2()() 类似, 但上层作用域 foo2 的this 被绑定到了 person1 上.

  • person1.obj.foo2().call(person2) : 返回的箭头函数指向上层 foo2 的 this 指向person2


Reference

MDN | this
深入理解js this绑定
跟着coderwhy学习JavaScript高级(四)