JavaScript中this
与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同
在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)
this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同
ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的
ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)
this的值
当前执行上下文(global,function或eval)的一个属性
在非严格模式下,总是指向一个对象
在严格模式下可以是任意值
全局上下文
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象
函数上下文
在函数内部,this的值取决于函数被调用的方式
类上下文
this 在类中的表现与在函数中类似,因为类本质上也是函数,但也有一些区别和注意事项
在类的构造函数中,this 是一个常规对象。类中所有非静态的方法都会被添加到 this 的原型中
箭头函数
在箭头函数中,this与封闭词法环境的this保持一致。在全局代码中,它将被设置为全局对象
作为对象的方法
当函数作为对象里的方法被调用时,this 被设置为调用该函数的对象
原型链中的 this
对于在对象原型链上某处定义的方法,同样的概念也适用
如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法就在这个对象上一样
getter与setter中的 this
当函数在一个getter或者setter中被调用。用作getter或setter的函数都会把this绑定到设置或获取属性的对象
作为构造函数
当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象
作为一个DOM事件处理函数
当函数被用作事件处理函数时,它的 this 指向触发事件的元素
作为一个内联事件处理函数
当代码被内联on-event处理函数调用时,它的this指向监听器所在的DOM元素
类中的this
和其他普通函数一样,方法中的this值取决于它们如何被调用
示例:下面代码this的值是什么
var obj = {
foo: function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
最后两行函数的值为什么不一样?因为函数的调用不同
JS中的函数调用形式
call方法
指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数
使用格式
func.call(thisValue, arg1, arg2, ...)
call 的第一个参数就是 this 所要指向的那个对象,后面的参数则是函数调用时所需的参数
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
上面代码中,全局环境运行函数f时,this 指向全局环境(浏览器为window 对象)
call 方法可以改变 this 的指向,指定 this 指向对象 obj,然后在对象obj 的作用域中运行函数 f
call 方法的参数,应该是一个对象。如果参数为空、null 和undefined,则默认传入全局对象
apply方法
apply 方法的作用与 call 方法类似,也是改变 this 指向,然后再调用该函数
唯一的区别就是,它接收一个数组作为函数执行时的参数
使用格式
func.apply(thisValue, [arg1, arg2, ...])
复制代码apply 方法的第一个参数也是 this 所要指向的那个对象,如果设为 null 或 undefined
则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数
原函数的参数,在 call 方法中必须一个个添加,但是在 apply 方法中,必须以数组形式添加
apply方法常用用法:
找出数组最大元素(结合使用apply方法和Math.max方法,就可以返回数组的最大元素)
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15
转换类似数组的对象(利用数组对象的 slice 方法,可以将一个类似数组的对象转为真正的数组)
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
将数组的空元素变为 undefined
通过 apply 方法,利用 Array 构造函数将数组的空元素变成undefined
Array.apply(null, ['a', ,'b']) // [ 'a', undefined, 'b' ]
bind方法
bind 方法用于将函数体内的 this 绑定到某个对象,然后返回一个新函数
bind 方法的参数就是所要绑定 this 的对象。如果 bind 方法的第一个参数是 null 或 undefined
等于将 this 绑定到全局对象,函数运行时 this 指向顶层对象(浏览器为 window)
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
上面代码中,counter.inc 方法被赋值给变量func
这时必须用bind方法将 inc 内部的 this,绑定到 counter,否则就会出错
总结
apply 、call、bind 三者都是用来改变函数的 this 对象的指向的
apply、call、bind 三者第一个参数都是 this 要指向的对象
apply、call、bind 三者都可以利用后续参数传参
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用
回到上面示例代码中的函数调用方式
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2)
call方法才是正常的调用形式,其他两种都是语法糖,可以等价地变为 call 形式:
func(p1, p2) 等价于 func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等价于 obj.child.method.call(obj.child, p1, p2)
现在我们的函数调用只有一种形式:func.call(context, p1, p2)
这样,this就好解释了,this,就是上面代码中的 context
this 是你call一个函数时传的context
func(p1, p2)中的this如何确定:当你写下面代码时
function func(){
console.log(this)
}
func()
转换成call形式进行调用
function func(){
console.log(this)
}
func.call(undefined) // 可以简写为 func.call()
理论上上打印出来的this应该是undefined,但是浏览器里有一条规则
如果你传的context是null或undefined,那么 window 对象就是默认的 context
(严格模式下默认 context 是 undefined)
因此上面的打印结果是 window
如果你希望这里的 this 不是 window
func.call(obj) //那么里面的 this 就是 obj 对象了
obj.child.method(p1, p2) 的this如何确定
var obj = {
foo: function(){
console.log(this)
}
}
obj.foo() //转换为call调用方法
obj.foo.call(obj) //this是obj
回到代码
var obj = {
foo: function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 转换为 obj.foo.call(obj),this 就是 obj
bar() // 转换为 bar.call(),由于没有传 context,所以 this 就是 undefined
// 最后浏览器给你一个默认的 this —— window 对象
总结
this就是你call一个函数时,传入的第一个参数