前端面试总结-ES6

203 阅读8分钟

ES6有哪些新特性

  • 新增let、const声明变量,实现了块级作用域

    let 块级作用域,没有变量提升,暂时性死区,块级作用域内不允许重复声明

    暂时性死区:

    在块级作用域内,若存在用let命令声明的变量,则所在区块对该变量形成封闭作用域,即该变量无视外部的同名变量。

    而又因为不存在变量提升,因此在未使用let声明变量前,该变量都不可使用,其在语法上称为“暂时性死区”。

    let a = 1;
    if(true){ 
        a = 2; //出错 not defined
        let a; 
    }
    
     var btns=document.getElementsByTagName('button')
          console.log(btns)
          for(var i=0;i<btns.length;i++){//如果使用var,每个打印的都是3,let则是123
              btns[i].addEventListener('click',function(){console.log(i)})
          }
    
  • 新增箭头函数

  • 引入promise、await/async解决异步回调问题

  • 引入class作为对象的模板,实现更好的面向对象编程

  • 引入模块方便模块化编程

  • 引入新的数据类型symbol,新的数据结构set和map

async函数

async函数返回一个Promise对象,可以使用then方法添加回调函数

async function helloAsync(){
    return "helloAsync";
  }
  
console.log(helloAsync())  // Promise {<fulfilled>: "helloAsync"}
 
helloAsync().then(v=>{
   console.log(v);         // helloAsync
})

async函数中可能会有await表达式,async函数执行时,如果遇到await就会先暂停执行,等到触发的异步操作完成之后,(await如果后面的出错 了,那么之后的语句就不会执行了)恢复async函数的执行并返回解析值

await关键字仅在async function中有效,如果在async function函数体外使用,只会得到一个语法错误

await

await操作符用于等待一个Promise对象,它只能在异步函数async function内部调用

返回值:

返回Promise对象的处理结果,如果等待的不是Promise对象,则返回该值本身

如果一个Promise被传递给一个await操作符,await将等待Promise正常处理完成并返回其结果

function testAwait(x){
    return new Promise(resolve=>{
          setTimeout(()=>{
              resolve(x)
         },2000)
    })
}
async function helloAsync(){
    const x = await testAwait('helloworld')
    console.log(x)
}
helloAsync()//helloworld
function testAwait(x){
    setTimeout(()=>{
        console.log(x)
    },3000)
}
async function helloAsync(){
   await testAwait('helloworld')
    console.log('11')
}
helloAsync()//先输出11,await期待的是promise,不是的话,按照普通顺序

await其实就是promise then的语法糖

如果要捕获错误的话得用try/catch

function testAwait(x){
    return new Promise((resolve,reject)=>{
          setTimeout(()=>{
              reject(x)
         },2000)
    })
}
async function helloAsync(){
    try{
        const x = await testAwait('helloworld')
        console.log(x)
         
    }catch(err){
        console.error(err)
    }
   
}
helloAsync()//helloworld

但是我尝试直接捕获好像也可以成功,不用加try catch

function testAwait(x){
    return new Promise((resolve,reject)=>{
          setTimeout(()=>{
              reject(x)
         },2000)
    })
}
async function helloAsync(){
   
        const x = await testAwait('helloworld').catch(err=>{console.error(err)})
        console.log(x)
}
helloAsync()//helloworld(err版本的)

Symbol的作用

唯一

a.js

const PASSWORD = Symbol()
 
class Login { constructor(username, password) { this.username = username this[PASSWORD] = password } checkPassword(pwd) { return this[PASSWORD] === pwd } } export default Login 

b.js

import Login from './a'
 
const login = new Login('admin', '123456') login.checkPassword('123456') // true login.PASSWORD // oh!no! login[PASSWORD] // oh!no! login["PASSWORD"] // oh!no! 

私有化:由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。

普通函数和箭头函数的区

  1. 定义不同

    箭头函数带箭头,普通函数不带

  2. 箭头函数全都是匿名函数

    普通函数有匿名函数,也可以是具名函数

  3. this指向不同

   var b=33
    let obj={
        a:12,
        fn(){
            console.log(this.a)
        },
        arrowfn:()=>{
            console.log('arrow',this.a)
            console.log('arrow',this.b)
        }
​
    }
    obj.fn()//12
    obj.arrowfn()//undefined   33
​
    var test=obj.fn
    var testarrow=obj.arrowfn
    test()//无调用者,this隐式丢失指向window
    testarrow()//与之前的输出结果一样,没有影响

image.png

普通函数的this指向调用者,箭头函数的this指向其定义位置的上下文this,即此处this指向obj的外层

  1. 箭头函数不具有prototype属性
function Foo(){}
let Foo2=()=>{}
let Foo3=function(){}
console.log(Foo.prototype)//{constructor: ƒ}
console.log(Foo2.prototype)//undefined
console.log(Foo3.prototype)//{constructor: ƒ}

image.png

5 . 箭头函数不能用于构造函数

ES6模块化

  1. ES6模块中自动采用严格模式,规定:

    • 变量必须先声明
    • 函数参数不能有同名属性
    • 禁止this指向全局
    • 对只读属性赋值、删除不可删除属性直接报错
    • arguments不可重新赋值,不会自动反应函数参数变化
    • 增加保留字static、interface、producted等
  2. export export语句输出的接口是对应值的引用,也就是一种动态绑定关系,通过该接口可以获取模块内部实时的值;export命令要处于模块顶层

    • 把export直接加到声明前面
    • export {a, b, c}
    • export default默认导出(一个js文件中只能有一个默认导出,但可以导出多个方法)
  3. import import是静态执行,Singleton模式;import命令要处于模块顶层

    • import {XX} from ‘./test.js’
    • import {XX as YY} from ‘./test.js’
    • import * as YY from ‘./test.js’

CommonJS与ES6模块化的区别

  1. 对于模块的依赖,CommonJS是动态的,ES6 Module 是静态的

    对于模块的依赖,何为动态?何为静态

    动态是指对于模块的依赖关系建立在代码执行阶段; 静态是指对于模块的依赖关系建立在代码编译阶段;

  2. CommonJS导入的是值的拷贝,ES6 Module导入的是值的引用

JS的new操作符做了哪些事情

  1. 创建一个类的实例:创建一个空对象obj,将obj.proto设置为构造函数的prototype
  2. 初始化实例:构造函数被传入参数并调用,this指针被设定为指向该实例obj
  3. 返回实例obj

JS单例模式

  • 定义 1.只有一个实例 2.可以全局访问
  • 主要解决的问题:一个全局使用的类 频繁的创建和销毁
  • 何时使用:当你想控制实例的数目 节省系统化资源的时候
  • 如何实现:判断系统是否有这个单例,如果有则返回,没有则创建
  • 单例模式的优点:内存中只有一个实例 减少了内存的开销 尤其是频繁的创建和销毁实例(比如首页页面的缓存)

ES6创建单例模式

class Person{
         constructor(name,sex,hobby){
             this.name = name
             this.sex = sex
             this.hobby = hobby
         }
         static getInstance(name,sex,hobby){//必须加static
             if(!this.instance){
                 this.instance = new Person(name,sex,hobby)
             }
             return this.instance
         }
     }
​
     let person1 = Person.getInstance('胡图图','男','吃瓜')
     let person2 = Person.getInstance('胡英俊','女','打豆豆')
     console.log(person1==person2)//true
     console.log(person1)//'胡图图','男','吃瓜'
     console.log(person2)

手写map

 var arr=[1,2,3]
        function map(arr,mapCallback){
            //检查参数是否正确
            if(!Array.isArray(arr)||!arr.length||typeof mapCallback!=="function"){return []}
            else{
                let result = []
                for(let i=0;i<arr.length;i++){
                    result.push(mapCallback(arr[i],i,arr))//1每一项 2索引 3传入数组
                }
                return result
            }
        }
​
        var res = map(arr,(item)=>{
            return item*2
        })
​
        console.log(res)//2,4,6

原型链写法+reduce

let arr=[1,2,3,4]
Array.prototype.mymap=function(callback){
    return this.reduce((pre,cur)=>{
        return pre.concat(callback(cur))
    },[])
}

原型链+for

let arr=[1,2,3,4]
Array.prototype.mymap=function(callback){
    let newarr=[]
    for(let value of this){
        newarr.push(callback(value))
    }
    return newarr
}

手写reduce

let arr=[1,2,3,4]
Array.prototype.myreduce=function(callback,initValue){
    var total=initValue?initValue:this[0]
    for(let i=initValue?0:1;i<this.length;i++){
        total=callback(total,this[i],i,this)
    }
    return total 
}
console.log(arr.myreduce((pre,cur)=>{
    return pre+cur
},10))

手写Promise

class mPromise{
    constructor(process){
        this.status = 'pending'
        this.msg = ''
        process(this.resolve.bind(this),this.reject.bind(this))//一定要bind,不然then中无法使用箭头函数,为啥要用bind,因为这个地方如果用call,那么resolve这个函数直接就在参数里面执行了
        return this
    }
    resolve(val){
        this.status = 'fulfilled'
        this.msg = val
    }
    reject(err){
        this.status = 'rejected'
        this.msg = err
    }
    then(resolve,reject){
        if(this.status=='fulfilled'){resolve(this.msg)}
        if(this.status=='rejected'){reject(this.msg)}
    }
}
var my_promise = new mPromise(function(resolve, reject) {
    resolve('OK');
});
my_promise.then(function(val) {
    console.log(`success: ${val}`);
}, function(err){
    console.log(`failed: ${err}`);
}); 

Promise实现sleep

function mysleep(time){
    return new Promise((resolve)=>setTimeout(resolve,time))
}
mysleep(5000).then(()=>{
    console.log("i sleep 5s")
})

setTimeout实现setInterval

function mySetInterval(fn, millisec){
    
  function interval(){
    setTimeout(interval, millisec);//由于为异步,所以先执行后面的
    fn();
  }
  mySetInterval.timer=setTimeout(interval, millisec)
}
mySetInterval.clear=function(){
    clearTimeout(mySetInterval.timer)
}
mySetInterval(()=>{
    console.log(1)
},1000)
​
mySetInterval.clear()

Promise链式调用

可以用来解决回调地狱

class Task{
    constructor(){
        this.do()
    }
    do(){
        this.log(1).then(res=>{
            return this.log(2)
        }).then(res=>{
            return this.sleep(3)
        }).then(res=>{
            return this.log(4)
        }).then(res=>{
            return this.sleep(2)
        }).then(res=>{
            return this.sleep(3)
        }).then(res=>{
            return this.log(5)
        })
        
    }
    log(count){
        return new Promise(resolve=>{
            console.log(count)
            resolve()
        })
    }
    sleep(time){
        return new Promise(resolve=>{
            setTimeout(()=>{
                resolve()
            },time*1000)
        })
    }
}
const t = new Task()

Promise.all

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})
​
let p2 = new Promise((resolve, reject) => {
  resolve('success')
})
​
let p3 = Promise.reject('失败')
​
Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})
​
Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})

Promise的几种方法

Promise.all

  • 所有成功才成功,一个失败就失败

    失败返回失败的值,成功返回成功一个数组,数组中记录着成功的值 eg:['成功了', 'success']

    手写

     Promise.myall=function(iterator){
                let count=0//用于计数,等于len的时候就resolve
                let len = iterator.length
                let res=[]
                return new Promise((resolve,reject)=>{
                    for(let i in iterator){//一定不能var
                        Promise.resolve(iterator[i])
                        .then(data=>{
                            res[i]=data
                            if(++count===len){resolve(res)}
                            
                        })
                        .catch(err=>{
                            reject(err)
                        })
                    }
                })
            }
    ​
            var p1=Promise.resolve(1)
            var p2=Promise.resolve(2)
            var p3=Promise.resolve(3)
    ​
            Promise.myall([p1,p2,p3]).then(res=>{
                console.log(res)
            }).catch(err=>{
                console.error(err)
            })
    

Promise.allSettled

  • 返回一个对象数组
  • 每一个promise的状态都包含在其中

image.png

Promise.race

  • 所有的promise进行赛跑,返回最快成功或者失败的那个

    手写

        Promise.myrace=function(iterators){
              return new Promise((resolve,reject)=>{
                  for(let p of iterators){
                      Promise.resolve(p)
                      .then(res=>{
                          resolve(res)
                      })
                      .catch(err=>{
                          reject(err)
                      })
                  }
              })
          }
    ​
            var p1=Promise.reject(1)
            var p2=Promise.resolve(2)
            var p3=Promise.resolve(3)
    ​
            Promise.myrace([p1,p2,p3]).then(res=>{
                console.log(res)
            }).catch(err=>{
                console.error(err)
            })
    

Promise.any

  • 与Promise.all相反
  • 一个成功就成功,所有失败才失败

一旦有一个成功,则返回这个成功的值,如果全部失败,返回一个AggregateError: All promises were rejected

模拟实现数组push

let arr=[1,2,3,4,5]
Array.prototype.mypush=function(){//如果用箭头函数里面不能使用arguments
    for(let i=0;i<arguments.length;i++){
        this[this.length]=arguments[i]
    }
    return this.length
}
arr.mypush(2)