0
点赞
收藏
分享

微信扫一扫

你不知道的this关键字


一、什么是​​this​

​this​​的值:当前执行代码的环境对象,​​this​​的指向不取决于它在什么位置创建,完全取决于函数在什么地方被调用,​​this​​不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。

二、​​this​​的值

全局环境(任何函数调用的外部)中,​​this​​​的值都是全局对象(浏览器中是​​window​​​对象,node中是​​global​​对象)

函数内部环境,​​this​​的值取决于函数被调用的方式

三、​​this​​指向规则


简单总结一个原理:this 永远指向最后调用它的那个对象


  • 函数绑定

直接调用函数的时候​​this​​指向的是全局对象

var name = 'Window.name'
function foo() {
console.log('this',this.name)
}
foo() // Window.name

注意:如果使用的是严格模式的话,全局对象是​​undefined​

var name = 'Window.name'
function foo() {
'use strict'
console.log('this',this.name)
}
foo() // Uncaught TypeError: Cannot read property 'name' of undefined
  • 隐式绑定(方法调用)

当函数作为对象的属性被调用的时候就属于隐式绑定,这个时候,this指向的是这个函数的对象

var obj = {
num:0,
add:function() {
console.log('this',this) // this {num: 0, add: ƒ}
this.num +=1
}
}
obj.add()
console.log('obj',obj) // obj {num: 1, add: ƒ}

在看下面一个栗子:

var name = 'ahwgs'
var obj = {
name:'aaa',
foo:function() {
console.log('name',this.name)
}
}
var res = obj.foo
res() // ahwgs

申明一个​​obj​​​对象,虽然将​​obj.foo​​​的引用赋值给​​res​​​,但实际上此时是​​res()​​​是不带修饰的函数调用(属于第一种函数绑定的情况),所以此时打印的值是​​ahwgs​

下面我们做一点改动:

var name = 'ahwgs'
var obj = {
name:'aaa',
foo:function() {
console.log('name',this.name)
}
}
obj.foo() // name aaa

这就是正常的隐式调用,这时候的​​this​​​为​​obj​​本身

在看一个栗子:

var name = 'ahwgs'
function doFoo(fn) {
console.log('fn',fn)
fn() //f(){console.log('name',this.name)} fn=obj.foo
}
var obj = {
name:'aaa',
foo:function() {
console.log('name',this.name)
}
}
doFoo(obj.foo) // name ahwgs

这时候,​​obj.foo​​​作为参数传递给了​​doFoo​​​,实际上调用还是​​doFoo​​​,这时候​​this​​​指向的是全局对象,所以打印的是​​ahwgs​

最后一个栗子:

var name = 'ahwgs'
function foo() {
var name = 'aaa'
fn()
function fn() {
console.log('name',this.name)
}
}
foo() // ahwgs

借用开头说的一句话,this指向的是这个函数所属的对象所以,​​fn​​指向的是全局对象

  • 显式绑定

使用​​call/apply/bind​​方法进行显式绑定

var name = 'ahwgs'
function foo() {
console.log('this',this)
console.log('name',this.name)
}

var obj = {
name:'obj'
}

foo.apply(obj) // name obj
foo.call(obj)// name obj
foo.bind(obj)()// name obj

这时候的​​this​​​指向都被显式绑定至​​obj​​,此后,无论如何调用函数,总会将obj绑定到foo中的this上。

  • ​new​​绑定

通过​​new​​​关键字调用的函数,属于​​new​​​绑定模式。这时​​this​​关键字指向这个新创建的对象。

function User(name,age) {
this.name = name
this.age = age
this.getInfo = function() {
console.log('info',this.name + '--->'+this.age)
}
}
var user = new User('ahwgs',20)
console.log(user) // User {name: "ahwgs", age: 20, getInfo: ƒ}
console.log(user.getInfo()) // info ahwgs--->20
  • 总结:

  1. 函数是否是new绑定?如果是,则this指向新创建的对象
  2. 函数是否通过call/apply/bind显式绑定或硬绑定?如果是,则this指向指定的对象;
  3. 函数是否在某个上下文对象中隐式调用?如果是,this绑定的是那个上下文对象;
  4. 上述全不是,则使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局window对象。
  5. 记住:this 永远指向最后调用它的那个对象

四、如何改变​​this​​指向

  • 使用ES6箭头函数

箭头函数的 this 始终指向函数定义时的 this,而非执行时,箭头函数中没有 ​​this​​​ 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 ​​this​​​ 绑定的是最近一层非箭头函数的 ​​this​​​,否则,this 为 ​​undefined​​。

看一个栗子:

var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
setTimeout(()=>{
this.foo1()
},100)
}
}
obj.foo2() // aaa

由于定时器中使用的是箭头函数的形式,上一级没有使用箭头函数,所以​​this​​​绑定的是最近一层非箭头函数的​​this​​​,在这里,即​​obj​

  • 在函数内部​​const self = this​
var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
var self = this
setTimeout(function() {
self.foo1()
})
}
}
obj.foo2() // aaa

定义一个变量,将当前的​​this​​​指向改变至​​self​​​中,这样调用​​foo1​​​的时候​​this​​​指向就是​​obj​​这个对象

  • 使用​​apply、call、bind​

先看一下这三个函数的使用方法:

  1. ​apply​
function.apply(obj, [param1,params2,...])
// obj:要绑定的this
// 第二个参数:类数组或数组,作为function的参数传入
// 立即执行
  1. ​call​
function.call(obj, param1, param2, ...)
// obj:要绑定的this
// 第二个参数:函数运行的参数,用逗号隔开
// 立即执行
  1. ​bind​
function.bind(obj, param1, param2, ...)
// obj:要绑定的this
// 第二个参数:函数运行的参数,用逗号隔开
// 返回一个函数

下面使用这三种方法修改​​this​​指向

  1. 使用​​apply​
var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
setTimeout(function() {
this.foo1()
}.apply(obj),100)
}
}
obj.foo2() // aaa
  1. 使用​​call​
var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
setTimeout(function() {
this.foo1()
}.call(obj),100)
}
}
obj.foo2() // aaa
  1. 使用​​bind​
var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
setTimeout(function() {
this.foo1()
}.bind(obj)(),100)
}
}
obj.foo2() // aaa

注意:​​bind​​​与其他两种方法不同,因为他返回的是一个函数,所以需要我们​​()​​去调用它。

  1. 使用​​null/undefined​​​作为​​bind/call/apply​​的参数
var name = 'ahwgs'
var obj = {
name:'aaa',
foo1:function() {
console.log('this.name',this.name)
},
foo2:function() {
setTimeout(function() {
this.foo1()
}.call(null),100)
}
}
obj.foo2() //Uncaught TypeError: this.foo1 is not a function

如果使用​​null/undefined​​​作为参数的话,被调用的时候会被忽略,所以调用​​obj.foo2()​​​的时候​​this​​​指向的是全局对象,而全局对象中却没有​​foo2​​这个函数,所以报错。

  • ​new​​实例化新对象

可看上述​​new​​绑定实例

总结


  • js中的​​this​​指的是允许的上下文环境,与后端语言不同
  • ​this​​不是一成一变的,会随着环境而变化
  • 严格模式与非严格模式下的​​this​​也不一样
  • 可以使用多种方式修改​​this​​的指向
  • 本文首发于:​​你不知道的this关键字​​


举报

相关推荐

0 条评论