【进阶3-2期】JavaScript深入之重新认识箭头函数的this

发布时间:2019-08-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了【进阶3-2期】JavaScript深入之重新认识箭头函数的this脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

我们知道this绑定规则一共有5种情况:

其实大部分情况下可以用一句话来概括,this总是指向调用该函数的对象

但是对于箭头函数并不是这样,是根据外层(函数或者全局)作用域(词法作用域)来决定this

对于箭头函数的this总结如下:

  1. 箭头函数不绑定this,箭头函数中的this相当于普通变量
  2. 箭头函数的this寻值行为与普通变量相同,在作用域中逐级寻找。
  3. 箭头函数的this无法通过bind,call,apply来直接修改(可以间接修改)。
  4. 改变作用域中this的指向可以改变箭头函数的this。
  5. eg. function closure(){()=>{//code }},在此例中,我们通过改变封包环境closure.bind(another)(),来改变箭头函数this的指向。

题目1

/**
 * 非严格模式
 */

@H_512_57@var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: 'person2' }

person1.show1()
person1.show1.call(person2)

person1.show2()
person1.show2.call(person2)

person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()

person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()




正确答案如下:

person1.show1() // person1,隐式绑定,this指向调用者 person1 
person1.show1.call(person2) // person2,显式绑定,this指向 person2

person1.show2() // window,箭头函数绑定,this指向外层作用域,即全局作用域
person1.show2.call(person2) // window,箭头函数绑定,this指向外层作用域,即全局作用域

person1.show3()() // window,默认绑定,这是一个高阶函数,调用者是window
                  // 类似于`var func = person1.show3()` 执行`func()`
person1.show3().call(person2) // person2,显式绑定,this指向 person2
person1.show3.call(person2)() // window,默认绑定,调用者是window

person1.show4()() // person1,箭头函数绑定,this指向外层作用域,即person1函数作用域
person1.show4().call(person2) // person1,箭头函数绑定,
                              // this指向外层作用域,即person1函数作用域
person1.show4.call(person2)() // person2

最后一个person1.show4.call(person2)()有点复杂,我们来一层一层的剥开。

  • 1、首先是var func1 = person1.show4.call(person2),这是显式绑定,调用者是person2show4函数指向的是person2
  • 2、然后是func1(),箭头函数绑定,this指向外层作用域,即person2函数作用域

首先要说明的是,箭头函数绑定中,this指向外层作用域,并不一定是第一层,也不一定是第二层。

因为没有自身的this,所以只能根据作用域链往上层查找,直到找到一个绑定了this的函数作用域,并指向调用该普通函数的对象。

题目2

这次通过构造函数来创建一个对象,并执行相同的4个show方法。

/**
 * 非严格模式
 */

var name = 'window'

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1()
personA.show1.call(personB)

personA.show2()
personA.show2.call(personB)

personA.show3()()
personA.show3().call(personB)
personA.show3.call(personB)()

personA.show4()()
personA.show4().call(personB)
personA.show4.call(personB)()




正确答案如下:

personA.show1() // personA,隐式绑定,调用者是 personA
personA.show1.call(personB) // personB,显式绑定,调用者是 personB

personA.show2() // personA,首先personA是new绑定,产生了新的构造函数作用域,
                // 然后是箭头函数绑定,this指向外层作用域,即personA函数作用域
personA.show2.call(personB) // personA,同上

personA.show3()() // window,默认绑定,调用者是window
personA.show3().call(personB) // personB,显式绑定,调用者是personB
personA.show3.call(personB)() // window,默认绑定,调用者是window

personA.show4()() // personA,箭头函数绑定,this指向外层作用域,即personA函数作用域
personA.show4().call(personB) // personA,箭头函数绑定,call并没有改变外层作用域,
                              // this指向外层作用域,即personA函数作用域
personA.show4.call(personB)() // personB,解析同题目1,最后是箭头函数绑定,
                              // this指向外层作用域,即改变后的person2函数作用域

题目一和题目二的区别在于题目二使用了new操作符。

使用 new 操作符调用构造函数,实际上会经历一下4个步骤:

  1. 创建一个新对象;
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
  3. 执行构造函数中的代码(为这个新对象添加属性);
  4. 返回新对象。

上期思考题解

依次给出console.log输出的数值。

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3;
        (function() {
            console.log(this.num);
            this.num = 4;
        })();
        console.log(this.num);
    },
    sub: function() {
        console.log(this.num)
    }
}
myObject.add();
console.log(myObject.num);
console.log(num);
var sub = myObject.sub;
sub();

答案有两种情况,分为严格模式和非严格模式。

  • 严格模式下,报错。TypeError: Cannot read PRoperty 'num' of undefined
  • 非严格模式下,输出:1、3、3、4、4

解答过程:

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3; // 隐式绑定 修改 myObject.num = 3
        (function() {
            console.log(this.num); // 默认绑定 输出 1
            this.num = 4; // 默认绑定 修改 window.num = 4
        })();
        console.log(this.num); // 隐式绑定 输出 3
    },
    sub: function() {
        console.log(this.num) // 因为丢失了隐式绑定的myObject,所以使用默认绑定 输出 4
    }
}
myObject.add(); // 1 3
console.log(myObject.num); // 3
console.log(num); // 4
var sub = myObject.sub;//  丢失了隐式绑定的myObject
sub(); // 4

内容来自评论区:【进阶3-1期】JavaScript深入之史上最全--5种this绑定全面解析

本期思考题

分别给出console.log输出的内容。

var obj = {
    say: function () {
        function _say() {
            console.log(this);
        }
        console.log(obj);
        return _say.bind(obj);
    }()
}
obj.say()

参考

从这两套题,重新认识JS的this、作用域、闭包、对象

进阶系列目录

  • 【进阶1期】 调用堆栈
  • 【进阶2期】 作用域闭包
  • 【进阶3期】 this全面解析
  • 【进阶4期】 深浅拷贝原理
  • 【进阶5期】 原型Prototype
  • 【进阶6期】 高阶函数
  • 【进阶7期】 事件机制
  • 【进阶8期】 Event Loop原理
  • 【进阶9期】 Promise原理
  • 【进阶10期】Async/Await原理
  • 【进阶11期】抖/节流原理
  • 【进阶12期】模块化详解
  • 【进阶13期】ES6重难点
  • 【进阶14期】计算机网络概述
  • 【进阶15期】浏览器渲染原理
  • 【进阶16期】webpack配置
  • 【进阶17期】webpack原理
  • 【进阶18期】前端监控
  • 【进阶19期】跨域和安全
  • 【进阶20期】性能优化
  • 【进阶21期】VirtualDom原理
  • 【进阶22期】Diff算法
  • 【进阶23期】MVVM双向绑定
  • 【进阶24期】Vuex原理
  • 【进阶25期】redux原理
  • 【进阶26期】路由原理
  • 【进阶27期】VueRouter码解析
  • 【进阶28期】ReactRouter源码解析

交流

进阶系列文章汇总:https://github.com/yygmind/blog,内有优质前端资料,欢迎领取,觉得不错点个star。

我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!

【进阶3-2期】JavaScript深入之重新认识箭头函数的this

脚本宝典总结

以上是脚本宝典为你收集整理的【进阶3-2期】JavaScript深入之重新认识箭头函数的this全部内容,希望文章能够帮你解决【进阶3-2期】JavaScript深入之重新认识箭头函数的this所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。