换个姿态学javascript的this
前言
this作为JavaScript中一个大难点,很多初学者一开始都理解不了,或者理解不准确之类的。我在看了《javascript高级程序设计》以及一些大佬的博客后,对this有了新的认识,于是写下来供大家参考。如果该博客有什么错误的地方,请读者指正。
网传的一句话:
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才确定this到底指向谁,实际上this的最终指向的是最后调用它的对象
于是就延伸出了确定this指向的几种方法:
- this是上下文的属性
- 全局环境执行函数时,this是全局对象
- 调用对象的方法时,this是指向该对象
- 函数里调用函数时,this是全局对象
- 还有人说箭头函数的this指向的是外面的this
- 用new构造函数时,this指向的是实例对象
- 可以用call/apply/bind指向this
虽然上面说的并没有错,但是这么多种情况,在实际应用中也不一定能够准确的对应上,我们不如来学一下this的原理,这样遇到问题也可以自己分析。
函数返回值的影响因素
- 函数调用时传入的参数(非箭头函数)
- 函数定义时的所在的作用域(箭头函数)
在接下来的实例讲解中,我将函数分为 箭头函数
与非箭头函数
。在非箭头函数
中,this是作为一个隐式的参数传入,而在箭头函数
中,this与其他变量一视同仁,不会被特殊处理。
通过例子学知识
非箭头函数
例子1:
function a(){
var name='ming';
var age=13;
console.log(this.name,age);
}
var name='ye';
var age=15;
a(); //输出的结果是ye 13
我们来分析一下:
a()作为一个普通的函数,
this
是作为一个隐式的参数传入到函数参数中。它完整的写法应该是window.a.call(window)
;由于我们没有显式的调用call
来指定this
的指向,那么JS就会帮我们完成这一步,让调用该函数的对象作为this
。所以this
在这里指向的是window
; 而函数的参数是在调用时候才确定的,所以this.name
输出的是ye;但是对于age,他在函数里面只是一个普通的变量,加上作用域链的查找规则,会使用离他最近的变量。与会这里的age
会输出13。
例子2:
var name='outter';
let obj={
name:'jack',
say(){
console.log(this.name)
};
};
(1) obj.say(); //输出的结果为'jack'
let c=obj.say
(2) c() //这里输出的是outter;
我们先来看一下第一个输出结果。 通过对象
obj
来调用say方法
,这段代码相当于obj.say.call(obj)
。因为不通过call
强行指定this
指向,那么JS会默认帮我们把this
指向调用它的对象,所以输出的结果是jack
。

第二个输出结果我们来看看上面的内存图,
变量C
保存的是say
的地址,也就是说我们调用C的时候,并不经过obj
的影响,那么这种情况也是相当于window.c.call(window)
,所以JS默认情况下会让this
指向调用它的对象,也就是全局对象window
。
注意!!!
注意!!!
注意!!!
这里有一个需要注意的地方,当var name='outter'
改为let name='outter'
的时候,情况却变了。
let name='outter'
let obj={
name:'jack',
say(){
console.log(this.name)
}
}
let c=obj.say
c() //输出的结果是undefined
这是因为let
声明的变量并没有成为window
的变量,而var
声明的变量成为了window
的变量,所以this指向的对象window
并没有name
这个属性
例子3
var obj1={
name:'pony',
say(num=''){
console.log(this.name+num)
}
}
var obj2={
name:'jack'
}
(1) obj1.say() //输出pony
(2) obj1.say.call(obj2,2) //输出jack2
(3) obj1.say.apply(obj2,[2]) //输出jack2
call
与apply
的最大区别就是apply
传入参数需要放在一个数组里面。 这里通过call
显式的指定this
的指向,于是这里就把obj2
作为say
的调用对象了。那么你可能会有疑问,为什么我的say
写在obj1
里面,用call
就可以改变指向呢。这个只是恰好say
写在了obj1
里面而已。
例子4
var name='one'
let a=function f1(){
console.log(this.name)
}
function f1(){
var name='inner'
a()
}
f1() //输出的结果为one,而不是inner
箭头函数
箭头函数不绑定this,他会捕捉定义的位置或者作用域链上最近的this,作为自己的this。所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。
注意,{}并不会形成一个作用域,在JS中只有全局作用域与函数生成的局部作用域。
例子1
var i=10;
var a={
i:20,
b:()=>{
console.log(this.i)
},
c:function(){
console.log(this.i)
}
}
a.b() //输出10
a.b.call(a) //输出10
a.c() //输出20
因为
{}
并不会形成作用域,箭头函数沿着作用域链向上查找,在全局作用域找到了i
,于是返回全局作用域的i
,由于箭头函数并没有绑定this
,所以使用call
指定也是没有办法改变this
,因为它压根没有this
。而对于a.c(),作为非箭头函数,上面的例子已经解析了。
var i=10
function f1(){
i=12;
var f=()=>{
console.log(this.i)
}
f()
}
f1() //输出12
因为函数会形成局部作用域,箭头函数查找的时候,在
f1
的作用域就能找到i
了。
以上为个人学习整理的内容,全部的例子都是经过个人检验的,欢迎一起交流学习。
参考文献
转载自:https://juejin.cn/post/6854573211258978312