JS 的 this 指向问题是老生常谈的难点了。我当初从 Java 转过来时极其不适应,花了好长时间才摆脱这个阴影。网上也有很多关于 this 的文章了,本文就简单说说,再聊点不一样的。
this 主要是在函数中使用,在函数外使用的话,一律指向全局对象
console.log(this) // window
var o = {
global: this // window
}
在函数内使用 this 时,具体指向是由函数的调用方式决定,而不是根据函数定义方式决定
1. 函数作为普通函数运行时,可以看做是当做全局对象的方法运行,此时 this 指向全局对象
function hello() {
console.log(this)
}
hello() // window 作为普通函数运行
2. 函数作为对象的方法调用时,函数内的 this 指向该对象
所以上面一条,普通函数可以看做是全局对象的方法,所以 this 指向全局对象
var name = 'global'
var obj = {
name: 'local',
getName: function () {
console.log(this.name)
},
outer: function () {
function inner() {
console.log(this.name)
}
inner() // 这是第 12 行代码
}
}
obj.getName() // local 作为 obj 的方法调用,此时 this 指向 obj
obj.outer() // 打印什么?控制台试试吧
这里提到了一个大家容易忽视的点: 嵌套函数
还记得上面标题说的吗? this 的指向是由函数的调用方式来决定的
由于大多数嵌套函数是直接被调用的, 比如:第 12 行代码,调用 inner()这时 inner 是被当做普通函数调用的
,也可以看做是 window.inner() ,所以此时 inner 内部的 this 指向 window,打印 global
常见的解决方案如下:
var obj = {
name: 'local',
outer: function () {
var that = this
function inner() {
console.log(that.name)
}
inner()
}
}
// 声明变量 that ,在 inner 内部用 that 代替 this
obj.outer() // local
还有个问题
var name = 'global'
var obj = {
name: 'local',
getName: function () {
console.log(this.name)
}
}
obj.getName() // local
var getName = obj.getName
getName() // ?
直接调用 getName() 会打印 global
还是前面说的,虽然 obj.getName() 在声明的时候是作为 obj 的方法
但是把它赋值给 getName, 再调用 getName() 和 obj.getName() 的调用方式已经不同了
3. apply call bind 改变 this 指向
这个没啥还说的,强制改变 this 的指向,bind 的优先级最高。
var name = 'global'
var obj = {
name: 'local',
getName: function () {
console.log(this.name)
}
}
var obj2 = {
name: 'xiaoming'
}
var obj3 = {
name: 'laowang'
}
obj.getName() // local
obj.getName.call(obj2) // xiaoming
var getName = obj.getName.bind(obj3) // 将 getName 内部的 this 绑定到 obj3
getName() // laowang 不再是 window
getName.call(obj2) // laowang
// bind 不会受到 apply 和 call 影响
4. 定时器设置的函数,this 会指向全局
var name = 'global'
var obj = {
name: 'local',
getName: function () {
console.log(this.name)
}
}
var obj3 = {
name: 'laowang'
}
setTimeout(obj.getName, 1000) // global
setTimeout(obj.getName.bind(obj3), 2000) // laowang
bind 强制绑定优先级最高,不受定时器影响
正确调用方式如下:
外面包一层匿名函数
setTimeout(function () {
obj.getName()
}, 1000) // local
5. ES6 箭头函数
ES6 推出了箭头函数,详细教程可以参考阮一峰老师的教程箭头函数没有自己的 this 和 arguments
, 因此在箭头函数内部使用 this 和 argments, 其实使用的是外层的 this 和 arguments
var name = 'global'
var obj = {
name: 'local',
getName: () => console.log(this.name),
outer: function () {
var inner = () => console.log(this.name)
inner()
}
}
obj.getName() // global 由于箭头函数没有自己的 this,所以 getName 内部的 this 其实是函数外部的 this,指向全局
obj.outer() // local
作为函数的方法,最好不要用箭头函数,因为箭头函数内部的 this 不再指向该对象。
所以箭头函数虽然好用,但是不要滥用哦
箭头函数还常用于数组的 forEach/map/some/every/filter/reduce 等循环方法中,比如
var arr = [1, 2, 3].map(item => item * 2)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。