【个人总结】es6箭头函数以及es5的this指向问题

以下js比较示例,引用“小小小小小亮”同学

点击我,跳转至他的分析结果


一、比较并分析下例代码的this指向问题

var a=11
function test1(){
  this.a=22;
  let b=function(){
    console.log(this.a);
  };
  b();
}
var x=new test1();
输出11
var a=11;
function test2(){
  this.a=22;
  let b=()=>{console.log(this.a)}
  b();
}
var x=new test2();
//输出22

二、解析方向

这里往往有三种反应

  1. 跟我想的不一样,这两个结果应该反过来
  2. 前者了解,但第二个不懂
  3. 前者不了解,后者我懂

1)1与3的反应

1与3的反应一般都是this指向的不熟悉以及相关业务的实践量缺少

1)2的反应

2的反应,应该是大多数人的反应,前者是解释的通的,单纯定义一个方法,并未被父级对象所引用(姑且这么说吧),则this指向的是全局,否,则指向父级对象

var a = function () {
    console.log(this.test)
}
var b = {
    test: 666,
    a: a // a挂在了对象上(被引用)
    // 等价于 a: function () {...}
}
b.a()
// 输出666
var a = function () {
    console.log(this.test)
}
var b = function () {
    this.test = 666
}
b.prototype.a = a // 挂在了原型链上(被引用)
var k = new b()
k.a()
// 输出666
var a = {
    test: 666,
    b: function () {
        var c = function () { // 未被引用
            console.log(this.test)
        }
        c()
    }
}
a.b()
// 输出undefined

但第二种是百思不得理解的,因缺少混合模式或构造函数模式的开发经验,又是封装方法依赖注入,所以大部分的避免了作用域中去定义函数调用。

随着该问题的暴露,趁着这次,来个实践出真理

var test = 777
var a = function () {
     // 不实例化的话,这里定义this是没有意义的
}
a.prototype.b = function () {
    this.test = 666
    let k = () => {
        console.log(this.test)
    }
    k()
}
a.prototype.b()
// 输出666

很神奇是不是,这可能关系到=>函数的解析机制,暂且分析为:

如果=>函数定义在prototype下的作用域中时,this会指向prototype上所引用的父级对象

实践下

var test = 777
var a = function () {}
a.prototype.b = function () {
    this.test = 666
    let k = () => {
        console.log(this.test)
    }
    k()
}
a.prototype.b.prototype.c = function () {
    this.test = 555
    let k = () => {
        console.log(this.test)
    }
    k()
}
a.prototype.b() // 输出666
a.prototype.b.prototype.c() // 输出555

这里如果执行a.prototype
会发现除了b定义的方法外,还有一个constructor(构造器)函数的生成,但事实上是没用到这个的(只有实例化的时候),而且delete a.prototype.constructor
删除该函数后,调用的=>对象也依然没影响到this的指向或抛错,所以
有没constructor并不能成为判断=>指向的依据(个人观点)

经检验确实无误,那实例化试试

var test = 777
var a = function () {
    // 实例化一定会经过构造器,则this可定义该处了
    this.test = 666
}
a.prototype.b = function () {
    let k = () => {
        console.log(this.test)
    }
    k()
}
var b = new a()
b.b()
// 输出666

那回过头来看看,为什么没有prototype,直接实例化也能达到this指向局部呢?

var a=11;
function test2(){
  this.a=22;
  let b=()=>{console.log(this.a)}
  b();
}
var x=new test2(); // 关键在此,new里实现了什么
//输出22

那关于new里实现了啥,百度上一搜一大把,这里简单来说,就是内部创建了个new Object()
并且将定义的”test2“这个函数用call或apply将this指向了object并return达到了this 的更变

来简单试试

var a = () => {
    console.log(this.b)
}
var k = {
    b: 666
}
a.call(k)
// 输出 undefined

按道理来说,应该是变的,因为ES5的函数定义更变this指向是成功了,那为啥ES6的=>函数指向没变?

要知道为啥,又得来看看=>函数里发生了什么……(无语)

这里就不提及更深层次的了,大致只要知道,=>函数因为是es6语法,已经被浏览器所解析,所以一用=>函数,就已经被预解析了(你还想在浏览器执行之前搞定?)因此,call或apply与=>有一定耦合性,没准在内部的执行过程中被if过滤掉了。

我们再来试试new Object()的创建方式

var a= {
    test: 666,
    b: function () {
        var k= () => {
            var kk = () => {
                console.log(this.test)
            }
            kk()
        }
        k()
    }
}
a.b()
// 输出666

=>函数在含有父级对象的作用域中定义,this即指向父级对象

好,实践到此为止,结果已经显而易见了。


三、个人结论

1)function:

function定义的子级函数对象必须被父级所引用,是父级对象的一部分,例如:

var a = {
    b: function () { // 一部分
        console.log(this)
    }
}
/* or */
var a = function () {}
a.prototype.b = function () { // 一部分
    console.log(this)
}

this则指向上一级对象,否则this永远指向全局。

1)=>:

=> 函数,如果定义在含有父级对象的作用域下或被实例化了,则this指向上一级对象,否则永远指向全局,例如:

var a = {
    b: function () { // 父级
        return (() => { // 父级对象下的作用域
            console.log(this)
        })()
    }
}
/* or */
var a = function () {}
a.prototype.b = function () { // 父级
    return (() => { // 父级对象下的作用域
        console.log(this)
    })()
}

还有二种说法:

1、有constructor则=>指向上一级,无则指向全局(不是很赞同)
2、有prototype则=>指向上一级,无则指向全局(有缺陷)


关于

make:o︻そ╆OVE▅▅▅▆▇◤(清一色天空)

blog:http://blog.csdn.net/mcky_love

掘金:https://juejin.im/user/59fbe6c66fb9a045186a159a/posts


结束语

找一个问题时,又会发现另外的问题,查找另外的问题时,往往又有无穷无尽的问题滚来,前端,广,还TM深 例如上面的问题,再找下去,估计就跑到浏览器的框架源码去了,所以量力而行,适可而止……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章