谁调用就指向谁
this 指向哪?先记住一句话:谁调用就指向谁,记住这句话就成功一大半了。
js
var userName = '张三'
function fn() {
var userName = '李四'
console.log(this.userName) // 张三
}
fn()
函数fn
相当于挂在window
,调用fn()
等同于window.fn()
,所以fn
可以看做是被window
调用,此时this
便指向window
,所以输出结果是函数外的userName
变量。
再看下面的示例:
js
var userName = '张三'
var obj = {
userName: '李四',
fn: function() {
console.log(this.userName) // 李四
}
}
obj.fn()
函数fn
被定义在对象obj
里,通过obj.fn()
调用,回到我们开头的那句话:谁调用就指向谁 ,fn
被obj
调用,所以this
指向obj
,输出结果是李四。
如果使用windowd.obj.fn()
调用呢?结果还是一样的,因为最终是obj
调用,所以this
指向的还是obj
。
接着往下看下面这段代码:
js
var userName = '张三'
var obj = {
userName: '李四',
fn: function() {
console.log(this.name) // 张三
}
}
var func = obj.fn
func()
输出结果为张三,因为代码中只是将obj.fn
赋值给func
,此时函数fn
还没有被调用,真正调用是在func()
,func
挂在window
下,所以最终相当于是window
调用了函数fn
,所以this
指向的是window
。
还是应了那句话:谁调用就指向谁。
其他情况
1、作为一个函数被直接调用
当函数被直接调用,没有挂在任何对象上时,this
指向window
。
js
var userName = '张三'
var obj = {
userName: '李四',
fn: function() {
function a() {
console.log(this.userName) // 张三
}
a()
}
}
obj.fn()
这里在fn
中定义了一个函数a
并调用,注意这里函数a
只是在函数fn
内,它并没有挂在哪个对象上,也没有通过其他方式被调用,而是作为一个函数被直接调用,所以this
指向window
。
2、箭头函数
箭头函数的特点是没有自己的this
也不能通过new
调用。箭头函数通过作用域向上查找,找到离包裹它最近的那一个普通函数的this
作为自己的this
,如果没有则指向全局对象window
。
js
var userName = '张三'
var obj = {
userName: '李四',
fn: function() {
var func = () => {
console.log(this.userName) // 李四
}
func()
},
a: {
b: () => {
console.log(this.userName) // 张三
}
}
}
obj.fn()
obj.a.b()
func
是一个箭头函数,箭头函数通过向上查找找到fn
,fn
的this
指向obj
,所以箭头函数的this
指向的也是obj
,输出结果:李四。
obj.a.b()
则指向window
,输出结果:张三。
3、new 构造函数
当函数通过使用new
调用时,new
过程中会创建一个新的对象,而this
便是指向这个新创建的对象。
js
function fn(name) {
this.userName = name
this.age = 18
console.log(this) // {userName: '张三', age: 18}
}
let data = new fn('张三')
// new的同时内部会默认把this做回返回值返回
console.log('data: ', data) // data: {userName: '张三', age: 18}
如何改变this指向
1、call、apply、bind
可以通过call
、apply
、bind
的第一个参数传值作为this
值,改变this
指向。
js
obj.fn('hello') // hello,张三
obj.fn.call({ userName: '李四' }, 'hello') // hello,李四
obj.fn.apply({ userName: '王五' }, ['hello']) // hello,王五
obj.fn.bind({ userName: '老六' }, 'hello')() // hello,老六
2、call、apply、bind区别
从上面的使用可以看到,这三个API的参数形式除了第一个参数外是有一些区别的
call
和bind
是多参数的形式:call(thisArg, arg1, arg2...)
。apply
是数组的形式:call(thisArg, [arg1, arg2...])
。
另外一个区别是:bind
的返回值是一个函数,需要自己再手动调用一次,而call
和apply
则不用。