箭头函数转换
箭头函数体内的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,所以也就不能用作构造函数。这也解释了前面箭头函数的特性。
总结
- 箭头函数内部没有构造方法,也没有prototype,不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
- 对this的处理也与和普通函数不一样,普通函数this指向window对象或调用者;而箭头函数的 this 始终指向函数定义时的 this即定义时所在的对象,而在没有执行的时候,箭头函数的this指向也是固定的
- 在以后的实际场景中,可以根据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);
-
fun(0)的结果:因为只传了一个参数n,首先打印undefined,返回值为一个对象类型,则a即为return的这个包含一个成员为方法类型fun的对象类型,由于闭包的特性,n不会被销毁,所以返回值为
{fun : function (m) {return fun(m, 0)}}
-
a.fun(1),获取a的成员,传入参数1,结果为fun(1, 0),执行方法结果打印o即0,返回一个成员为方法类型fun的对象类型
{fun : function (m) {return fun(m, 1)}}
-
得出推测,以此类推,后面a.fun(n)结果都为打印0
最后输出
undefined
0
0
0
b
var b = fun(0).fun(1).fun(2).fun(3);
- 首先fun(0)的结果是打印undefined,返回值为包含一个成员为方法类型fun的对象类型
{fun : function (m) {return fun(m, 0)}}
- fun(0).fun(1)的结果即fun(1,0),打印0,返回包含一个成员为方法类型fun的对象类型
{fun : function (m) {return fun(m, 1)}}
- fun(0).fun(1).fun(2)结果即为2中结果带入参数,结果为fun(2,1),执行后结果为 打印1,同理返回包含一个成员为方法类型fun的对象类型
{fun : function (m) {return fun(m, 2)}}
- 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
最后实际结果与预测结果一致!