Javascript中的this(二)

本文详细讲解了JavaScript中的显式绑定(通过apply、call和bind改变this指向)、new绑定(构造函数中this指向新创建对象)及其优先级规则,包括隐式绑定和默认绑定的比较。还探讨了特殊情况下this的指向,以及箭头函数的this行为。

接着上一章继续讲,还有两个this的绑定规则就是显式绑定new绑定

显式绑定

顾名思义,显式绑定意思就是明确的将某个对象绑定到this上,我们知道Javascript有三个APIapplycallbind。他们的共同作用就是手动改变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) 

image.png
看,此时的this是不是指向了obj对象?这就是apply最基础的用法。实际上apply还有第二个参数,可以传递参数到foo函数。

 function foo(name, age) {
     console.log('foo函数调用', this, name, age)
   }
   const obj = {
     name: 'zengge'
   }
   foo.apply(obj, ['zengge', 22]) 

image.png
这就是apply的用法,callapply唯一的区别就是在第二个参数上,apply是以数组形式传参,而call是一个一个传参。

 function foo(name, age) {
     console.log('foo函数调用', this, name, age)
   }
   const obj = {
     name: 'zengge'
   }
   foo.call(obj, 'zengge', 22) 

image.png
bindapplycall的调用时机不同,bind会返回一个函数并且可以选择调用时机,而applycall必须立即调用。

 function foo(name, age) {
     console.log('foo函数调用', this, name, age)
   }
   const obj = {
     name: 'zengge'
   }
   const newFoo = foo.bind(obj, 'zengge', 22)
   newFoo() 

image.png foo.bind()执行后会返回一个函数,这个函数可以在你想执行的地方执行。如上图newFoo函数是独立调用,按理说this会执行默认绑定规则,应该指向window对象,但是因为newFoobind返回的函数,返回之前已经将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) 

image.png
那么这个new关键字具体做了什么:

  1. 创建了一个空对象
  2. 将空对象的原型指向了构造函数本身的原型
  3. 将空对象作为构造函数的上下文,即改变了this的指向
  4. 对构造函数有返回值的处理判断

我们重点来讨论第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) 

image.png
那么问题来了new绑定显式绑定的优先级谁高呢?一起来研究一下:

 const obj = {
     name: 'zengge'
   }
   function Foo() {
     console.log(this)
   }
   const bar = foo.bind(obj)
   new bar() 

image.png
由此可知,new绑定的优先级大于显式绑定
结论: new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

this规则之外

凡是总有个例外,同样这个this绑定也有个例外。如果我们显式地将this指向null或者undefined会怎么样呢?它会变成null或者undefined吗?

 function foo() {
     console.log(this)
   }
   foo.apply(null)
   foo.apply(undefined) 

image.png
可见this并没有像我们像的那样指向null或者undefined,而是指向了window对象。这应该是显式绑定apply内部有机制处理,不让this显式绑定成null或者undefined

其他一些函数的this

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

image.png 这里的this比较特殊,this指向全局对象也就是window对象,这是因为setTimeout函数内部改变了传入函数的this的指向。

监听函数
 const div = document.getElementById('box')
   div.onclick = function() {
     console.log(this)
   } 

image.png
这里实际上就是将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) 

image.png

箭头函数的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) 

image.png
结果全都是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就聊到这了,加油学习~~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值