ES6特性:箭头函数转换,闭包

箭头函数转换

箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

// ES6写法
var handler = {
        id: "123456",
        init: function () {
          document.addEventListener(
            "click",
            (event) => this.doSomething(event.type),
            false
          );
        },
        doSomething: function (type) {
          console.log(type);
          console.log("Handling " + type + " for " + this.id);
        },
      };
      handler.init();

      ES6 允许使用“箭头”(=>)定义函数:   类似于JDK中的Lambda表达式,箭头函数如果在代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。反之可以不使用return语句,默认添加。

总结下来如下:

  • 参数类型
    • 无参数 () => {…}
    • 一个参数 (a) => {…}
    • 多个参数 (a,b) => {…}
  • 返回值类型
    • 单行表达式 默认自带 (a) => a+1;
    • 多条语句 大括号括起 (a) => { … return …},有return则return

通过以上规则可以改写回ES5格式

// 改写成ES5
var handler = {
        id: "123456",
        init: function () {
          document.addEventListener(
            "click",
            function(event) {
                console.log(this)
                this.doSomething(event.type)
            },
            false
          );
        },
        doSomething: function (type) {
          console.log(type);
          console.log("Handling " + type + " for " + this.id);
        },
      };
      handler.init();

为了检验一下,这里先执行ES6写法后的效果,监听鼠标点击事件

打印输出

// ES6执行结果
click
Handling click for 123456
click
Handling click for 123456
click
Handling click for 123456
click
Handling click for 123456

可以看到在调用handler.init()后,执行到init方法中的第二个方法参数,其中通过this调用了handler的另一个成员doSomething方法,从结果可以看出,这个this指向的是handler本身;

从开头的引用结论完全可以看出事情没有这么简单,**箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。**既然ES6的箭头函数有这种特性,那么普通函数呢?

这里我思考了一下,结合开发时碰到的场景来看,普通函数的this的指代者肯定不会是固定的,果不其然,第一次改写的ES5格式js执行结果如下

VM251:7 Uncaught TypeError: this.doSomething is not a function
    at HTMLDocument.<anonymous> (<anonymous>:7:22)

提示this.doSomething不是方法,所以this指代肯定有误。从以往的经验来看,这个this肯定指向document对象。

JS执行队列

结合上节课定时器改变函数执行队列的问题,简单的学习了一下。JS是单线程的一个时间只能做一件事,为了解决这个问题,JS包含了同步和异步的JS

同步任务都在主线上执行,形成一个执行线
异步任务通过回调函数实现,异步任务有以下三种类型
1. 普通事件,如click,resize
2. 资源加载,如load
3. 定时器

在回调函数中执行说明:先执行 执行线 中的同步任务。异步任务放到任务队列中。一旦执行线中的同步任务执行完,按照次序执行任务队列中的异步任务。

所以说,定时器和click监听事件回调函数都会改变函数的执行队列。那么我们要做的就是将调用doSomething方法的对象改成handler即可!

// 最终版
var handler = {
        id: "123456",
        init: function () {
          var self = this;
          document.addEventListener(
            "click",
            function(event) {
                console.log(self)
                self.doSomething(event.type)
            },
            false
          );
        },
        doSomething: function (type) {
          console.log(type);
          console.log("Handling " + type + " for " + this.id);
        },
      };
      handler.init();

最后输出结果和箭头函数一致!

上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。这也解释了前面箭头函数的特性。

总结

  1. 箭头函数内部没有构造方法,也没有prototype,不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
  2. 对this的处理也与和普通函数不一样,普通函数this指向window对象或调用者;而箭头函数的 this 始终指向函数定义时的 this即定义时所在的对象,而在没有执行的时候,箭头函数的this指向也是固定的
  3. 在以后的实际场景中,可以根据ES6箭头函数的this 始终指向this定义时所在的对象 这个特性来灵活的解决一些问题,既可以像Lambda表达式一下简化代码,又可以运用到指代问题上

闭包

闭包

闭包是指有权访问另一个函数作用域中的变量的函数。主要有以下三个特性:
①函数嵌套函数
②函数内部可以引用函数外部的参数和变量
③被引用的参数和变量不会被垃圾回收机制回收

    初步来看,我学习并理解的是,在函数嵌套的前提下,函数内部引用函数外部的参数和变量,不会因为内部函数执行完毕销毁对其的引用。

   因此可以做到在内存中维持一个变量做缓存的作用,与此同时,同样变量过多也会造成内存泄漏,需要失去引用价值时赋值为null。

看以下闭包的实例:

function fun(n, o) {
    console.log(o);
    return {
        fun: function (m) {
            return fun(m, n)
        }
    };
}

var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);

var b = fun(0).fun(1).fun(2).fun(3);

var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

推测结果:

a

var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
  1. fun(0)的结果:因为只传了一个参数n,首先打印undefined,返回值为一个对象类型,则a即为return的这个包含一个成员为方法类型fun的对象类型,由于闭包的特性,n不会被销毁,所以返回值为{fun : function (m) {return fun(m, 0)}}

  2. a.fun(1),获取a的成员,传入参数1,结果为fun(1, 0),执行方法结果打印o即0,返回一个成员为方法类型fun的对象类型{fun : function (m) {return fun(m, 1)}}

  3. 得出推测,以此类推,后面a.fun(n)结果都为打印0

最后输出

undefined
0
0
0

b

var b = fun(0).fun(1).fun(2).fun(3);
  1. 首先fun(0)的结果是打印undefined,返回值为包含一个成员为方法类型fun的对象类型 {fun : function (m) {return fun(m, 0)}}
  2. fun(0).fun(1)的结果即fun(1,0),打印0,返回包含一个成员为方法类型fun的对象类型 {fun : function (m) {return fun(m, 1)}}
  3. fun(0).fun(1).fun(2)结果即为2中结果带入参数,结果为fun(2,1),执行后结果为 打印1,同理返回包含一个成员为方法类型fun的对象类型 {fun : function (m) {return fun(m, 2)}}
  4. fun(0).fun(1).fun(2).fun(3)结果即为3中结果带入参数,结果为fun(3, 2),执行后打印2,同理返回包含一个成员为方法类型fun的对象类型 {fun : function (m) {return fun(m, 2)}}

最后输出

undefined
0
1
2

c

var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

由前面的规律可以看出,函数嵌套时,由于闭包引入的n和m,对于内部函数来说是外部引入的参数和变量,不会被垃圾回收机制回收,一直缓存在内存中。只要函数嵌套,值一直发生改变。

所以函数嵌套到fun(0).fun(1)不再嵌套时,闭包特性也没有了。fun(0).fun(1)结果即fun(1,0),打印0,返回包含一个成员为方法类型fun的对象类型 {fun : function (m) {return fun(m, 1)}},后面第二个参数o不会改变,c.fun(2)和c.fun(3)结果都为打印1

undefined
0
1
1

最后实际结果与预测结果一致!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值