接着上一章继续讲,还有两个this的绑定规则就是显式绑定和new绑定。
显式绑定
顾名思义,显式绑定意思就是明确的将某个对象绑定到this上,我们知道Javascript有三个API:apply,call,bind。他们的共同作用就是手动改变this指向。来看栗子:
function foo() {
console.log('foo函数调用',this)
}
foo()
此时调用foo函数,this指向window对象没毛病。如果我想将this指向为obj对象怎么办,一种方法是在obj对象中添加foo方法,然后通过obj.foo()的方式调用。但是若规定不允许改变obj对象呢?此时apply上场了,我们可以这么做:
function foo() {
console.log('foo函数调用',this)
}
const obj = {
name: 'zengge'
}
foo.apply(obj)

看,此时的this是不是指向了obj对象?这就是apply最基础的用法。实际上apply还有第二个参数,可以传递参数到foo函数。
function foo(name, age) {
console.log('foo函数调用', this, name, age)
}
const obj = {
name: 'zengge'
}
foo.apply(obj, ['zengge', 22])

这就是apply的用法,call跟apply唯一的区别就是在第二个参数上,apply是以数组形式传参,而call是一个一个传参。
function foo(name, age) {
console.log('foo函数调用', this, name, age)
}
const obj = {
name: 'zengge'
}
foo.call(obj, 'zengge', 22)

bind与apply和call的调用时机不同,bind会返回一个函数并且可以选择调用时机,而apply和call必须立即调用。
function foo(name, age) {
console.log('foo函数调用', this, name, age)
}
const obj = {
name: 'zengge'
}
const newFoo = foo.bind(obj, 'zengge', 22)
newFoo()
foo.bind()执行后会返回一个函数,这个函数可以在你想执行的地方执行。如上图newFoo函数是独立调用,按理说this会执行默认绑定规则,应该指向window对象,但是因为newFoo是bind返回的函数,返回之前已经将this显式绑定到obj对象上了,所以绑定规则也有优先级:显式绑定大于默认绑定。下文会详细介绍绑定规则的优先级。
new绑定
Javascropt中有一个关键字new,当我们通过new来调用一个函数时,这个时候this是在调用这个函数的时候创建出来的对象,即this的指向是这个函数本身。这个过程称之为new绑定。
function Foo(name, age) {
this.name = name
this.age = age
}
const foo = new Foo('zengge', 22)
console.log(foo.name, foo.age)

那么这个new关键字具体做了什么:
- 创建了一个空对象
- 将空对象的原型指向了构造函数本身的原型
- 将空对象作为构造函数的上下文,即改变了
this的指向 - 对构造函数有返回值的处理判断
我们重点来讨论第3点,其余几点我们会在以后的面向对象章节中详细讨论。 在new操作符的内部改变this的指向时,实际就是使用了apply,将this指向了创建的那个空对象。即fn(构造函数).apply(obj(创建的空对象), args(传递的参数,若有))。
绑定规则的优先级
毫无疑问,默认绑定优先级是最低的,存在其他规则时,就会覆盖默认绑定。
当隐式绑定与显式绑定冲突时,谁的优先级更高呢?答案肯定是显式绑定。
const obj = {
name: 'zengge',
foo
}
const obj2 = {
name: 'zengdada'
}
function foo() {
console.log(this)
}
obj.foo.apply(obj2)

那么问题来了new绑定和显式绑定的优先级谁高呢?一起来研究一下:
const obj = {
name: 'zengge'
}
function Foo() {
console.log(this)
}
const bar = foo.bind(obj)
new bar()

由此可知,new绑定的优先级大于显式绑定
结论: new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
this规则之外
凡是总有个例外,同样这个this绑定也有个例外。如果我们显式地将this指向null或者undefined会怎么样呢?它会变成null或者undefined吗?
function foo() {
console.log(this)
}
foo.apply(null)
foo.apply(undefined)

可见this并没有像我们像的那样指向null或者undefined,而是指向了window对象。这应该是显式绑定时apply内部有机制处理,不让this显式绑定成null或者undefined。
其他一些函数的this
setTimeout
setTimeout(function() {
console.log(this)
}, 2000)
这里的this比较特殊,this指向全局对象也就是window对象,这是因为setTimeout函数内部改变了传入函数的this的指向。
监听函数
const div = document.getElementById('box')
div.onclick = function() {
console.log(this)
}

这里实际上就是将function函数作为div的一个属性进行了隐式绑定。所以this指向的就是div节点。
forEach/map/filtter/reduce/find
const arr = [1,2,3]
arr.forEach(function(item) {
console.log(item, this)
})
这这里的回调函数function也是在forEach内部执行的,结果跟setTimeout一样,this指向window的对象,特殊的是forEach/map/filtter/reduce/find这些函数还可以手动改变this指向,实际上除了回调函数还有第二个参数,用于改变回调函数中的this指向。
const obj = {
name: 'zengge'
}
const arr = [1,2,3]
arr.forEach(function(item) {
console.log(item, this)
}, obj)

箭头函数的this
在Javascript中函数有两种写法,一种是普通函数function(){},另一种是箭头函数() => {},我们上面说的都是普通函数中的this指向,那么箭头函数的this指向跟普通函数一样吗?我们来看看:
const obj2 = {
name: 'zengdada'
}
const obj = {
name: 'zengge',
foo
}
const foo = () => {
console.log(this)
}
foo()
obj.foo()
foo.apply(obj2)

结果全都是window对象,这是因为箭头函数不会绑定this,它的this指向的是上层作用域。这一特性在Javascript中非常有用,举一个实际开发中的栗子:
const dataObj = {
data: [],
getData(){
const _this = this
setTimeout(function() {
const dataList = ['zengge','zengdada','zyq']
_this.data = dataList
}, 2000)
}
}
dataObj.getdata()
在setTimeout函数中的this我们上面说过指向的是全局this,也就是window对象。所以我们如果直接使用this的话是不能直接拿到dataObj中的data然后进行赋值的。只能在getData函数中创建一个_this变量指向getData中的this(实际上getData中的this就是指向dataObj对象,因为在外部调用getData时是隐式绑定,this指向dataObj对象),随后在setTimeout中将dataList赋值给_this,这样才能正确完成赋值。这也是很多人在开发中用到的操作,但是有了箭头函数就根本没有必要这样创建中间变量来赋值,直接取上层作用域(getData)中的this。
const dataObj = {
data: [],
getData(){
setTimeout(() => {
const dataList = ['zengge','zengdada','zyq']
this.data = dataList
}, 2000)
}
}
dataObj.getdata()
所以要巧用箭头函数可以让开发变得更加简洁和高效。
还有其他函数就不一一列举了,掌握了以上的规则以及常见函数的this绑定,开发和一般的面试就已经够了,那么this就聊到这了,加油学习~~
本文详细讲解了JavaScript中的显式绑定(通过apply、call和bind改变this指向)、new绑定(构造函数中this指向新创建对象)及其优先级规则,包括隐式绑定和默认绑定的比较。还探讨了特殊情况下this的指向,以及箭头函数的this行为。
361

被折叠的 条评论
为什么被折叠?



