JS 高频笔试题——ES6+

218 阅读9分钟

ES6技能点总结

模板字符串

let const

var的问题:

1、会被声明提前(打乱程序正常的执行顺序)
2、没有块级作用域(代码块内的变量会超出代码块范围,影响外部的变量)

使用 let 可以避免以上问题。

let本质:

底层会被翻译为匿名函数自调: (function(){ ... })()

let的三个小脾气:

1、因为不会声明提前,所以不能在声明变量之前,提前使用该变量。
2、在相同作用域内,禁止声明两个同名的变量!
3、因为let底层相当于匿名函数自调,所以,即使在全局创建的let变量,在window中也找不到!

var, let, const区别

区别varletconst
是否产生”块级作用域”×✔️✔️
是否会被声明提前✔️××
是否保存在window中✔️××
相同作用域中能否重复声明变量✔️××
是否能提前使用✔️××
是否必须设置初始值××✔️
能否修改实际保存在变量中的原始类型值或引用类型地址✔️✔️×

箭头函数

for of

普通for循环:

1、优点: 既可遍历索引数组,又可以遍历类数组对象(arguments)——只要下标是数字
2、缺点: 没有可简化的空间

forEach:

1、优点: 可以配合ES6的箭头函数,很简化
2、缺点: 只能遍历数字下标的索引数组,无法遍历类数组对象
for (var 元素值n of 索引数组 / 类数组对象) {
    // of会依次取出数组或类数组对象中每个属性值
    // 自动保存of前的变量中
}

for of的问题:

1、无法获得下标位置i,只能获得元素值
2、无法控制遍历的顺序或步调,只能从头到尾,一个挨一个的顺序遍历
3、无法遍历下标名为自定义下标的对象和关联数组

总结:

1、下标为数字,首选for of
2、下标为自定义字符串,首选for in

参数增强

1、参数默认值(default)

2、剩余参数(rest)

剩余参数(rest)的优点:

1、支持箭头函数
2、生成的数组是纯正的数组类型,所以使用数组家所有函数
3、自定义数组名arr,比arguments简单的多!

只要箭头函数中,不确定实参值个数时,都可用…数组名代替arguments接住所有或剩余实参值。

不能使用箭头函数的地方/箭头函数缺点

1、构造函数不能用
2、对象的方法不能用
3、原型对象方法不能用
4DOM中事件处理函数不能用
5、箭头函数无法用call,apply,bind改变this
6、箭头函数不支持arguments
7、箭头函数没有prototype

3、展开运算符(spread)

1、定义函数时,形参列表中的...,表示收集
2、调用函数时,实参列表中的...,反而表示拆散

语法糖:

1、复制一个数组:[...arr]
2、合并多个数组和元素:[...arr1, ...arr2]
3、克隆一个对象:{...obj}
4、合并多个对象和属性:{...obj1, ...obj2}

解构(destruct)

数组解构

对象解构

class & 继承

class:程序中专门集中保存一种类型的所有子对象的统一属性结构和方法定义的程序结构

定义class:

1、用class{}包裹原构造函数+原型对象方法
2、原构造函数名升级为整个class的名字,所有构造函数 统一更名为"constructor“
3、原型对象中的方法,不用再加prototype前缀,也不用 =function,直接简写为: 方法名(){ ... ...}

直接放在class{}内的方法定义,其实还是保存在原型对象中的。

只要如果希望所有子对象,都可使用一个共同的属性值时,都要用静态属性代替原来的原型对象属性。

class 类型名{ 
    static 共有属性名=属性值
    ... 
    ... 
}

原理:

1、标有static的静态属性, 
2、都是保存在构造函数对象身上。 
3、因为构造函数在程序中不会重复!
4、所以,静态属性,也不会重复!
5、任何时候,任何地点,访问一个类型的静态属性,永远访问的都是同一份!

class只是个外壳,其本质还是普通的构造函数function

class 子类型 extends 父类型{ ... }

inherit是继承的意思
但是,为什么用extends表示继承?
继承是为了更好的扩展,
程序中的继承,都是为了在继承现有成员的基础上,进一步扩展出自己个性化的新成员!
所以,程序中的继承,都用extends(扩展)

为什么指向父类型构造函数的关键字,称为super? 
super在数学中指超集

promise

Promiss对象三大状态:

1、当异步任务执行过程中,整个new Promise()对象处于pending(挂起)状态
2、当异步任务成功执行完,调用成功的开关函数(resolve())时,整个new Promise()对象切换为fulfilled(成功)状态,new Promise()会自动调用.then()执行下一项任务
3、当异步任务执行出错,调用失败的开关(reject())函数时,整个new Promise()对象切换为rejected(出错)状态,new Promise()会自动调用.catch()执行错误处理代码

1pending(挂起)
2fulfilled(成功)
3rejected(失败)

async / await

async和await 其实就是promise中.then()的简写,目的是彻底消除嵌套。

1、只有基于Promise的函数,才支持asyncawait
2await必须用在被async标记的函数内
3、外层函数必须用async标记。目的是告诉主程序,这段函数内的代码整体是异步执行的。不影响主程序的执行。 
4await必须写在前一项任务之前
5await的作用等效于.then(),用来通知程序必须等待前一项任务执行完,才能继续执行后续任务。 
6、一旦使用了await,前一项任务的resolve(返回值),可以像普通函数一样用=接住。后续代码可继续使用该变量里获得的返回值。 
7await和.then()一样,可多次使用。控制多个异步任务顺序执行。

async和await的组合虽然相对于主程序,整体是异步的,但async内部的多个await 是同步顺序执行的。

用try catch组合代替了.catch(function(){ … })函数嵌套调用,可自动接住 promise函数中reject()抛出的异常信息。

手写promise

var PENDING = 0; //挂起状态
var FULFILLED = 1; //执行成功状态
var REJECTED = 2; //执行失败状态

//定义Promise构造函数,将来用new创建Promise对象
function Promise(fn) {
    // state变量存储当前Promise对象的执行状态
    var state = PENDING;
    // value变量存储执行成功后的返回值,或执行失败后的错误提示信息
    var value = null;
    // handlers变量是一个数组,存储将来用.then()函数传入的一个或多个后续任务函数。
    var handlers = [];

    function fulfill(result) {
        //执行成功后,把状态改为成功状态,并把执行结果返回值,保存在变量value中
        state = FULFILLED;
        value = result;
    }
    function reject(error) {
        //执行失败后,把状态改为失败状态,并把错误提示信息,保存在变量value中
        state = REJECTED;
        value = error;
    }

    function resolve(result) {
        //如果当前任务成功执行完成,使用者调用了resolve(返回值)
        try {
            var then = getThen(result); //就要收集当前Promise对象身上后续的.then()函数中传入的内容
            if (then) {
                //如果有.then
                //就调用核心doResolve函数,执行.then()中的函数,并传入两个状态切换函数。
                doResolve(then.bind(result), resolve, reject); //resolve和doResolve之间的递归用来处理promise的层层嵌套
                return;
            }
            //如果没有.then,就直接切换当前Promise对象的状态,并返回执行结果,结束当前Promise对象的执行
            fulfill(result);
        } catch (e) {
            //如果调用过程中出错,就调用reject()函数,将当前Promise状态切换为失败,并返回错误提示信息
            reject(e);
        }
    }

    /**
     * .then()函数中传入的内容有两种情况: 可能传入的是下一个Promise对象,也可能直接传入匿名函数
     * 如果调用resolve时传入的是下一个Promise对象,
     * 就返回这个Promise对象的.then()函数.
     * 如果调用resolve时传入的是下一个函数
     * 就直接返回这个函数即可
     * 如果调用resolve时什么都没传,就返回null
     */
    function getThen(value) {
        var t = typeof value;
        if (value && (t === "object" || t === "function")) {
            var then = value.then;
            if (typeof then === "function") {
                return then;
            }
        }
        return null;
    }

    /**
     * 调用传入的.then()函数,并传入执行成功和执行失败两个修改状态的回调函数*/
    function doResolve(fn, onFulfilled, onRejected) {
        var done = false; //默认暂时未执行成功
        try {
            fn(
                //调用当前任务函数
                function (value) {
                    //传入执行成功后,请使用者主动调用的res函数
                    if (done) return; //如果done被标记为true,说明当前异步任务执行完
                    done = true; //否则如果done暂时未被标记为true, 就标记为true,让当前异步任务状态变为完成状态
                    onFulfilled(value); //调用传入的第一步定义的改变当前Promise状态的函数,把当前Promise对象标记为执行成功,并保存返回值
                    //这里调用了resolve函数。
                },
                function (reason) {
                    //传入执行失败后,请使用者主动调用的resolve函数
                    if (done) return; //如果done被标记为true,说明当前异步任务执行完
                    done = true; //否则如果done暂时未被标记为true,就标记为true,让当前异步任务状态变为完成状态
                    onRejected(reason); //调用传入的第一步定义的改变当前Promise状态的函数,把当前Promise对象标记为执行失败,并保存错误提示
                }
            );
        } catch (ex) {
            //如果出现异常
            if (done) return; //如果done被标记为true,说明当前异步任务执行完,就退出当前任务的执行
            done = true; //否则如果done暂时未被标记为true,就标记为true。
            onRejected(ex); //调用传入的改变状态函数,把当前Promise对象标记为执行失败
        }
    }

    doResolve(fn, resolve, reject); // 调用上一步的doResolve(…)

    /*
    fn: function(res, rej){ 异步任务; 执行成功res(返回值); 执行失败rej(错误提示) }
    resolve是上一步定义的resolve函数
    reject是第一步定义的出错后,将当前Promise对象状态修改为失败的回调函数
    */

    function handle(handler) {
        if (state === PENDING) {
            handlers.push(handler);
        } else {
            if (
                state === FULFILLED &&
                typeof handler.onFulfilled === "function"
            ) {
                handler.onFulfilled(value);
            }
            if (
                state === REJECTED &&
                typeof handler.onRejected === "function"
            ) {
                handler.onRejected(value);
            }
        }
    }

    this.done = function (onFulfilled, onRejected) {
        // 使用定时器,确保当前任务一定是异步执行的
        setTimeout(function () {
            handle({
                //传入修改状态的两个回调函数
                onFulfilled: onFulfilled,
                onRejected: onRejected,
            });
        }, 0);
    };

    this.then = function (onFulfilled, onRejected) {
        var self = this; //保存当前Promise对象
        return new Promise(function (resolve, reject) {
            //创建并返回下一个Promise对象
            return self.done(
                function (result) {
                    //调用当前对象的done()函数
                    if (typeof onFulfilled === "function") {
                        //如果.then()中传入的是一个函数
                        try {
                            return resolve(onFulfilled(result)); //就调用resolve,传入下一项任务的函数,执行。
                        } catch (ex) {
                            return reject(ex);
                        }
                    } else {
                        return resolve(result); //否则就传入下一个Promise对象,继续等待。
                    }
                },
                function (error) {
                    if (typeof onRejected === "function") {
                        try {
                            return resolve(onRejected(error));
                        } catch (ex) {
                            return reject(ex);
                        }
                    } else {
                        return reject(error);
                    }
                }
            );
        });
    };
}