在JavaScript编程中,this关键字总是让初学者感到迷惑,this的指向总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境;
1.1 this
在JavaScript编程中,this关键字总是让初学者感到迷惑,this的指向总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境;
1.1.1 this的指向
this的指向大致可以分为以下4种:
- 作为对象的方法调用。
- 作为普通函数调用。
- 构造器调用。
- Fuction.prototype.call 或 Function.prototype.apply调用
1.作为对象的方法调用
当作为对象的方法被调用时,this
指向该对象:
const obj = {
a: 1,
getA: function() {
console.log(this === obj) // 输出:true
console.log(this.a) // 输出: 1
}
}
obj.getA()
2.作为普通函数调用
当函数不作为对象的属性被调用时,也就是普通函数方式,此时的 this
总是指向全局对象window
.
window.name = 'globalName'
const getName = function() {
return this.name
}
console.log(getName()) // 输出: globalName
或者:
window.name = 'globalName'
var myObject = {
name: 'sven',
getName: function() {
return this.name
}
}
const getName = myObject.getName
console.log(getName()) // 输出: globalName
在ECMAScript5中的 strict
模式下,this被规定不会指向全局对象,而是 undefined
3.构造器中调用
当用 new
运算符调用函数式,该函数总会返回一个对象,通常情况下,构造器里的 this
就指向返回的这个对象,如下代码:
const myClass = function() {
this.name = 'sven'
}
const obj = new myClass()
console.log(obj.name) // 输出:sven
还要注意一个问题,如果构造器显示的返回了一个object
类型的对象,那么此次运算结果最终会返回这个对象,而不是我们之前期待的 this
:
var MyClass = function() {
this.name = 'sven'
return {
name: 'anne'
}
}
const obj = new MyClass()
console.log(obj.name) // 输出:anne
当然如果构造器返回的是一个非对象类型的数据,就不会造成上述问题:
var MyClass = function() {
this.name = 'sven'
return 'anne'
}
const obj = new MyClass()
console.log(obj.name) // 输出:sven
4.Function.prototype.call
或 Function.prototype.apply
调用
跟普通的函数调用相比Function.prototype.call
或 Function.prototype.apply
可以动态地改变传入函数的this
:
const obj1 = {
name: 'sven',
getName: function() {
return this.name
}
}
const obj2 = {
name: 'anne'
}
console.log(obj1.getName()) // 输出:sven
console.log(obj1.getName.call(obj2)) // 输出: anne
1.1.2 丢失的 this
const obj = {
name: 'sven',
getName: function() {
return this.name
}
}
console.log(obj.getName()) // 输出:sven
const getName2 = obj.getName
console.log(getName2()) // 输出: undefined
console.log(getName2().call(obj)) // 输出: sven
当调用obj.getName()
时,getName方法是作为obj对象的属性被调用的,所以此时的this
指向obj
,所以obj.getName()
输出 sven
当用一个变量getName2
来引用obj.getName
并且调用时,此时是普通函数调用,this
指向全局windows
,所以程序执行结果是 undefined
。
当调用getName2().call(obj)
,this
指向修正为obj
,所以输出sven
。
2.2 call 和 apply
Function.prototype.call
或Function.prototype.apply
,在实际开发中,call
和apply
方法很有用处,能熟练运用这两个方法,是我们真正成为一名JavaScript程序员的重要一步。
2.2.1 call
和 apply
的区别
作用一模一样,区别在于传入参数形式的不同。
apply
接受两个参数,第一个参数指定了函数体内this
对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply
方法把这个集合中的元素作为参数传递给被调用的函数:
const func = function(a, b, c) {
console.log([a, b, c]) // 输出: [1, 2, 3]
}
func.apply(null, [1, 2, 3])
上述代码中,参数1
,2
,3
被放在数组中一起传入 func函数,他们分别对应func参数列表中的 a
、b
、c
call
传入的参数数量不固定,跟apply
相同的是,第一个参数也是代表函数体内的this
指向,从第二个参数开始往后,每个参数被依次传入函数:
const func = function(a, b, c) {
console.log([a, b, c]) // 输出: [1, 2, 3]
}
func.apply(null, 1, 2, 3)
当使用apply
或 call
的时候,如果我们传入的第一个参数为null
,函数体内的this
在非严格模式
下指向window
,在严格模式
下,函数体的this
为null
。
2.2.1 call
和 apply
的用途
1.改变this指向
const obj1 = {
name: 'sven'
}
const obj2 = {
name: 'anne'
}
window.name = 'window'
const getName = function() {
console.log(this.name)
}
getName() // 输出: window
getName.call(obj1) // 输出:sven
getName.call(obj2) // 输出: anne
当执行 getName.call(obj1)
这句代码时,getName
函数体内的this
就指向obj1
,getName.call(obj2)
同理。
再看如下代码:
document.getElementById('div1').onclick = function() {
const func = function() {
console.log(this.id)
}
func() // 输出: undefined
func.call(this) // 输出: div1
}
2.借用其它对象的方法
杜鹃鸟既不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等鸟类,让它们代为孵化和养育。在javascript也存在类似的借用现象。
- 借用方法第一种:借用构造函数,实现类似于继承的效果:
const A = function(name) {
this.name = name
}
const B = function() {
A.apply(this, arguments)
}
B.prototype.getName = function() {
return this.name
}
const b = new B('sven')
console.log(b.getName()) // 输出: sven
- 借用其它方法
比如函数的参数列表argument
是一个类数组对象,虽然它也有下标,但它并非真正的数组,所以也不能像数组一样,进行排序操作或添加删除元素等。这时候我们可以借用Array.prototype
对象上的方法,比如往argument
中添加一个新的元素,通常会借用Array.prototype.push
:
(function() {
Array.prototype.push.call(argument, 3)
console.log(argument) // [1, 2, 3]
})(1,2)
以上是本人对于this、call、apply的一些理解
码字不易,你的赞♥是对我最大的支持(≧∇≦)ノ