每日一篇之——彻底搞懂this指针

455 阅读3分钟

1.this的绑定类型

1.1 规则一:默认绑定

独立函数调用时(fn()),this都指向window。跟函数定义的位置没有关系

案例一

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

foo()//window

案例二

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

function foo2() {
  console.log(this)
  foo1()
}

function foo3() {
  console.log(this)
  foo2()
}

foo3()//window window window

案例三

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

var bar = obj.foo
bar() // window

1.2 规则二:隐式绑定

通过某个对象发起的函数调用(obj.fn)

案例一:

var obj = {
  name: "CLF",
  foo: foo
}

obj.foo() // obj对象

案例二:

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

var obj2 = {
  name: "obj2",
  bar: obj1.foo
}

obj2.bar() //obj对象

1.3 规则三:显式绑定call/apply/bind

bind的优先级高于call和apply。

call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定

function foo() {
  console.log("函数被调用了", this)
}

var obj = {
  name: "obj"
}

foo.call(obj)  //obj
foo.apply(obj)  //obj
foo.apply("aaaa")  //"aaaa"

call和apply的区别:

如果函数需要传入参数,call是直接传入参数,而apply是以数组形式传入参数

function sum(num1, num2, num3) {
  console.log(num1 + num2 + num3, this)
}

sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])

我们可以使用bind,将一个函数总是显示的绑定到一个对象上

我们发现newFoo()和默认绑定规则冲突,根据结果可以得到:bind显示绑定优先级高于默认绑定

var newFoo = foo.bind("aaa")

newFoo() //'aaa'

1.4 规则四:new绑定

JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字

我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象

使用new关键字来调用函数时,会执行如下的操作:

1.创建一个全新的对象;

2.这个新对象会被执行prototype连接;

3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);

4.如果函数没有返回其他对象,表达式会返回这个新对象;

function Person(name, age) {
  this.name = name
  this.age = age
}

var p1 = new Person("CLF", 18)
console.log(p1.name, p1.age) //CLF 18

var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age) //kobe 30

2.绑定规则优先级

总结:new绑定 > 显示绑定(call/apply/bind)>隐式绑定(obj.foo())> 默认绑定(foo())

  • 默认绑定优先级最低
  • call/apply的显示绑定高于隐式绑定
obj.foo.call("abc")     //abc 
obj.foo.apply("abc")     //abc                             
  • bind的显示绑定高于隐式绑定
function foo(){
console.log(this)
}

var obj = {
  foo:foo.bind('aaa')
}

obj.foo()  //'aaa'
  • new关键字高于显示绑定(new不能和call/apply共用)
function foo(){
console.log(this)
}
var bar = foo.bind('aaa')

var obj = new bar()  //foo()

3.this规则之外

3.1忽略显示绑定

  • 忽略显示绑定:apply/call/bind当传入null或者undefined时,自动将this指向window
foo.apply(null)  //window
foo.apply(undefined)  //window

3.2间接函数引用

  • 间接函数引用:注意obj2的分号必须加,否则会因为代码编译报错
var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2"
};


(obj2.bar = obj1.foo)()  //window

4.this的其他补充

4.1.setTimeout的this

function hySetTimeout(fn, duration) {
  fn.call("abc")
}

hySetTimeout(function() {
  console.log(this) // window
}, 3000)

setTimeout(function() {
  console.log(this) // window
}, 2000)

4.2.监听点击事件的this

const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
  console.log(this)     //<div class='box'></div>
}

boxDiv.addEventListener('click', function() {
  console.log(this)      //<div class='box'></div>
})
boxDiv.addEventListener('click', function() {
  console.log(this)      //<div class='box'></div>
})
boxDiv.addEventListener('click', function() {
  console.log(this)      //<div class='box'></div>
})

43.数组.forEach/map/filter/find 都可以接收第二个参数,进行this指向

var names = ["abc", "cba", "nba"]

names.forEach(function(item) {
  console.log(this)               //window
})
names.forEach(function(item) {
  console.log(this)
},'abc')                          //abc
names.map(function(item) {
  console.log(this)                //cba
}, "cba")

5.箭头函数的this获取

5.1.箭头函数中this指向

箭头函数中的this,永远指向外层作用域中最接近自己的普通函数的this

箭头函数的this指向该函数定义时所在的作用域

var name = "CLF"

var foo = () => {
  console.log(this);
}

foo()             //window

var obj = {foo: foo}
obj.foo()         //window
foo.call("abc")   //window

5.2应用场景

箭头函数(因为箭头函数的this指向定义时的对象,即obj)

var obj = {
  data: [],
  getData: function() {
    setTimeout(() => {
      console.log(this)
    }, 2000);
  }
}

obj.getData()