昨日,发了一篇公众号文章:前端
原文连接:别低估本身,但,这道题,真的有点难git
在部分群里引发了一些讨论,其中有一点是关于箭头函数的 this 指针的问题。使用了阮一峰《ES6 入门》文章的内容来反驳。https://github.com/ruanyf/es6tutorial/blob/gh-pages/docs/function.md 为了隐私,屏蔽掉了微信昵称:es6
上述截图,来自阮一峰的《ECMAScript 6 入门》:github
下面咱们就来看看箭头函数的 this 究竟是啥样的!web
起初,群里一个朋友抛出了这个疑问,为啥这两个输出有差别。微信
一个是空 person,一个是有值的 person 呢?闭包
那么咱们首先就来分析一下究竟是什么缘由。app
let pp = new person("251");编辑器
这里建立了一个 person 实例 pp函数
而后 pp.getval(),直接中了以前文章提到的 [谁调用,指向谁] 逻辑,pp 实例调用了 getval,因此 getval 的 this 指向 pp 实例,打印出 pp 实例内容。
这个看起来没有人有疑问,一切都很美好。
let pp = new person("251");
这里也建立了一个 pp 实例
在 pp.getval() 的时候,箭头函数的 this 指向谁呢?
参考文章 别低估本身,但,这道题,真的有点难
箭头函数:父级指向谁,当前箭头函数就指向谁。
这里 getval 的箭头函数的父级是谁呢?
是 o 对象
可是 o 对象不是一个函数做用域,没有 this,因此继续往上查找,而后找到了 person 函数,因此 getval 的 this 与 person 函数 this 一致的。
那么 person 函数的 this 指向谁呢?
咱们增长点 log 来增强理解:
var flag=996;
function person(fg){
let o = new Object();
o.flag = fg;
o.getval=()=>{
console.log(this);
}
this.a = 1;
console.log("^^^^^^");
console.log(this);
console.log("^^^^^^");
return o;
}
var pp = new person("251")
pp.getval();
console.log("&&&&&&&");
console.log(pp);
console.log("&&&&&&&");
// 输出结果以下:
^^^^^^
person {a: 1}
^^^^^^
person {a: 1}
&&&&&&&
{flag: "251", getval: ƒ}
&&&&&&&
复制代码
这里 getval 函数是箭头函数,咱们知道,始终与父级的 person 的 this 保持一致,这里 person 的 this 设置了 a = 1,因此只打印了 {a:1}。
而 person 函数里 return o,是函数的返回值,这里实际上被 new 命令返回 o 给 pp 实例了。
咱们看到输出 pp 实例,是 {flag: "251", getval: ƒ}。
那 person 的 this 与 pp 实例的 this 有啥区别呢?
这里的关键知识点是:new 操做符
因为 person 函数返回的是一个对象(null 除外),因此在 new 的时候返回的是 person 函数返回的 o 对象,并无返回 person 函数的 this 给实例对象。
这里若是 person 函数返回的是一个 [数字、字符串、布尔等],那么 new 的时候,会忽略返回值,而是仍然会返回 person 的 this 给实例对象。
这也是为啥这里输出的 pp 实例不包含 person 函数内定义的 this.a。
而箭头函数的 this 指向 person 的 this ,输出了 this.a=1 可是确不包含 o 对象。
总结:这里箭头函数的 this 永远指向的是父级的 person 的 this,而不是这里的 pp 实例
例子:
var flag=996;
function person(fg){
let o = new Object();
o.flag = fg;
o.getval=()=>{
console.log(this);
}
this.a = 1;
return true;
}
var pp = new person("251");
console.log(pp.a);// 1
复制代码
这里在 new person 的时候,person 构造函数返回的不是对象,因此直接忽略。
两个要点:
这实际上是“表象”,其实是由于箭头函数的 this 是指向父级的 this,由于箭头函数自身没有 this,因此没法修改自身的 this,从而言之 “忽略”。
箭头函数的 this 是从当前箭头函数逐级向上查找 this,若是找到了,则做为本身的 this 指向,若是没有则继续向上查找。而父级的 this 是可变的,因此箭头函数的 this 也可跟随父级而改变。
所以,想修改箭头函数“自己”的 this 是作不到的。
可是能够采用变动父级的 this 来达到变动子箭头函数的 this。
function outer(){
var inner = function(){
var obj = {};
obj.getVal=()=>{
console.log("*******");
console.log(this);
console.log("*******");
}
return obj;
};
return inner;
}
outer()().getVal();
// 输出以下
*******
Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
*******
复制代码
getVal 函数是箭头函数,方法里面的 this 是跟着父级的 this。
在 outer() 执行后,返回闭包函数 inner
而后执行闭包函数 inner,而闭包函数的 inner 仍然遵循 [谁调用,指向谁],这里没有直接调用对象,而是最外层的“省略的” window 调用的,因此 inner 的 this 是指向 window 的。
闭包函数的做用域与父级的函数做用域是一致的,咱们能够理解为闭包函数做用域已经跳出父函数了,可是还能够直接访问父函数内的变量参数等(这就是闭包的强大之处了)。
这里的 inner 实际上与 outer 的做用域一致。
咱们可使用 Bind 改变父级 inner 函数的 this,来达到改变子箭头函数 getVal 的 this 指向。
例子:
function outer(){
var inner = function(){
var obj = {};
obj.getVal=()=>{
console.log("*******");
console.log(this);
console.log("*******");
}
return obj;
};
return inner;
}
outer().bind(outer)().getVal();
//输出以下
*******
ƒ outer(){
var inner = function(){
var obj = {};
obj.getVal=()=>{
console.log("*******");
console.log(this);
console.log("*******");
}
…
*******
复制代码
执行 outer 方法,返回 inner 函数.
而后咱们改变 inner 的 this 指针,使用 bind 将 inner 的 this 指向到 outer。
而后执行方法,咱们看到,输出的 this 是 outer 函数。这里咱们成功改变了 getVal 的 this 指向。
箭头函数的 this 已经随同父级元素的 this 的改变而改变。
咱们这里拷贝原文以下(开始有截图内容,是同样的):
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
上面四点中,第一点尤为值得注意。this对象的指向是可变的,可是在箭头函数中,它是固定的。
对此有几点疑虑:
上面例子定义时所在的对象是 inner,可是执行的时候,this 指向了 outer
上面例子定义时所在的对象是 inner,可是执行时 this 已经指向 outer,已经被改变。
结论:箭头函数的 this 并非固定的,而是牢牢跟随父级的 this 指针,若是父级 this 改变,那么子箭头函数 this 也会跟着改变。(根本缘由是箭头函数没有 this,而是在运行时使用父级的 this)。
若是《ES6 入门》中应该理解为箭头函数 this 的指向是固定的(词法做用域),都是指向父级函数,改变的只是父级函数的 this?
那感受也能够理解为 this 都是不可变的,【谁调用,指向谁】,是调用者在变化?这样和文中也有冲突,文中提到普通函数的 this 是变化的。
若有疏漏,欢迎指正~。加我我的微信号交流:lqyygyss
欢迎关注个人微信公众号,一块儿作靠谱前端!