this的指向概念
this指向的可以说js是中重中之重,如果要考试,那么它就是逢考必问的题。
要弄清this的指向问题,我们首先要了解一些概念:
- 作用域
- 执行期的上下文
- 上下文对象(this)
1.作用域
定义:**变量(变量作用域又称为上下文)**和函数生效的区域
2.执行期的上下文
当**函数执行**时(在函数发生编译的前一刻),会创建一个执行期的上下文的内部对象。
一个执行期上下文定义了一个函数执行时的环境。
每调用一次函数,就会创建一个新的上下文对象,他们之间是互不影响且独一无二的。
当函数执行完毕,它所执行期上下文会被销毁。
3.上下文对象
*解析器在调用函数每次都会向函数内部传递一个隐含的参数,
这个隐含的参数就是this,this会指向一个对象,这个对象我们称为函数执行的 上下文对象*
函数内this的指向(important!!!)
函数调用方式的不同,`this` 会指向不同的对象:
- 以函数的形式(包括普通函数,定时器函数(setTimeout,setInterval),立即执行函数)调用时,this的指向永远都是window比如
fun();
相当于windoww.fun();
- 以方法的形式调用时,this指向调用方法的那个对象1sff
- 以构造函数的形式调用时,this指向实例对象
- 以事件绑定函数的形式调用时,this指向绑定事件的对象
- 使用 call 和apply 调用时,this 会指向指定的那个对象
注意:this永远指向调用它的对象,new的时候(构造函数),指向new出来的对象
箭头函数的this,要记得,箭头函数没有自己的this,当我们在箭头函数内部使用了this,this会继承外层函数调用的this绑定(无论this绑定的是什么)
1.函数的形式
var name = '全局的name属性'
function demo(){
console.log(this);//输出结果:Window
console.log(this.name);//输出结果:全局的name属性
};
var obj1 = {
name:'西城辉',
sayName:demo
};
var obj2 = {
name:'老王',
sayName:demo
};
由上可知,this的指向是window 对象,所以 this.name 指的是全局的name
2.以方法的形式调用
function demo(){
console.log(this);//输出结果:Object
console.log(this.name);//输出结果:vaw
};
var obj1 = {
name:'xichenghui',
sayName:demo,
};
var obj2 = {
name: 'vaw',
sayName: demo,
};
var name = '全局的name属性';
//以方法的形式调用,this是调用方法的对象
obj2.sayName();
上面的举例可以看出,this 指向的是 对象 obj2 ,所以 this.name 指的是 obj2.name。
在JS中提供的几种改变this指向的函数
js提供改变函数内部的this指向的方法有:call()
,apply()
, bind()
- call()方法
我们先来看看call()。
call()方法的作用:可以调用一个函数,并可以改变这个函数内部的this指向。
call()方法的其他应用:可以实现继承。之所以可以实现继承,就是因为改变了函数内部的this指向
注意:第一个参数中,如果不需要改变this的指向,则传null。
语法:
fn1.call(想要this指向哪里,函数实参1,函数实参2);
举例
例一通过call()调用函数:
const obj1 = {
nickName:'xichenghui',
age:18,
};
function fn1(){
console.log(this);//输出结果:window
console.log(this.nickName);//undefined
}
fn1.call(this);//此时this的指向并没有改变,此时相当于 fn1();
例二通过call()改变this的指向:
const obj1 = {
nickName:'xichenghui',
age:18,
};
function fn1(a,b){
console.log(this);//输出结果:window
console.log(this.nickName);//undefined
console.log(a + b);
}
fn1.call(obj1,2,4);//此时 先将 this 指向 obj1,然后执行 fn1() 函数
例三通过call()实现继承:
//给 Father 增加 name 和 age 属性
function Father(myName,myAge){
this.name = myName;
this.age = myAge;
}
function Son(myName,myAge){
//重要:通过这一步,将Father 里面的 this 修改为 Son 里面的 this;
//然后给Son 加上相应的参数,让Son 自动拥有 Father 里面的属性。最终实现继承
Father.call(this,myName,MyAge);
}
const son1 =new Son {'西尘辉',18};
console.log(JOSN.stringify(son1));//输出结果:{"myName":"西尘辉","myAge":28}
上方代码中,通过 call()
方法,让Son 继承了 Father 里面的 name 和 age 属性
- apply() 方法
apply() 方法的作用:可以调用一个函数,与此同时,它还可以改变这个函数内部的this
指向。这一点和call()
类似。
apply() 方法的应用: 由于 apply() 需要传递数组,有些地方的使用会有很巧妙的应用。
语法:
fn1.apply(想要将this指向哪里,[函数实参1,函数实参2]);//注意和call()语法的区别,函数参数为一个数组
在此处,外面可以看出,call() 和 apply() 方法的作用是相同的。唯一区别在于,apply()里面传入实参,必须是数组或者伪数组
。
Tips:伪数组是基于Object构造函数创建的,它的原型是Object.prototype,所以只拥有原型的属性值,没有Array的数组方法。
举例
例一通过 apply() 改变 this 指向:
let obj1 ={
nickName:'xichenghui',
age:18,
};
function fn1(a){
console.log(this);//输出结果:obj1
console.log(this.nickName);//输出结果:xichenghui
console.log(a);//输出结果:hello
}
fn1.apply(obj1,['hello']);//此处将this指向 obj1 ,然后执行 fn1() 函数
注意:上方的代码中,apply() 里面传实参时,需要以数组的形式。即便是一个实参,也需要传数组
例二apply()方法巧用
我们知道,想要求数组元素的最大值时,数组本身是没有自带方法的。那么该怎么办呢?
虽然数组里面没有获取最大值的方法但是数值里面有Math.max(数字1,数字2,数字3)
方法,可以获取多个数值中的最大值,且,由于apply()方法在传递实参时,必须要以数组的形式,所以我们可以通过Math.max() 和 apply() 来实现要求。
**例子:**求数组中多个元素的最大值:
const arr1 =[3,7,10,8];
//下面代码的目的是,无需改变 this 的指向, 所以: 第一个参数填 null 或者填Math,或者填 this 都可以。严格模式下,不可填写null
//求数组arr1 中元素的最大值
const maxValue =Math.max.apply(Math,arr1);
consoloe.log(maxValue);//10
//求数组 arr1 中元素最小值
const minValue = Math.min.apply(Math,arr1);
consoloe.log(minValue); //3
- bind方法
bind()方法的作用:bind()方法不会调用函数,但是可以改变函数内部的this 指向。
在bind(),apply(),call()三个方法对比之下,我们会发现,实际开发中,bind()方法是使用最多的。如果有函数,我们不需要立刻调用,但是又想要改变这个函数内部的this指向,此时使用bind()函数是最适合的。
语法:
新函数 = fn1.bind(想要将this指向哪里,函数实参1,函数实参2);
参数:
- 第一个参数:在fn1函数运行时,指定fn1函数的this指向。如果不需要改变this 指向,则传null.
- 其他参数:fn1函数的实参
注意:bind()不会调用fn1 函数,但是会返回由指定this和指定实参的原函数拷贝从此处可以看出,bind()方法是有返回值的。