这周因为朋友结婚帮忙的缘故,学习的内容明显下降了不少。所以,等明天开始,得需要恢复之前每天学习的强度。
本周的学习共分为3块:
- ES6基础学习与深入;
- Vue源码阅读;
- 前端进阶班的预习;
下面我们按照上面模块一起去看一下本周我到底获得了哪些收货。
1.ES6学习
阅读完成《深入理解ES6》的第三章。第三章主要讲述的是函数的ES6新特性。
我们都知道ES6是ES5的加强与补充,是为了解决ES5中某些遗留问题而提出的新的解决方案或者加强,所以顺着这个思路,我就去做了这样的笔记。
1.1 ES6函数新特性
在ES6中,针对函数,补充了如下的一些特性:
- 参数默认值
- 使用不具名函数
- 函数构造器增强
- 扩展运算符
- 函数的双重用途
- 块级函数
- 箭头函数
- 尾调用优化
1.1.1 参数默认值
传统的ES5设置函数默认值需要逐个对参数进行设置,所以代码量非常多,而且对于JS这种不限制函数参数的行为其扩展性也很差。所以ES6在ES5的基础上增加了参数默认值的特效,其语法如下:
function demo(url, timeout = 2000 , fn = function()
{console.log('do it')
})
值得注意的是,我们可以向参数传递Undefined值来使用默认的参数值,以上面函数为例,
demo('http://www.baidu.com',undefined,fn)
这样就可以继续使用第二个参数的默认值了。
初次之外,还涉及到了2个比较“冷门的问题”:
- ES6参数默认值是如何影响argument对象;
- 参数默认值的暂时性死区;
1.1.2 使用不具名函数
ES5的不具名的arguments对象在实际使用过程中会有很多的限制。在书中,就通过underscorejs库中Pick函数的讲解来说出arguments的不足。
// 复制特定对象的特定属性
function pick(object) {
let result = Object.create(null);
// 从第二个参数开始处理
for (let i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] = object[arguments[i]];
}
return result;
}
- 虽然该函数能处理多个函数,但是在函数的表现上则完全看不出;
- 函数的第一个参数是选中的目标对象,从第二个开始才是需要复制的属性。
因此ES6引入了剩余参数这个概念,使用剩余函数对上述函数进行改写,则清晰和优雅不少。
function pick(object, ...keys) {
let result = Object.create(null);
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
通过引入剩余参数,我们就可以知道函数可以处理多余的函数。并且这与包含所有参数的arguements不同,我们可以单独对剩余函数进行处理。当然剩余函数也有剩余的不足,主要有以下两点:
- 剩余参数必须要放在最后面;
- 剩余参数放在setter函数中不可用,会报语法错误。
1.1.3 函数构造器
作者声称,函数默认值加上剩余函数加强了函数构造器的能力,但是,在实际工作中,笔者觉得函数构造器的用处并不多。
1.2.4 扩展运算符
剩余参数是将多个参数添加进数组,而扩展运算符则是将数组的元素分成单个的元素,类似于split的功能。它主要的目的是代替apply。其使用场景如下:
let values = [1,8,3];
// ES5版本
Math.max.apply(Math, values);
// ES6版本
Math.max(...values);
在之前的ES5版本中,要实现求一个数组的最大数,需要使用apply去更改上下文的执行环境,而有了扩展运算符之后,则轻松很多,并且从语义化角度上来说,也清晰不少。
1.1.4 函数的双重用途
在ES5中,函数是否调用new操作符则意味着不同的用途。没有new操作符,它就是执行函数体内的代码。而有了new,函数就会执行内部[[ constructor]]方法,构建一个对象。所以这就是函数的双重用途的意思。
在ES5中,判断函数是否调用了new操作,最简单也是最流行的一种做法就是使用instanceof这个方法,但是,这个方法也有明显的缺陷,开发者可以使用apply去改变函数执行的上下文顺序,从而使instanceof的执行判断出错。
function Person(name) {
if (this instanceof Person) {
this.name = name; // 使用 new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // 奏效了!
因此在ES6中引入了new.target来弥补相关不足。
function Person(name){
if(typeof new.target != undefined){
this.name =name;
}else{
throw new Error('must with new');
}
}
其底层原理是,在调用new 操作符时,调用[[ constructor ]]方法,new.target才会生成,(否则为undefined)并且将this对象赋值为原构造函数。
1.1.5 ES6箭头函数
之前也学习过ES6箭头函数,但是没有想到的是,ES6函数为了增强ES5函数的一些不足,竟然和传统的函数有很多不一样的特点:
- 没有this,super,arguments和new.target绑定;
- 箭头函数不能作为构造函数,也就是说不能被new,也没有prototype属性;
- 不能更改this的指向(js深恶痛绝的this指向问题);
- 不能重复相同的参数命名。
这些特点是为了减少箭头函数内部的错误与不确定性,为了js函数更好的运行。
2. Vue源码学习
主要是看了第500-900行的代码,粗略地知道了Vue对数组的监听。
首先获取数组的原型对象,在这个对象上有数组的方法。
var arrProto = Array.prototype;
然后使用Object.create的方法创建了一个空对象,其中它的原型链上有数组方法的集合。
var arrMethods = Object.create(arrProto);
创建一个数组方法的集合:
var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
最关键的一步:对arrMethods进行构建:
给arrMethods添加属性
- 遍历methodsToPatch,在回调函数中调用def函数给arrMethods添加方法相对应的属性
methodsToPatch.forEach(method => {
def(arrMethods, method, function mutator() {
// 相关代码第二步进行解析
})
});
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}Ï
- muator函数的定义
function mutator() {
//定义args容器,并且获取函数参数的长度
var args = [],
len = arguments.length;
while (len--) args[len] = arguments[len]; //将类数组转换成数组
//这里的this指的就是arrMethods 调用arrMethods中的方法和参数
var result = original.apply(this, args);
//监听类 下面代码就调用了它的notify和observeArray方法
var ob = this.__ob__;
var inserted;
//如果是push/unshift/splice方法 对inserted进行赋值
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.splice(2);
break;
}
if (inserted) {
ob.observeArray(inserted); //对传入的参数进行监听
}
ob.deep.notify(); //通知视图发生变化
return result;
};
3.课程进阶学习
这里总结性的语言就不写了,抛出问题,测试一下是否能回答出:
-
有哪3种为DOM元素注册事件处理函数的方式?分别有什么限制和优点?
-
Dom Event接口中哪些属性和接口需要了解?
-
如何自定义一个Event事件?请写出一个demo;
-
JS事件的传播是如何传播的?
-
简述下面代码的执行顺序
// html
<div id='outter'>
<p id='inner'> click </p>
</div>
// js
let outter = document.getElementById('outter');
let inner = document.getElementById('inner');
outter.addEventListener('click', function () {
console.log('outter-true')
}, true);
inner.addEventListener('click', function () {
console.log('inner-true')
}, true);
inner.addEventListener('click', function () {
console.log('inner-false')
}, false);
outter.addEventListener('click', function () {
console.log('outter-false')
}, false);
-
请解释一下什么是JS事件代理?
-
请简单地写一下事件代理的demo;
-
可以使用哪个事件来阻止事件的传播?
-
util.callbackify这个API的作用是什么?
-
回调函数中的第一个参数和第二个参数分别是什么?
-
请写出API的具体使用方法:将一个异步函数转换为错误回调优先的函数
-
回调函数中的
null
有什么意义么? -
回调函数中的错误原因可以通过什么属性获取?
-
generator对象是什么?怎样才能获取到?
-
请写出generator最基本的语法;
-
调用一个生成器函数会立即执行函数体里面的语句么?
-
发布/订阅模式和观察者的区别是什么?
梳理完本周学习的内容,发现有很多都已经忘了,不多说了,需要赶紧去复习。
网友评论