javascript中this的指向问题非常具有迷惑性,而分辨this指向问题简单来说可以记住以下两点。
在this定义时并不能确定它的指向
在通常情况下this永远指向其执行时所在函数的所属对象
下面简单从这两点谈谈this的指向问题
一,在this定义时并不能确定它的指向
那为什么在this定义时不能确定this的指向呢,如果在this定义时就确定了它的指向不是就简单多了吗。 这是因为js是一门非常灵活的语言,函数在js中属于引用数据类型,什么叫引用数据类型,简单来说定义一个函数
function fun(){
console.log('this is a function')
}
那么fun在栈内存中储存的并不是一段函数体代码,而是储存的是一段类似于0xE3434这样的东西,而0xE3434是一个指向堆内存的指针,当定义一个函数时,会先在堆内存中开一个房间,把函数体放到这个房间中,把这个房间的放假号设为0xE3434,然后再把这个房间号储存在fun这个变量上面,而当我们执行fun时,js引擎会先拿到这个房间号,然后再去这个房间号对应的房间去拿函数体来执行。简单的画了个图,感受一下
let obj1 = {
x:'obj-x',
fun:function () {
console.log(this.x)
}
}
let obj2 = {
x:'obj2-x',
fun:obj1.fun
}
obj2.fun() // obj2-x
在上面这个代码中obj2中变量fun储存的就是obj1中变量fun储存的内容,所以在obj2.fun()执行时函数中的this指向obj2,换言之是obj2让js引擎去该房间拿东西,所以this指向的是obj2。 这就是为什么 ### 在this定义时并不能确定它的指向 ### 因为并不确定this在什么环境下执行,自然也不能确定this的指向。
二,在通常情况下this永远指向其执行时所在函数的所属对象
这个问题在上面也进行了一部分说明,现在来说另一部分。什么叫 “通常情况下”。 通常情况下是指在没有人为改变this指向的情况下,而人为绑定this指向可以通过以下几种方式:
function.call
call 方法是一个提供给函数调用的方法,调用方法类似与
fun.call(obj,arg1,arg2,...)
第一个参数就是你想要给fun所绑定的this值,其后的参数是想要传递给fun函数的实参。上面代码的意思就是:把fun的this绑定到obj上,然后传给fun的参数为arg1,arg2, ...,并且执行fun函数。 值得注意的是call方法是一个一次性的方法,既只有在调用call方法时this的指向才会改变,调用结束后this指向又变为不明确。
fun.call(obj,arg1,arg2,...) // this => obj
fun() // this => window
该方法并不能永久改变this指向。
function.apply
fun.apply(obj,[arg1,arg2,...])
该方法同call方法的使用方法相同,唯一不同的时apply只接收两个参数,第二个参数是需要传递给fun的实参数组。 那call和apply只有这一个区别吗,是的,两者只有这个区别。
function.bind
bind也是用于显示绑定this,只不过bind会返回一个永久改变this的原函数的拷贝 例:
let obj1 = {
x: 'obj1-x',
fun: function () {
console.log(this.x)
}
}
function fun2() {
console.log(this.x)
}
fun2.bind(obj1)() // obj1-x
fun2() // undefined
bind也可以接收多个参数,多余参数会作为返回函数的初始参数。
箭头函数
es6中新增了一种函数的定义方式:箭头函数,箭头函数在执行时会自动将函数内的this指向与函数所在上下文的this指向保持一致,也就是说,无论是哪个人让js引擎去取函数代码,函数的this指向都与这个人所在上下文的this指向保持一致。
new
new 是js中的一个操作符,也是js中少有的一个一目操作符,new 的作用对象是一个构造函数,构造函数与普通的函数并没有什么不同,之所以叫构造函数是从一个函数的用途来区分,如果一个函数是用来实例化一个子类的,那么这个函数就可以叫做构造函数,正式因为js不是传统的面向对象语言,所以在es6之前通过new操作符来实现子类的继承属性。而new操作符会把构造函数中的this指向绑定到操作接收方。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.showCarYear=function(){
console.log(this.year)
}
}
var mycar = new Car("Eagle", "Talon TSi", 1993);
mycar.showCarYear() // 1993
这段代码创建了 mycar 并给他的属性指定值,于是 mycar.make 的值为"Eagle", mycar.year 的值为1993,以此类推。