有关this的指向问题应该很多人都会深有感触,this的指向是前端面试中的一个经典问题!因为我一直对this的指向问题理解不深,所以今天记录下来方便以后参考。
this的概述
- 解析器在每次调用函数的时候都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this;
- this指向的是一个对象,函数执行的上下文对象;
- 根据函数的调用方式的不同,this会指向不同的对象;
要想理解this可以先记住这两点:
- this永远指向一个对象;
- this的指向完全取决于函数调用的位置;
第一点挺好理解的,不管在什么地方使用this,它必然会指向某个对象;确定了第一点后,也引出了一个问题,就是this使用的地方到底在哪里,而第二点就解释了这个问题,但关键是在JavaScript语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象下运行,而this就是函数运行时所在的对象。这本来并不会让我们糊涂,但是JavaScript支持运行环境动态切换,也就是说,this的指向是动态的,很难事先确定到底指向哪个对象,这才是最让我们感到困惑的地方。
所以说:this的指向不是在创建时就决定了,而是由执行环境决定的,包括全局环境、对象环境、构造函数环境、事件对象
全局环境
在全局环境下,this就指向了window对象:
var name = 'zhangsan';
function sayName(){
console.log(this.name);
}
sayName(); //输出zhangsan
对象环境
在对象环境下,this指向当前对象:
var obj = {
name : "zhangsan",
sayName : function(){
console.log(this.name);
}
}
obj.sayName(); //输出zhangsan
构造函数环境
在构造函数中,this 会指向创建出来的实例对象:
function Person() {
this.name = 'zhangsan';
}
var p = new Person();
console.log(p.name); // 输出zhangsan
事件对象
在 DOM 事件中使用 this,this 指向了触发事件的 DOM 元素本身:
li.onclick = function(){
console.log(this.innerHTML);
}
this的指向涉及到ES6中新增的箭头函数,那么普通的函数和箭头函数之间又有什么区别呢?
ES6中箭头函数和普通函数的区别
- 普通函数中的this总是指向调用它的那个对象,箭头函数没有自己的this,他的this永远指向其定义环境,任何方法都改变不了其指向,如call()、bind()、apply()。(正是因为它没有this,所以也就不能用作构造函数,也没有原型对象);
- 箭头函数不能当作构造函数,也就是说,不能使用new命令,否则会报错。
- 箭头函数没有原型属性。箭头函数不能使用argumen对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
- 变量提升:由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let、const)关键字,而var所定义的变量不能得到变量提升。故箭头函数一定要定义于调用之前。
那么this的指向怎样改变呢
改变函数内this指向,js提供了三种方法 call() , apply() , bind()
- call()方法
call 第一个作用是可以调用函数 第二个可以改变函数内的this 指向
call 的主要作用可以实现继承
function fn(a,b,c){
console.log(this,a+b+c); // this指向window
}
fn();
fn.call(document,1,2,3);//call改变之后this指向document
//输出 #document 6 1,2,3是实参 结果相加为6
- apply()方法
第一个作用是调用函数 第二个可以改变函数内部的this指向
但是他的参数必须是数组(伪数组)
apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求数组最大值
function fn(a,b,c){
console.log(this,a+b+c);
}
fn();
fn.apply(document,[1,2,3]);
- bind()方法
不会调用原来的函数 可以改变原来函数内部的this 指向
返回的是原函数改变this之后产生的新函数
如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
function fn(a,b,c){
console.log(this,a+b+c); //window
}
let ff = fn.bind('小明',1,2,3); //手动调用一下
那么这三种方法的区别是什么
相同点:
都可以改变函数内部的this指向
区别点:
- call 和 apply 会调用函数,并且改变函数内部的this指向
- call 和 apply 传递的参数不一样,call 传递的参数是aru1,aru2…形式,apply 必须是数组形式
- bind 不会调用函数,可以改变函数内部this的指向
主要运用场景
- call 经常用来做继承
- apply 经常跟数组有关系,比如实现最大值和最小值
- bind 不调用函数但还想改变this指向,可以用来改变定时器中的this指向
总结
- 一般函数中this指向window。
- 对象中函数的this指向,它的 this 是调用该函数的对象。
- 构造函数里的this,指向创建出来的实例。
- 事件处理函数中,它的this指向触发事件的元素。
- 箭头函数没有自己的this,它的this指向上下文中的this(即所处环境的this)