es6基础语法解析

205 阅读3分钟

一、历史

  • 2011年6月es5发布,2015年6月发布es6(es2015)
  • ESNext:es下一个版本

二、es6及以后新增的常用api解析

1. let和const

for(var i=0;i<=3;i++){
    setTimeout(function(){
        console.log(i)
    },10)
}
//结果 4 4 4 4
//原因:
//1、var 定义的变量是全局的,所以全局只有一个变量i,
//2、setTimeout,下一轮事件循环的时候执行,i=4
for(var i=0;i<=3;i++){
    setTimeout(function(){
        console.log(i)
    },10)
}
//结果 1 2 3 4
//原因:见let的特性
//除了let 的方式,还可以适用闭包自执行函数。
for(var i=0;i<=3;i++){
    (function(i){
        setTimeout(function(){
            console.log(i)
        },10)
    })(i)
}

  • let引用了块级作用域的概念,创建setTimeout的时候,变量i只在作用域内生效,对于循环的每一次,引用的i都是不同的。
  • 变量提升的问题
  • const 定义一个常量

2、箭头函数

1、指向问题
箭头函数的this: 指向箭头函数定义时所处的对象,
普通函数的this:指向它的调用者,如果没有调用者则默认指向window.
2、缩写
const arrowFn = (value)=>Number(value);
const obj = ()=>{{}} // {}
3、箭头函数不能用作构造函数的原因
构造函数:改变this指向,指到新实例

箭头函数:this是在定义的时候决定的

它们的**结果是相悖的**,所以不能用作构造函数。

3、class

class Test{
    _name = '';
    constructor(name){
        this.name = name
    }
    static getFormatName(){
    
    }
    get name(){
    
    }
    set name(val){
        this._name = val
    }
}
const instance = new Test('duoduo')

4、模板字符串

面试题:编写一个render函数,大概实现模板字符串的功能
const year = '2021';
const month = '10';
const day = '01';

const template = '${year}-${month}-${day}';
const context = {year, month, day};

const str = render(template)(context);
console.log(str);

function render(template){
    return function(context){
        return template.replace(/\$\{(.*?)\}/g,(match,key)=>context[key]);
    }
}

5、解构

// 数组
let [a,b,c] = [1,2,3];
// 对象
const {f1,f2} = {
    f1:'11',
    f2:'22',
}
解构的原理:

针对可迭代对象iterator,通过遍历按顺序获取对应的值进行赋值:

  • iterator是什么?是一种接口,interface,为各种不一样的数据解构提供统一的访问机制;
  • 任何数据解构只要有iterator接口;
  • for of ,相当于一个遍历器,遍历数据的时候,去寻找interator,
//对象不能使用for of遍历
const obj = {name:'duoduo'};
for(let key of obj){
    console.log(key) // 报错
}
  • interator的作用?
1、为各种不同的数据解构提供统一的访问接口
2、数据解构按照顺序处理
3for of 可以进行遍历
function generateInterator(array){
    let nextIndex = 0;
    
    return {
        next:()=>nextIndex<array.length ? {
            value:array[nextIndex++],
            done:false
        }:{
            value:undefined,
            done:true
        }
    }
}

const iterator = generateInterator([0,1,2])
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next()) //{value:undefined,done:true}
  • 可迭代对象是什么?
iterator接口的实现;
可迭代对象存在两种协议,可迭代协议,迭代器协议;
*可迭代协议:对象必须实现iterator方法,对象或者原型上必须有一个Symbol.iterator:()=>迭代器协议
*迭代器协议:必须实现一个next方法,next方法返回的对象done value
面试题:使一个对象可以使用for of遍历?
const obj = {
    count:0,
    [Symbol.iterator]:()=>{
        return {
            next:()=>{
                obj.count ++;
                if(obj.count <= 10){
                    return {
                        value:obj.count,
                        done:false
                    }
                }else{
                    return {
                        value:undefined,
                        done:true
                    }
                }
            }
        }
    }
}
for(let item of obj){

}

6、遍历

for in:

遍历数组的时候,特点:

  • 不仅遍历当前的对象,还会遍历原型链上的;
  • 不适合遍历数组
forEach
  • 不会被break中断;
for of:
  • 特性:会被break中断

7、object

Object.keys()

获取所有key的数组

面试题:怎么模拟实现一个Object.keys
function getObjectKeys(obj){
    const result = [];
    for(const key in obj){
        if(obj.hasOwnProperty(key)){
            result.push(key)
        }
    }
    return result
}

Object.values()

获取所有value的数组

  • Object.entries() 获取所有键值对数组
面试题:⼿写实现⼀个函数模拟Object.entries?
function getObjectEntries(obj){
    const result = [];
    for(const key in obj){
        if(obj.hasOwnProperty(key)){
            result.push([key,obj[key]])
        }
    }
    return result;
}
Object.getOwnPropertyNames()

该方法返回一个数组,该数组对元素是obj自身拥有的枚举或者不可枚举属性名称字符串。

Object.prototype.aa = '1111';
const testData = { a: 1, b: 2 }
for (const key in testData) {
    console.log(key); 
} 
console.log(Object.getOwnPropertyNames(testData))
// ['a','b']
Object.getOwnPropertyDescriptor

是对象属性的描述符,是一个对象。 什么是descriptor? 对象对应的属性描述符, 是⼀个对象. 包含以下属性:

  • configurable。 如果为false,则任何尝试删除⽬标属性或修改属性特性(writable, configurable, enumerable)的⾏为将被⽆效化。所以通常属性都有特性时,可以把 configurable设置为true即可。
  • writable 是否可写。设置成 false,则任何对该属性改写的操作都⽆效(但不会报错,严格模 式下会报错),默认false。
  • enumerable。是否能在for-in循环中遍历出来或在Object.keys中列举出来。

8、get set

// vue2中的数据劫持
const obj = {}
let val = undefined;
Object.defineProperty('obj','a',{
    set:function(value){
        val = value
    },
     get:function(){
        return val;
    },
    configurable:true
})

vue3中的数据劫持

const obj = new Proxy({},{
    get:function(target,propKey){
        return target[propKey]
    },
    set;function(target,propKey,value){
        return Reflect.set(target,propKey,value)
    }
})

Reflect是什么? 是js语言的一种优化 将命令式行为转为函数式行为;

9、Object.assign() 浅拷贝

类似于{...a,...b}

const newObj = Object.assign({},{
    name:'niuniu',
    age:11
},{
    name:'duoduo'
})
console.log(newObj)  {name:'duoduo',age;11}
面试题:实现一个浅拷贝
function shallowClone(source){
    const target = {};
    for(const i in source){
        if(source.hasOwnProperty(i)){
            target[i] = source[i]
        }
    }
    return target;
}

10、promise

面试题:实现一个promise.all
function PromiseAll(promiseArray){
    return new Promise(function(resolve, reject)=>{
        // 判断参数类型
        if(!Array.isArray(promiseArray)){
            return reject(new TypeError('参数必须是函数'))
        }
        let counter = 0;
        let promiseNum = promiseArray.length;
        let resolvedArray = [];
        for(let i=0;i<promiseNum;i++){
            Promise.resolve(promiseArray[i])
                .then((value)=>{
                    counter++;
                    resolvedArray[i] = value;
                    if(counter == promiseNum){
                        resolve(resolvedArray)
                    }
                })
                .catch(e=>reject(e))
        }
    })
}
面试题:实现一个promise.allSettled

返回所有promise的状态和结果

function PromiseAllSettled(promiseArray){
    return new Promise((resolve, reject)=>{
        if(!Array.isArray(promiseArray)){
            return reject(new TypeError('参数必须是一个数组'))
        }
        
        let counter = 0;
        const promiseNum = promiseArray.length;
        const resolvedArray = [];
        
        for(let i=0;i<promiseNum;i++){
            Promise.resolve(promiseArray[i])
                .then((value)=>{
                    resolvedArray[i]=>{
                        status:'fulfilled',
                        value
                    }
                })
                .catch((reason)=>{
                    resolvedArray[i]=>{
                        status:'rejected',
                        reason
                    }
                })
                .finally(()=>{
                    counter++;
                    if(counter === promiseNum){
                        resolve(resolvedArray)
                    }
                })
        }
    })
}

11、数组

Array.flat(arr,deep)数组扁平化

flat() ⽅法会按照⼀个可指定的深度递归遍历数组,并将所有元素与遍历到的⼦数组中的元素 合并为⼀个新数组返回

const arr1 = [1,2,[3,4,[5]]]
arr1.flat(1) //或者Infinity
面试题:实现一个flatDeep
function flatDeep(arr, d=1){
    if(d>0){
        return arr.reduce((res,val)=>{
            if(Array.isArray(val)){
                res = res.concat(flatDeep(val,d-1))
            }else{
                res = res.concat(val)
            }
            return res;
        },[])
    }else{
        return arr.slice();
    }
}
console.log(flatDeep(arr1,Infinity))
Array.includes()

includes() ⽅法⽤来判断⼀个数组是否包含⼀个指定的值,根据情况,如果包含则返回 true, 否则返回false。

Array.find

find() ⽅法返回数组中满⾜提供的测试函数的第⼀个元素的值。否则返回 undefined。

Array.from

法从⼀个类似数组或可迭代对象创建⼀个新的,浅拷⻉的数组实例

面试题:如何把一个类数组转换成真数组?

1、Array.from

2、[...arguments]

3、Array.prototype.slice.call()

Array.from([1,2,3],x=>x+1) //[2,3,4]

12、babel的解析

1、解析

接受代码输出ast

  • 词法分析
  • 语法分析
2、转换

接受ast对其进行遍历,可以对节点进行添加、更新、移除等操作

3、生成

把转换过的ast生成为字符串形式的代码,并且创建source map.