思考下面这几种情况下输出的结果,来理解this的指向问题
var name = 'windowName'
function a(){
var name = 'zhangsan'
console.log(this.name)
}
a()
解释:
this 指向的是调用他的对象,上面的 a() 是没有调用者的,所以 this 指向的是默认的 window,var name = 'windowName' 就是先把 name 属性挂到 window 上,然后再赋值为 windowName
var name = 'windowName'
var a = {
name: 'zhangsan',
fn: function(){
console.log(this.name)
}
}
a.fn()
解释:
fn() 的调用者是 a ,所以 this 指向 a ,this.name 也就是 a.name
var name = 'windowName'
var a = {
name: 'zhangsan',
fn: function(){
console.log(this.name)
}
}
var f = a.fn
f()
解释:
fn() 没有调用者,所以 this 指向的是默认的 window ,所以打印的是 windowName
var name = 'windowName'
function fn(){
var name = 'zhangsan'
innerFun()
function innerFun(){
console.log(this.name)
}
}
fn()
解析:
同理,fn() 并没有调用者,所以 this 指向的还是 window
var name = 'windowName'
var a = {
name: 'zhangsan',
func1: function(){
console.log(this.name)
},
func2: function(){
setTimeout(function(){
this.func1()
},100)
}
}
a.func2()
解析:
在这里,setTimeout 中的 this 没有调用者,所以指向的是 window ,所以结果是 undefined
那如何才能使这里的 setTimeout 中的这个 this 指向 a 对象呢?
- 可以使用箭头函数
setTimeout(() => {
this.func1()
},100)
- 可以用一个变量存储指针
var that = this
setTimeout(function(){
that.func1()
},100)
- 使用
函数.call(作用域对象)
setTimeout(function(){
this.func1()
}.call(a),100)
- 使用
函数.apply(作用域对象)
setTimeout(function(){
this.func1()
}.apply(a),100)
可以看到 call 和 apply 都可以改变 this 的指向,但是他们有什么区别呢?
var a = {
name: 'zhangsan',
fn: function(a,b){
console.log(a + b)
}
}
var b = a.fn
// 多个参数是用数组的形式传递
b.apply(a, [1,2])
// 多个参数直接在后面追加传递
a.call(a,1,2)
- 使用
bind绑定作用域,但不立即执行,需要手动再执行
setTimeout(function(){
this.func1()
}.bind(a)(),100)
call的实现原理
Function.prototype.mycall = function(ctx){
ctx = ctx || window
ctx.fn = this // 这个this就是调用者对象
let arg = [...arguments].slice(1)
let result = ctx.fn(...arg)
return result
}