2018W23 学习笔记

这周因为朋友结婚帮忙的缘故,学习的内容明显下降了不少。所以,等明天开始,得需要恢复之前每天学习的强度。

本周的学习共分为3块:

  1. ES6基础学习与深入;
  2. Vue源码阅读;
  3. 前端进阶班的预习;
    下面我们按照上面模块一起去看一下本周我到底获得了哪些收货。

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个比较“冷门的问题”:

  1. ES6参数默认值是如何影响argument对象;
  2. 参数默认值的暂时性死区;

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;
  }

  1. 虽然该函数能处理多个函数,但是在函数的表现上则完全看不出;
  2. 函数的第一个参数是选中的目标对象,从第二个开始才是需要复制的属性。

因此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不同,我们可以单独对剩余函数进行处理。当然剩余函数也有剩余的不足,主要有以下两点:

  1. 剩余参数必须要放在最后面;
  2. 剩余参数放在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函数的一些不足,竟然和传统的函数有很多不一样的特点:

  1. 没有this,super,arguments和new.target绑定;
  2. 箭头函数不能作为构造函数,也就是说不能被new,也没有prototype属性;
  3. 不能更改this的指向(js深恶痛绝的this指向问题);
  4. 不能重复相同的参数命名。

这些特点是为了减少箭头函数内部的错误与不确定性,为了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添加属性

  1. 遍历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
            });
        }Ï

  1. 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 
        

click

// 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最基本的语法;

  • 调用一个生成器函数会立即执行函数体里面的语句么?

  • 发布/订阅模式和观察者的区别是什么?


梳理完本周学习的内容,发现有很多都已经忘了,不多说了,需要赶紧去复习。

你可能感兴趣的:(2018W23 学习笔记)