精华内容
下载资源
问答
  • ES6前端面试题整理

    千次阅读 2021-03-18 20:52:12
    ES6前端面试题整理,es5和es6的区别, ES6新增的一些特性,箭头函数,模板字符串,解构赋值,... 展开运算符,修饰器 @,Symbol,Proxy代理,使用箭头函数应注意什么?Set、Map的区别?setTimeout、Promise、Async/Await 的...

    1、说说你了解的ES6新增特性

    let声明变量和const声明常量,两个都有块级作用域

    ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明

    箭头函数

    ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义

    特点:箭头函数中的this始终指向箭头函数定义时的离this最近的一个函数,如果没有最近的函数就指向window。

    模板字符串

    模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串

    var name = '丁香医生';
    var desc = '丁香医生是面向大众的科普性健康类网站';
    var html = function(name, desc){
      var tpl = '公司名:' + name + '\n'+
          '简介:'+ desc;
      return tpl;
    }
    而现在:
    
    var html = `公司名:${name}
      简介:${desc}`;
    

    解构赋值

    ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值

    for of循环

    for…of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串

    应用场景

    //arguments是个类数组对象,通常先要将其转换为数组才能遍历,但for...of可以直接遍历
    function sum() {
        let sum = 0
        for(let num of arguments){
            sum += num
        }
        console.log(sum);        //15
    }
    
    sum(1,2,3,4,5)
    //遍历字符串
    let name = 'Asher';
    for (let char of name){
        console.log(char);         //A s h e r
    }
    //遍历nodelists,效果如下图
    <style type="text/css">
        .completed {
            text-decoration: line-through;
        }
    </style>
    <body>
        <ul>
            <li>yoga</li>
            <li>boxing</li>
            <li>press</li>
        </ul>
    
        <script type="text/javascript">
            const lis = document.querySelectorAll('li');
            for(let li of lis){
                li.addEventListener('click',function(){
                    this.classList.toggle('completed');
                })
            }
        </script>
    </body>
    

    import、export导入导出

    ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用

    set数据结构

    Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数

    应用

    1、数组去重
    
    let arr = [3, 5, 2, 2, 5, 5];
    let unique = [...new Set(arr)];
    // [3, 5, 2]
    
    2、并集(Union)、交集(Intersect)和差集(Difference)
    
    let a = new Set([1, 2, 3]);
    let b = new Set([4, 3, 2]);
    
    // 并集
    let union = new Set([...a, ...b]);
    // Set {1, 2, 3, 4}
    
    // 交集
    let intersect = new Set([...a].filter(x => b.has(x)));
    // set {2, 3}
    
    // 差集
    let difference = new Set([...a].filter(x => !b.has(x)));
    // Set {1}
    

    … 展开运算符

    可以将数组或对象里面的值展开;还可以将多个值收集为一个变量

    应用

    1. 合并数组
    
    arr1.push(...arr2) // 将arr2 追加到数组的末尾
    arr1.unshift(...arr2) // 将arr2 追加到数组的开头
    
    2. 拷贝数组
    
    var arr = [1,2,3];
    var arr2 = [...arr]; // 和arr.slice()差不多
    arr2.push(4)
    //记住:数组中的对象依然是引用值,所以不是任何东西都“拷贝”过去了。
    
    3.将arguments或者NodeList转换为Array
    
    
    var myFn = function(...args) {
    // ...
    }
    
    4. 使用Math函数
    
    let numbers = [9, 4, 7, 1];
    Math.min(...numbers); // 1
    //相当于Math.min.apply(numbers);
    

    修饰器 @

    decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数

    class 类的继承

    ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念

    class Person
      {  
            constructor(name, age) {
              this.name = name;
              this.age = age;
            }
     
            sayName(){
                console.log("the name is:"+this.name);
            }
      }
     
      class Worker extends Person{
        constructor(name, age,job) {
             super(name, age);
              this.job = job;
        }
        sayJob(){
          console.log("the job is:"+this.job)
        }
      }
     
      var worker = new Worker('tcy',20,'teacher');
      worker.sayJob();//the job is:teacher
      worker.sayName();//the name is:tcy
    

    async、await

    使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程,提高代码的简洁性和可读性

    async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成

    // 2s 之后返回双倍的值
    function doubleAfter2seconds(num) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2 * num)
            }, 2000);
        })
    }
    
    async function testResult () {
        let first = await doubleAfter2seconds(10);
        let second = await doubleAfter2seconds(20);    
        console.log(first + second);
    }
    

    promise

    Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大

    new Promise((resolve,reject)=>{
    if('成功') resolve(1);
    if('失败') reject(0);
    }).then(()=>{
        console.log("成功");
    }).catch(()=>{
        console.log("失败");
    })
    

    它有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败。

    Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。对于已经实例化过的 promise 对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。then()方法接收两个参数:onResolve和onReject,分别代表当前 promise 对象在成功或失败时。

    var promise = new Promise((resolve, reject) => {
        var success = true;
        if (success) {
            resolve('成功');
        } else {
            reject('失败');
        }
    }).then(
        (data) => { console.log(data)},
        (err) => { console.log(err)}
    )
    

    promise的执行过程

    setTimeout(function() {
        console.log(0);
    }, 0);
    var promise = new Promise((resolve, reject) => {
        console.log(1);
        setTimeout(function () {
            var success = true;
            if (success) {
                resolve('成功');
            } else {
                reject('失败');
            }
        },2000);
    }).then(
        (data) => { console.log(data)},
        (data) => { console.log(data)}
    );
    console.log(promise);	//<pending> 进行中
    setTimeout(function () {
        console.log(promise);	//<resolved> 已完成
    },2500);
    console.log(2);
     
    //1
    //Promise {<pending>}
    //2
    //0
    //成功
    //Promise {<resolved>: undefined}
    

    Symbol

    Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的

    let s1 = Symbol()
    let s2 = Symbol('another symbol')
    let s3 = Symbol('another symbol')
    
    s1 === s2 // false
    s2 === s3 // false
    //symbol是唯一的
    

    应用场景1:使用Symbol来作为对象属性名(key)

    在这之前,我们通常定义或访问对象的属性时都是使用字符串,比如下面的代码:
    
    let obj = {
      abc: 123,
      "hello": "world"
    }
    
    obj["abc"] // 123
    obj["hello"] // 'world'
    而现在,Symbol可同样用于对象属性的定义和访问:
    
    const PROP_NAME = Symbol()
    const PROP_AGE = Symbol()
    
    let obj = {
      [PROP_NAME]: "一斤代码"
    }
    obj[PROP_AGE] = 18
    
    obj[PROP_NAME] // '一斤代码'
    obj[PROP_AGE] // 18
    
    
    随之而来的是另一个非常值得注意的问题:就是当使用了Symbol作为对象的属性key后,在对该对象进行key的枚举时,会有什么不同?在实际应用中,我们经常会需要使用Object.keys()或者for...in来枚举对象的属性名,那在这方面,Symbol类型的key表现的会有什么不同之处呢?来看以下示例代码:
    
    let obj = {
       [Symbol('name')]: '一斤代码',
       age: 18,
       title: 'Engineer'
    }
    
    Object.keys(obj)   // ['age', 'title']
    
    for (let p in obj) {
       console.log(p)   // 分别会输出:'age' 和 'title'
    }
    
    Object.getOwnPropertyNames(obj)   // ['age', 'title']
    由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
    
    也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外:
    
    JSON.stringify(obj)  // {"age":18,"title":"Engineer"}
    我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。
    
    然而,这样的话,我们就没办法获取以Symbol方式定义的对象属性了么?非也。还是会有一些专门针对Symbol的API,比如:
    
    // 使用Object的API
    Object.getOwnPropertySymbols(obj) // [Symbol(name)]
    
    // 使用新增的反射API
    Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
    

    应用场景2:使用Symbol来替代常量

    先来看一下下面的代码,是不是在你的代码里经常会出现?
    
    const TYPE_AUDIO = 'AUDIO'
    const TYPE_VIDEO = 'VIDEO'
    const TYPE_IMAGE = 'IMAGE'
    
    function handleFileResource(resource) {
      switch(resource.type) {
        case TYPE_AUDIO:
          playAudio(resource)
          break
        case TYPE_VIDEO:
          playVideo(resource)
          break
        case TYPE_IMAGE:
          previewImage(resource)
          break
        default:
          throw new Error('Unknown type of resource')
      }
    }
    如上面的代码中那样,我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间是唯一的关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字。
    
    现在有了Symbol,我们大可不必这么麻烦了:
    
    const TYPE_AUDIO = Symbol()
    const TYPE_VIDEO = Symbol()
    const TYPE_IMAGE = Symbol()
    这样定义,直接就保证了三个常量的值是唯一的了!是不是挺方便的呢。
    

    应用场景3:使用Symbol定义类的私有属性/方法

    我们知道在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。
    
    而有了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内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
    

    Proxy代理

    使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

    ES6原生提供了Proxy构造函数,用来生成Proxy实例。
    
    var proxy = new Proxy(target, handler);
    Proxy对象的所有用法,都是上面的这种形式。不同的只是handle参数的写法。其中new Proxy用来生成Proxy实例,target是表示所要拦截的对象,handle是用来定制拦截行为的对象。
    
    var proxy = new Proxy({}, {
      get: function(target, property) {
        return 35;
      }
    });
    
    proxy.time // 35
    proxy.name // 35
    proxy.title // 35
    上面是一个拦截读取属性的行为的例子。要使Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象(target)进行操作。
    

    2、var、let、const之间的区别

    • var声明变量可以重复声明,而let不可以重复声明
    • var是不受限于块级的,而let是受限于块级
    • var会与window相映射(会挂一个属性),而let不与window相映射
    • var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
    • const声明之后必须赋值,否则会报错
    • const定义不可变的量,改变了就会报错
    • const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错

    3、使用箭头函数应注意什么?

    • 用了箭头函数,this就不是指向window,而是父级(指向是可变的),在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象
    • 不能够使用arguments对象
    • 不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
    • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数

    this的指向总是让人困扰,但是有了=>以后妈妈再也不用担心你使用this了

    class Animal {
        constructor(){
            this.type = 'animal'
        }
        says(say) {
            setTimeout(function () {
                console.log(this.type + 'says' + say)
            },1000)
        }
    }
    var animal = new Animal()
    animal.says('hi') // undefined says hi
    我们再来看看=>的情况
    
    class Animal() {
        constructor() {
            this.type = 'animal'
        }
        says(say) {
            setTimeout(() => {
                console.log(this.type + ' says ' + say)
            }, 1000)
        }
    }
    var animal = new Animal()
    animal.says('hi') // animal says hi
    

    4、ES6的模板字符串有哪些新特性?并实现一个类模板字符串的功能

    基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定

    在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)就能解决

      let name = 'web';
            let age = 10;
            let str = '你好,${name} 已经 ${age}岁了'
            str = str.replace(/\$\{([^}]*)\}/g,function(){
                return eval(arguments[1]);
            })
            console.log(str);//你好,web 已经 10岁了    
    

    5、介绍下 Set、Map的区别?

    应用场景
    Set用于数据重组,Map用于数据储存

    Set

    • 成员不能重复
    • 只有键值没有键名,类似数组
    • 可以遍历,方法有add, delete,has

    Map

    • 本质上是健值对的集合,类似集合
    • 可以遍历,可以跟各种数据格式转换

    6、ECMAScript 6 怎么写 class ,为何会出现 class

    ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法

    //定义类
    class Point{
        constructor(x,y){
            //构造方法
            this.x = x; //this关键字代表实例对象
            this.y = y;
        }
        toString(){
            return `(${this.x},${this.y})`; //在windows键盘上,``是英文输入法下tab按键上面那个键。
            //'('+this.x+','+this.y+')';
        }
    }
    let p  =new Point(1,2);
    p.toString(); //"(1,2)"
    

    7、Promise构造函数是同步执行还是异步执行,那么 then 方法呢?

    promise构造函数是同步执行的,then方法是异步执行的

    8、setTimeout、Promise、Async/Await 的区别

    事件循环中分为宏任务队列和微任务队列

    其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行

    promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行

    async函数表示函数里面可能会有异步方法,await后面跟一个表达式

    async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行

    9、promise有几种状态,什么时候会进入catch?

    三个状态:pending、fulfilled、reject

    两个过程:pending -> fulfilled、pending -> rejected

    当pending为rejected时,会进入catch

    10、下面的输出结果是多少

    const promise = new Promise((resolve, reject) => {
        console.log(1);
        resolve();
        console.log(2);
    })
    
    promise.then(() => {
        console.log(3);
    })
    
    console.log(4);
    
    // 1 2 4 3
    

    Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次事件循环的结尾立刻执行,所以会继续输出4,最后输出3.

    11、设计一个对象,键名的类型至少包含一个symbol类型,并且实现遍历所有key

    let name = Symbol('name');
     let product = {
        [name]:"洗衣机",    
        "price":799
      };
      Reflect.ownKeys(product); 
      //Reflect.ownKeys() :相当于Object.getOwnPropertyNames(target) concat(Object.getOwnPropertySymbols(target)
    

    12、下面Set结构,打印出的size值是多少

    let s = new Set();
    s.add([1]);
    s.add([1]);
    console.log(s.size);
    //2
    

    两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值

    都能存储到Set结构中,所以size为2

    13、Promise 中reject 和 catch 处理上有什么区别

    • reject 是用来抛出异常,catch 是用来处理异常
    • reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
    • reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch
    • 网络异常(比如断网),会直接进入catch而不会进入then的第二个回调

    14、使用class 手写一个promise

    //创建一个Promise的类
      class Promise{
        constructor(executer){//构造函数constructor里面是个执行器
          this.status = 'pending';//默认的状态 pending
          this.value = undefined//成功的值默认undefined
          this.reason = undefined//失败的值默认undefined
          //状态只有在pending时候才能改变
          let resolveFn = value =>{
            //判断只有等待时才能resolve成功
            if(this.status == pending){
              this.status = 'resolve';
              this.value = value;
            }
          }
          //判断只有等待时才能reject失败
          let rejectFn = reason =>{
            if(this.status == pending){
              this.status = 'reject';
              this.reason = reason;
            }
          }    
          try{
            //把resolve和reject两个函数传给执行器executer
            executer(resolve,reject);
          }catch(e){
            reject(e);//失败的话进catch
          }
        }
        then(onFufilled,onReject){
          //如果状态成功调用onFufilled
          if(this.status = 'resolve'){
            onFufilled(this.value);
          }
          //如果状态失败调用onReject
          if(this.status = 'reject'){
            onReject(this.reason);
          }
        }
      } 
    

    15、如何使用Set去重

    let arr = [12,43,23,43,68,12];
    let item = [...new Set(arr)];
    console.log(item);//[12, 43, 23, 68]
    let item1 = Array.from(new Set(arr));
    console.log(item1);//[12, 43, 23, 68]
    

    16、将下面for循环改成for of形式

    let arr = [11,22,33,44,55];
    let sum = 0;
    for(let i=0;i<arr.length;i++){
        sum += arr[i];
    }
    

    答案:

    let arr = [11,22,33,44,55];
    let sum = 0;
    for(value of arr){
        sum += value;
    }
    

    遍历数组:for in 和 for of

    for(let i in arr ){
        console.log(arr[i])
    }
    for(let i of arr){
        console.log(i);
    }
    

    17、理解 async/await以及对Generator的优势

    • async await 是用来解决异步的,async函数是Generator函数的语法糖
    • 使用关键字async来表示,在函数内部使用 await 来表示异步
    • async函数返回一个Promise对象,可以使用then方法添加回调函数
    • 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

    async较Generator的优势:

    • 内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
    • 更好的语义。async 和 await 相较于 * 和 yield 更加语义化
    • 更广的适用性。yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值
    • 返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用

    18、forEach、for in、for of三者区别

    • forEach更多的用来遍历数组

    • for in 一般常用来遍历对象或json

    • for of数组对象都可以遍历,遍历对象需要通过和Object.keys()

    • for in循环出的是key,for of循环出的是value

      var user = {name:‘张三’,age:10,sex:‘男’}
      for(let index in user){
      console.log(index+’:’+user[index])
      //“name:张三”"age:10"“sex:男”
      }
      for(let index of Object.keys(user)){
      console.log(index+’:’+user[index])
      //“name:张三”"age:10"“sex:男”
      }

    19、说一下es6的导入导出模块

    导入通过import关键字

    // 只导入一个
    import {sum} from "./example.js"
    
    // 导入多个
    import {sum,multiply,time} from "./exportExample.js"
    
    // 导入一整个模块
    import * as example from "./exportExample.js"
    

    导出通过export关键字

    //可以将export放在任何变量,函数或类声明的前面
    export var firstName = 'Michael';
    export var lastName = 'Jackson';
    export var year = 1958;
    
    //也可以使用大括号指定所要输出的一组变量
    var firstName = 'Michael';
    var lastName = 'Jackson';
    var year = 1958;
    export {firstName, lastName, year};
    
    //使用export default时,对应的import语句不需要使用大括号
    let bosh = function crs(){}
    export default bosh;
    import crc from 'crc';
    
    //不使用export default时,对应的import语句需要使用大括号
    let bosh = function crs(){}
    export bosh;
    import {crc} from 'crc';
    

    本文链接:https://blog.csdn.net/qq_39903567/article/details/114990474

    展开全文
  • 2020年Web前端面试题及答案----ES6

    千次阅读 2020-12-14 16:36:28
    ES6添加了一系列新的语言特性,其中一些特性比其它更具有开创性以及更广的可用性。 列举常用的ES6方法: let、const 箭头函数 类的支持 字符串模块 Promises 箭头函数需要注意哪些地方? 当要求动态上下文的时候,...

    前言

    失踪博主终于找回自己的账号密码了0V0,这次给大家带来的是Es6的一些的面试问题,不知道看过前面文章的小伙伴们有没有找到心仪的工作呢?

    ES6添加了一系列新的语言特性,其中一些特性比其它更具有开创性以及更广的可用性。
    列举常用的ES6方法:

    1. let、const
    2. 箭头函数
    3. 类的支持
    4. 字符串模块
    5. Promises

    箭头函数需要注意哪些地方?
    当要求动态上下文的时候,就不能够使用箭头函数,也就是this的固定化。

    1. 在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象;
      
    2. 不能够用作构造函数,这就是说,不能够使用new命令,否则就会抛出一个错误。
      
    3. 不能够使用arguments对象;
      
    4. 不能使用yield命令
      

    let、const、var
    var声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的,由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明;
    let是更完美的var,不是全局变量,具有块级函数作用域,大多数情况不会发生变量提升。

    1. let声明的变量具有块级作用域
      
    2. let生命的变量不能通过window.变量名访问
      
    3. 形如for(let x...)的循环是每次迭代都为x创建新的绑定
      
    4. let约束了变量提升
      
    5. let不允许在相同作用域内重复声明同一个变量名,var是允许的
      

    const定义的常量值,不能够重新赋值,如果值是一个对象,可以改变对象里边的属性值。const变量声明的时候必须初始化
    拓展:var方式定义的变量有什么样的bug?

    1. js没有块级作用域,在Js函数中的var声明,其作用域是函数体的全部
    var
    for(var i=0;i<10;i++){
    var a = 'a';
    }
    console.log(a,i); //a 10
    
    let
    for(let i=0;i<10;i++){
    let a = 'a';
    }
    console.log(a,i); //a is not defined
    
    1. 循环内变量过度共享
    for (var i = 0; i < 3; i++) {
    setTimeout(function () {
    console.log(i)
    }, 1000);
     }//3个3
    

    这里循环本身及三次timeout回调均共享唯一的变量i。当循环结束执行时,i的值为3.所以当第一个timeout执行时,调用的i当然也为3了。

    Set数据结构
    ES6中的Set方法本身是一个构造函数,它类似于数组,但是成员的值都是唯一的
    拓展:数组去重的方法
    ES6 set方法

    var arr = new Set([1,2,2,3,4]);
        console.log([...arr]); //(4) [1, 2, 3, 4]
    

    以往去重方法

    var arr = [1,1,2,2,3,4];
        //创建一个空数组用于接收不重复内容的数组
        var new_arr = [];
        for(var i = 0;i<arr.length;i++){
            if(new_arr.indexOf(arr[i])==-1){ //判断arr[i]在new_arr中是否存在相同的内容,不存在则push到数组中
                new_arr.push(arr[i])
            }
        }
        console.log(new_arr); //(4) [1, 2, 3, 4]
    

    箭头函数this的指向
    在非箭头函数下,this指向调用其所在的函数对象,而且是离谁近就指向谁(此对于常规对象,原型链,getter&setter等都适用);
    构造函数下,this与被创建的新对象绑定;DOM事件下,this指向触发事件的元素;内联事件分为两种情况,bind绑定,call&apply等方法改变this指向等。而有时this也会指向window;
    所以ES6的箭头函数,修复了原有的this指向问题。
    手写ES6 class继承。
    //定义一个类

       class Children{
            constructor(skin,language){
                this.skin = skin;
                this.language = language;
            }
            say(){
                console.log("I'm a Person")
            }
        }   
        class American extends Children{
            constructor(skin,language){
                super(skin,language)
            }
            aboutMe(){
                console.log(this.skin+" "+this.language)
            }
        }
        var m = new American("张三","中文");
        m.say();
         m.aboutMe();
    

    1)子类没有constructor
    子类American继承父类Person,子类没用定义constructor则默认添加一个,并且在constructor中调用super函数,相当于调用父类的构造函数。调用super函数是为了在子类中获取父类的this,调用之后this指向子类。也就是父类prototype.constructor.call(this)
    2) 子类有constructor
    子类必须在constructor方法中调用super方法,否则new实例时会报错。因为子类没有自己的this对象,而是继承父类的this对象。如果不调用super函数,子类就得不到this对象。super()作为父类的构造函数,只能出现在子类的constructor()中,但是super指向父类的原型对象,可以调用父类的属性和方法。
    const foo = async() => {};

    generator生成器函数:
    Generator(生成器)是ES6标准引入的新数据类型,一个Generator看上去像是一个函数,但可以返回多次。Generator的声明方式类似一般的函数声明,只是多了个*号,并且一般可以在函数内看到yield关键字。
    调用generator对象有两个方法,一是不断的调用generator对象的next()方法,第二个方法是直接用for。。。of循环迭代generator对象。
    每调用一次next,则执行一次yield语句,并在该处暂停。return完成后退出生成器函数,后续如果还有yield操作就不再执行了。

    <script>
    function * showWords(){
    yield "one";
    yield "two";
    return 'three'
    }
    var show = showWords();
    console.log(show.next()); //{value: "one", done: false}
    console.log(show.next()); //{value: "two", done: false}
    console.log(show.next()); //{value: "three", done: true}
    console.log(show.next()); //{value: "undefined", done: true}
    </script>
    

    yield和yield*

    //yield
    function * showWords(){
    yield "one";
    yield showNumber();
    return 'three';
    }
    // yield *
    function * showWords2(){
    yield "one";
    yield* showNumber(2,3);
    return 'three';
    }
    function * showNumber(a,b){
    yield a+b;
    yield a*b;
    }
    var show = showWords();
    console.log(show.next()); //{value: "one", done: false}
    console.log(show.next()); //{value: "showNumber", done: false}
    console.log(show.next()); //{value: "three", done: true}
    var show2 = showWords2();
    console.log(show2.next()); //{value: "one", done: false}
    console.log(show2.next()); //{value: 5, done: false}
    console.log(show2.next()); //{value: 6, done: true}
    console.log(show2.next()); //{value: "three", done: true}
    

    什么是async/await及其如何工作?
    async/await是JS中编写异步或非阻塞代码的新方式。它建立在Promises之上, 让异步代码的可读性和简洁度都更高。async关键声明函数会隐式返回一个Promise。await关键字只能在async function中使用,在仁和非async function的函数中使用await关键字都会抛出错误。await关键字在执行下一行代码之前等待右侧表达式(可能是一个Promise)返回
    async函数的基本用法:
    asyn函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。由于async函数返回的是Promise对象,可以作为await命令的参数。
    ES6 async函数有多种使用形式:
    //函数声明

    async function foo(){}
    

    //函数表达式

    const foo = async function(){}
    

    //对象的方法

    let obj = {async foo()}
    obj.foo().then(...)
    

    //class方法

    class Storage{
    	constructor(){
    	this.cachePromise = cache.open("avatars")
    	}
    	async getAvatar(name){
    	const cache = await this.cachePromise;
    	return cache.match(`/avatars/${name}.jpg`)
    	}
    }
    

    //箭头函数

    async与generator的区别?
    async函数是Generator函数的语法糖,async函数就是将Generator函数的星号(*)替换成async,将yield替换成await。
    async函数对Generator函数的改进,体现在以下四点:

    1. 内置执行器
    2. 更好的语义:async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
    3. 更广的适用性:async函数的awai他命令后面,可以是Promise对象和原始类型的值(数字、字符串和布尔值,但这时等同于同步操作)
    4. 返回值是Promise:async函数的返回值是Peomise对象,这比Generator函数的返回值是Iterator对象方便对了,可以用then方法指定下一步的操作。

    进一步说,async函数完全可以看作多个异步操作包装成的一个Promise对象,而await命令就是内部then命令的语法糖
    简单实现async/await中的async函数
    async函数的实现原理,就是将Generator函数和自动执行器,包装在一个函数里

    function spawn(genF){
    return new Promise(function(resolve,reject){
    const gen = genF();
    function step(nextF){
    let next;
    //ry/catch/finally 语句用于处理代码中可能出现的错误信息。
    try {
    	next = nextF()
    		}catch(e){
    			return reject(e);
    		}
    		if(next .done){
    			return resolve(next.value);
    		}
    		Promise.resolve(next.value).then(
    			function(v){
    				step(function(){
    					return gen.next(v)
    					});
    				},
    				function(v){
    					step(function(){
    						return gen.throw(e)
    					});
    				}
    			)
    		}
    		step(function(){
    		return gen.next(undefined);
    		});
    	})
    }
    

    有用过promise吗?请写出下列代码的执行结果,并写出你的理解思路:

    setTimeout(()=>{
            console.log(1);
    }, 0);
    new Promise((resolve)=>{
            console.log(2);
            for(var i = 1; i < 200; i++){
                    i = 198 && resolve();
            }
            console.log(3);
    }).then(()=>{
            console.log(4);
    });console.log(5);
    

    //想理解promise就得知道js的执行顺序,这里顺便讲一下同步异步
    首先要讲一下,js是单线程执行,那么代码的执行就有先后;
    有先后,那就要有规则(排队),不然就乱套了,那么如何分先后呢?大体分两种:同步、异步;
    同步很好理解,就不用多说了(我就是老大,你们都要给我让路);

    异步(定时器[setTimeout ,setInterval]、事件、ajax、promise等),说到异步又要细分宏任务、微任务两种机制,

    宏任务:js异步执行过程中遇到宏任务,就先执行宏任务,将宏任务加入执行的队列(event queue),然后再去执行微任务;

    微任务:js异步执行过程中遇到微任务,也会将任务加入执行的队列(event queue),但是注意这两个queue身份是不一样的,不是你先进来,就你先出去的(就像宫里的皇上选妃侍寝一样,不是你先进宫(或先来排队)就先宠幸的 ),真执行的时候是先微任务里拿对应回调函数,然后才轮到宏任务的队列回调执行的;
    理解了这个顺序,那上面的结果也就不难懂了。
    Object.is()与原来的比较操作符===,==的区别?
    ==相等运算符,比较时会自动进行数据类型转换

    ===严格相等运算符,比较时不进行隐式类型转换

    Object.is同值相等算法,在===基础上对0和NaN特别处理
    +0 === -0 //true
    NaN === NaN // false
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true

    介绍一下Set、Map、WeakSet和WeakMap的区别?
    Set: 成员不能重复
    只有键值,没有键名
    可以遍历,方法有add,delete,has
    WeakSet:成员都是对象
    成员都是弱引用,随时可以消失,可以用来保存DOM节点,不容易造成内存泄漏
    不能遍历,方法有add,delete ,has
    Map: 本质上是键值对的集合,类似集合
    可以遍历,方法很多,可以跟各种数据格式转换
    WeakMap:只接受对象作为键名(null除外),不接受其他类型的值作为键名
    键名所指向的对象,不计入垃圾回收机制
    不能遍历,方法有get、set、has、delete

    ES5的继承和ES6的继承有什么区别?
    ES5的继承实质上是先创建子类的实例对象, 然后再将父类的方法添加到this上(Parent.apply(this))
    ES6继承机智完全不同, 实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法), 然后再用子类的构造函数修改this
    具体的: ES6通过class关键字定义类, 里面有构造方法, 类之间通过extends关键字实现继承。子类必须在constructor方法中哄调用super方法,否则新建实例报错。 因为子类没有自己的this对象,而是继承了父类的this对象, 然后对其进行加工。如果不调用super方法, 子类得不到this对象
    PS:super关键字指代父类的实例,即父类的this对象。在子类构造函数中, 调用super后,才可使用this关键字,否则报错

    ES5的继承:

    1. 原型链继承
    2. 构造函数继承
    3. 组合继承
    4. 寄生组合继承

    ES6的extends继承

    super关键字的使用, 新增的静态字段使用, 支持对原生构造函数的继承, 对object继承的差异

    ES6 class 的new实例和ES5的new实例有什么区别?
    相同点:
    1、 实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)
    2、类的所有实例共享一个原型对象
    不同点:
    1、 ES6用class进行实例和普通构造函数进行实例
    2、lass不存在变量提升
    3、ES6为new命令引入了一个new.target属性(在狗仔函数中) 但会new命令作用域的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined

    ES6新特性详细介绍说明:
    变量声明:const和let:
    ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部),而let不会将声明变量提前;
    let表示声明变量,而const表示声明常量,两者都为块级作用域;const声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了。
    注意:let关键词声明的变量不具备变量提升的特性
    const和let声明只在最高进的一个块中(花括号内)有效
    const在声明时必须被赋值
    箭头函数:
    箭头函数是函数的一种简写形式,使用括号包裹参数,跟随一个=>,紧接着是函数体
    箭头函数最直观的三个特点:
    不需要function关键字来创造
    省略return关键字
    修复了this指向
    类和继承:
    class和extend是一种语法糖,也是基于原型继承实现的
    class和super calls,实例化,静态方法和构造函数

    <script>
            //class声明类 内部直接是属性和方法 不用,分隔。 constructor
            class Person{
                constructor(name, age){
                    this.name = name;//实例属性
                    this.age = age;
                    console.log(name,age)
                }
                sayhi(name){
                    //使用ES6新特性字符串模块,注意外层包裹符号是``反引号来创建字符串,内部变量使用${}
                    console.log(`this name is ${this.name}`);
                    //以往的写法
                    console.log('my age is '+this.age)
                }
            }
            
     
            class Programmer extends Person{
                constructor(name,age){
                    //直接调用父类结构器进行初始化
                    super(name,age)
                }
                program(){
                    console.log("I'm coding...")
                }
            }
            var anim = new Person("张三",18);
            anim.sayhi();
            var wayou = new Programmer("李四",20);
            wayou.sayhi();
            wayou.program();
      </script>
    

    字符串模板:
    ES6中允许使用反引号` 来创建字符串,此方法创建的字符串里面可以包含由${ }包裹的变量

    <script>
        //产生一个随机数
        var num = Math.random();
        //将这个数字输出到console
        console.log(`输出随机数${num}`);
    </script>
    

    增强的对象字面量:
    对象字面量被增强了,写法更加简洁与玲姐,同时在定义对象的时候能够做的事情更多了。具体表现在:

    1. 可以在对象子里面量里面定义原型
      
    2. 定义方法可以不用function关键词
      
    3. 直接调用父类方法
      
    <script>
        var human = {
            breathe(){
                console.log('breathing...');
            }
        };
        var worker = {
            __proto__:human, //设置此对象的原型为human,想党羽继承human
            company:'freelancer',
            work(){
                console.log("working..")
            }
        }
        human.breathe();
        //调用继承来的breathe方法
        worker.breathe();
    </script>
    

    解构:

    </script>
    <script>
        var [x,y] = getVal(), //函数返回值解析
            [name, ,age] = ["wayou","male","secrect"]; //数组解析
        function getVal(){
            return [1,2];
        }
        console.log(`x:${x},y:${y}`); //x:1,y:2
        console.log(`name:${name},age:${age}`); //name:wayou,age:secrect
    </script>
    

    参数默认值,不定参数,拓展参数:
    默认参数值:
    可以在定义函数的时候指定参数的默认值,而不用像以前那样通过逻辑或操作符来达到目的了。

    <script>
        //传统方式设置默认方式
        function sayHello(name){
            var name = name||'dude';
            console.log("Hello "+name);
        }
        sayHello(); //Hello dude
        sayHello("wayou"); //Hello wayou
     
        
        //ES6的默认参数
        function sayHello2(name = "dude"){
            console.log("Hello "+name)
        }
        sayHello2(); //Hello dude
        sayHello2("wayou"); //Hello wayou
    </script>
    

    不定参数(拓展符):
    不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。这只是一种语法糖,在以前的JavaScript代码中我们可以通过arguments变量来达到这一目的。不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如下面这个例子中,…x代表了所有传入add函数的参数。

    <script>
        //将所有参数想加的函数
        function add(...x){
            return x.reduce((m,n)=>m+n);
        }
        //传递任意个数的参数
        console.log(add(1,2,3)); //输出:6
        console.log(add(1,2,3,4,5)); //输出:15
    </script>
    

    reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
    reduce()可以作为一个高阶函数,用于函数的compose
    注意:reduce()对于空数组是不会执行回调函数的
    拓展符:将一个数组转为用逗号分隔的参数序列。(若数组为空不产生任何效果)

    <script>
        var x = [1,2,3,4,5,6];
        console.log(x); //(6) [1, 2, 3, 4, 5, 6]
        console.log(...x); //1 2 3 4 5 6
    </script>
    

    拓展参数:
    拓展参数则是另一种形式的语法糖,它允许传递数组或类数组直接作为函数的参数而不用通过apply。

    <script>
        var people = ['wayou','john','sherlock'];
        //sayHello函数来接收三个单独的参数
        function sayHello(people1,people2,people3){
            console.log(`Hello ${people1},${people2},${people3}`)
        }
        //以前的方式,如果需要传递数组当参数,我们需要使用函数apply方法
        sayHello.apply(null,people); //Hello wayou,john,sherlock
        sayHello(...people); //Hello wayou,john,sherlock
    </script>
    

    for of值遍历:
    for in循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,不同的是每次循环他提供的不是序号而是值

    <script>
        var someArray = ['a','b','c'];
        for(v of someArray){
            console.log(v); //a,b,c
        }
    </script>
    

    iterator/generator:
    iterator:它是这么一个对象,拥有一个next方法,这个方法返回一个对象{done,value},这个对象包含两个属性,一个布尔类型的done和包含任意值的value。
    iterable:这是这么一个对象,拥有一个obj[@@iterator]方法,这个方法返回一个iterator
    generator:它是一个特殊的iterator。反的next方法可以接收一个参数并且返回值取决于它的构造函数(generator function)。generator同时拥有一个throw方法。
    generator番薯:即generator的构造函数。此函数内可以使用yield关键字,在yield出现的地方可以通过generator的next或throw方法向外界传递值。generator函数是通过function*来声明的。
    yield关键字:它可以暂停函数的执行,随后可以再进入函数继续执行
    具体详情:https://blog.domenic.me/es6-iterators-generators-and-iterables/
    模块:
    在ES6标准中,Javascript原生支持module了。这种将JS代码分割成不同功能的小块进行模块化的概念是在一些三方规范中流行起来的,比如CommonJS和AMD模式。
    将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入方式可以在其他地方使用。

    <script>
        //单独的js文件,如:point.js
        module "point" {
            export class Point {
                constructor (x,y){
                    publice x = x;
                    publice y = y;
                }
            }
        }
     
        //在需要引用模块的js文件内
        //声明引用的模块
        module point from './point.js';
        //这里可以看出,尽管声明了引用的模块,还是可以通过指定需要的部分进行导入
        import Point from "point"
        var origin = new Ponit(0,0);
        console.log(origin)
    </script>
    

    Map、Set和WeakMap、WeakSet
    这些是新加的集合类型,提供了更加方便的获取属性值的方法,不用像以前一样用hasOwnProperty来检查某个属性是属于原型链上的还是当前对象的。同时,在进行属性值添加与获取时有专门的get、set方法。

    //Sets
        var s = new Set();
        s.add("hello").add("goodbye").add("hello");
        s.size === 2;
        s.has("hello")===true;
        //Maps
        var m = new Map();
        m.set("hello",42);
        m.set(s,34);
        m.get(s) === 34;
    

    我们会把对象作为一个对象的键来存放属性值,普通集合类型比如简单对象会阻止垃圾回收器对这些作为属性键存在的对象回收,偶造成内存泄露的危险。而weakMap,weakSet则更加安全些,这些作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉,具体还看下面的例子。

    //weak Maps
        var wm = new WeakMap();
        wm.set(s,{eatra:42});
        wm.size === undefined;
     
        //weak Sets
        var ws = new WeakSet();
        ws.add({data:42}); //因为添加到ws的这个临时对象没有其他变量引用它,所以ws不会保存它的值,也就是说这次添加其实没有意思
    

    Proxies
    proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。一下子让我们对一个对象有了很强的跟踪能力,同时咋数据绑定方面也很有用处。

    <script>
        //定义被侦听的目标对象
        var engineer = {name:"Join",salary:50};
        //定义处理程序
        var interceptor = {
            set:function(receiver,property,value){
                console.log(property,"is changed to",value);
                receiver[property] = value;
            }
        }
        //创建代理以进行侦听
        engineer = new Proxy(engineer,interceptor);
        //做一些改动来触发代理
        engineer.salary = 60; //控制台输出:salary is changed to 60
    </script>
    

    上面代码,我们已经添加了注释,这里进一步解释。对于处理程序,是在被侦听的对象身上发生了相应事件之后,处理程序里面的方法会被调用,上面例子中我们设置了set的处理函数,表明。如果我们侦听对象的属性被更改,也就是被set了,那这个处理程序就会被调用,同时通过参数能够的值是哪个属性被更改,更改为什么值
    symbols
    symbols是一种基本类型,像数字、字符串还有布尔一样。它不是一个对象。symbols通过调用symbol函数产生,接收一个可选的名字参数,该函数返回的symbol是唯一的。之后就可以用这个返回值作为对象的键了。symbol还可以用来创建私有属性,外部无法直接访问偶symbol作为键的属性值。

    <script>
        var MyClass = (function() {
            // module scoped symbol
            var key = Symbol("key");
            function MyClass(privateData) {
                this[key] = privateData;
            }
            MyClass.prototype = {
                doStuff: function() {
                    this[key]
                }
            };
            return MyClass;
        })();
        var c = new MyClass("hello")
        console.log(c["key"] === undefined); //true,无法访问该属性,因为是私有的
    </script>
    

    Math、Number、String、Object的新API
    对Math、Number、String还有Object等添加了许多新的API。
    Promises
    Promise是处理异步操作的一种模式,之前在很多三方库中有实现,比如JQuery的deferred对象。当你发起一个异步请求,并绑定了.when()/.done()等事件处理程序时,其实就是在应用promise模式。

     <script>
        //创建Promise
        var promise = new Promise(function(resolve,reject){
            //进行一些异步或耗时操作
            if(/*如果成功*/){
                resolve("stuff worked")
            }else{
                reject(Error("It broke"));
            }
        });
        //绑定处理程序
        promise.then(function(result){
            //promise成功的话会执行这里
            console.log(result); //"stuff worked"`在这里插入代码片`
        },function(err){
            //promise处理失败会执行这里
            console.log(err); //Error:"It broke"
        });
    </script>
    
    展开全文
  • 前端ES6面试题

    万次阅读 多人点赞 2019-06-15 14:43:04
    1、ES5、ES6和ES2015有什么区别? ES2015特指在2015年发布的新一代JS语言标准,ES6泛指下一代JS语言标准,包含ES2015、ES2016、ES2017、ES2018等。现阶段在绝大部分场景下,ES2015默认等同ES6。ES5泛指上一代语言...

    1、ES5、ES6和ES2015有什么区别?

    ES2015特指在2015年发布的新一代JS语言标准,ES6泛指下一代JS语言标准,包含ES2015ES2016ES2017ES2018等。现阶段在绝大部分场景下,ES2015默认等同ES6ES5泛指上一代语言标准。ES2015可以理解为ES5ES6的时间分界线

    2、babel是什么,有什么作用?

    babel是一个 ES6 转码器,可以将 ES6 代码转为 ES5 代码,以便兼容那些还没支持ES6的平台

    3、let有什么用,有了var为什么还要用let?

    ES6之前,声明变量只能用varvar方式声明变量其实是很不合理的,准确的说,是因为ES5里面没有块级作用域是很不合理的。没有块级作用域回来带很多难以理解的问题,比如for循环var变量泄露,变量覆盖等问题。let声明的变量拥有自己的块级作用域,且修复了var声明变量带来的变量提升问题。

    4、举一些ES6对String字符串类型做的常用升级优化?

    优化部分

    ES6新增了字符串模板,在拼接大段字符串时,用反斜杠()`取代以往的字符串相加的形式,能保留所有空格和换行,使得字符串拼接看起来更加直观,更加优雅

    升级部分

    ES6String原型上新增了includes()方法,用于取代传统的只能用indexOf查找包含字符的方法(indexOf返回-1表示没查到不如includes方法返回false更明确,语义更清晰), 此外还新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用于查找,补全字符串

    5、举一些ES6对Array数组类型做的常用升级优化

    优化部分

    • 数组解构赋值。ES6可以直接以let [a,b,c] = [1,2,3]形式进行变量赋值,在声明较多变量时,不用再写很多let(var),且映射关系清晰,且支持赋默认值
    • 扩展运算符。ES6新增的扩展运算符(...)(重要),可以轻松的实现数组和松散序列的相互转化,可以取代arguments对象和apply方法,轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments并不是一个真正的数组,而是一个类数组的对象,但是扩展运算符的逆运算却可以返回一个真正的数组)。扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = [...a]

    升级部分

    ES6Array原型上新增了find()方法,用于取代传统的只能用indexOf查找包含数组项目的方法,且修复了indexOf查找不到NaN的bug([NaN].indexOf(NaN) === -1).此外还新增了copyWithin(),includes(), fill(),flat()等方法,可方便的用于字符串的查找,补全,转换等

    6、举一些ES6对Number数字类型做的常用升级优化

    优化部分

    ES6在Number原型上新增了isFinite(), isNaN()方法,用来取代传统的全局isFinite(), isNaN()方法检测数值是否有限、是否是NaNES5isFinite(), isNaN()方法都会先将非数值类型的参数转化为Number类型再做判断,这其实是不合理的,最造成isNaN('NaN') === true的奇怪行为--'NaN'是一个字符串,但是isNaN却说这就是NaN。而Number.isFinite()Number.isNaN()则不会有此类问题(Number.isNaN('NaN') === false)。(isFinite()同上)

    升级部分

    ES6Math对象上新增了Math.cbrt()trunc()hypot()等等较多的科学计数法运算方法,可以更加全面的进行立方根、求和立方根等等科学计算

    7、举一些ES6对Object类型做的常用升级优化?(重要)

    优化部分

    对象属性变量式声明。ES6可以直接以变量形式声明对象属性或者方法,。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰

    let [apple, orange] = ['red appe', 'yellow orange'];
    let myFruits = {apple, orange};    // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
    

    尤其在对象解构赋值(见优化部分b.)或者模块输出变量时,这种写法的好处体现的最为明显

    let {keys, values, entries} = Object;
    let MyOwnMethods = {keys, values, entries}; // let MyOwnMethods = {keys: keys, values: values, entries: entries}
    

    可以看到属性变量式声明属性看起来更加简洁明了。方法也可以采用简洁写法

    let es5Fun = {
        method: function(){}
    }; 
    let es6Fun = {
        method(){}
    }
    

    对象的解构赋值。 ES6对象也可以像数组解构赋值那样,进行变量的解构赋值

    let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
    

    对象的扩展运算符(...)。 ES6对象的扩展运算符和数组扩展运算符用法本质上差别不大,毕竟数组也就是特殊的对象。对象的扩展运算符一个最常用也最好用的用处就在于可以轻松的取出一个目标对象内部全部或者部分的可遍历属性,从而进行对象的合并和分解

    let {apple, orange, ...otherFruits} = {apple: 'red apple', orange: 'yellow orange', grape: 'purple grape', peach: 'sweet peach'}; 
    // otherFruits  {grape: 'purple grape', peach: 'sweet peach'}
    // 注意: 对象的扩展运算符用在解构赋值时,扩展运算符只能用在最有一个参数(otherFruits后面不能再跟其他参数)
    let moreFruits = {watermelon: 'nice watermelon'};
    let allFruits = {apple, orange, ...otherFruits, ...moreFruits};
    

    super 关键字。ES6Class类里新增了类似this的关键字super。同this总是指向当前函数所在的对象不同,super关键字总是指向当前函数所在对象的原型对象

    升级部分

    ES6Object原型上新增了is()方法,做两个目标对象的相等比较,用来完善'==='方法。'==='方法中NaN === NaN //false其实是不合理的,Object.is修复了这个小bug(Object.is(NaN, NaN) // true)

    ES6Object原型上新增了assign()方法,用于对象新增属性或者多个对象合并

    const target = { a: 1 };
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    

    注意: assign合并的对象target只能合并source1、source2中的自身属性,并不会合并source1source2中的继承属性,也不会合并不可枚举的属性,且无法正确复制get和set属性(会直接执行get/set函数,取return的值)

    • ES6Object原型上新增了getOwnPropertyDescriptors()方法,此方法增强了ES5getOwnPropertyDescriptor()方法,可以获取指定对象所有自身属性的描述对象。结合defineProperties()方法,可以完美复制对象,包括复制getset属性
    • ES6Object原型上新增了getPrototypeOf()setPrototypeOf()方法,用来获取或设置当前对象的prototype对象。这个方法存在的意义在于,ES5中获取设置prototype对像是通过__proto__属性来实现的,然而__proto__属性并不是ES规范中的明文规定的属性,只是浏览器各大产商“私自”加上去的属性,只不过因为适用范围广而被默认使用了,再非浏览器环境中并不一定就可以使用,所以为了稳妥起见,获取或设置当前对象的prototype对象时,都应该采用ES6新增的标准用法
    • ES6Object原型上还新增了Object.keys()Object.values()Object.entries()方法,用来获取对象的所有键、所有值和所有键值对数组

    8、举一些ES6对Function函数类型做的常用升级优化?

    优化部分

    箭头函数(核心)。箭头函数是ES6核心的升级项之一,箭头函数里没有自己的this,这改变了以往JS函数中最让人难以理解的this运行机制。主要优化点

    • 箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。ES5函数里的this总是指向函数执行时所在的对象,这使得在很多情况下this的指向变得很难理解,尤其是非严格模式情况下,this有时候会指向全局对象,这甚至也可以归结为语言层面的bug之一。ES6的箭头函数优化了这一点,它的内部没有自己的this,这也就导致了this总是指向上一层的this,如果上一层还是箭头函数,则继续向上指,直到指向到有自己this的函数为止,并作为自己的this
    • 箭头函数不能用作构造函数,因为它没有自己的this,无法实例化
    • 也是因为箭头函数没有自己的this,所以箭头函数 内也不存在arguments对象。(可以用扩展运算符代替)
    • 函数默认赋值。ES6之前,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。ES6以更简洁更明确的方式进行函数默认赋值
    function es6Fuc (x, y = 'default') {
        console.log(x, y);
    }
    es6Fuc(4) // 4, default
    

    升级部分

    ES6新增了双冒号运算符,用来取代以往的bindcall,和apply。(浏览器暂不支持,Babel已经支持转码)

    foo::bar;
    // 等同于
    bar.bind(foo);
    
    foo::bar(...arguments);
    // 等同于
    bar.apply(foo, arguments);
    

    9、Symbol是什么,有什么作用?

    SymbolES6引入的第七种原始数据类型(说法不准确,应该是第七种数据类型,Object不是原始数据类型之一,已更正),所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。对象中Symbol()属性不能被for...in遍历,但是也不是私有属性

    10、Set是什么,有什么作用?

    SetES6引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item成员,区别是Set实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重

    11、Map是什么,有什么作用?

    MapES6引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object。可以更加全面的描述对象的属性

    12、Proxy是什么,有什么作用?

    ProxyES6新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得JS的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的get/set方法,可以轻松地定制自己想要的key或者value。下面的例子可以看到,随便定义一个myOwnObjkey,都可以变成自己想要的函数`

    function createMyOwnObj() {
    	//想把所有的key都变成函数,或者Promise,或者anything
    	return new Proxy({}, {
    		get(target, propKey, receiver) {
    			return new Promise((resolve, reject) => {
    				setTimeout(() => {
    					let randomBoolean = Math.random() > 0.5;
    					let Message;
    					if (randomBoolean) {
    						Message = `你的${propKey}运气不错,成功了`;
    						resolve(Message);
    					} else {
    						Message = `你的${propKey}运气不行,失败了`;
    						reject(Message);
    					}
    				}, 1000);
    			});
    		}
    	});
    }
    
    let myOwnObj = createMyOwnObj();
    
    myOwnObj.hahaha.then(result => {
    	console.log(result) //你的hahaha运气不错,成功了
    }).catch(error => {
    	console.log(error) //你的hahaha运气不行,失败了
    })
    
    myOwnObj.wuwuwu.then(result => {
    	console.log(result) //你的wuwuwu运气不错,成功了
    }).catch(error => {
    	console.log(error) //你的wuwuwu运气不行,失败了
    })
    
    

    13、Reflect是什么,有什么作用?

    ReflectES6引入的一个新的对象,他的主要作用有两点,一是将原生的一些零散分布在ObjectFunction或者全局函数里的方法(如applydeletegetset等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API

    14、Promise是什么,有什么作用?

    PromiseES6引入的一个新的对象,他的主要作用是用来解决JS异步机制里,回调机制产生的“回调地狱”。它并不是什么突破性的API,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用

    15、Iterator是什么,有什么作用?(重要)

    • IteratorES6中一个很重要概念,它并不是对象,也不是任何一种数据类型。因为ES6新增了SetMap类型,他们和ArrayObject类型很像,ArrayObject都是可以遍历的,但是SetMap都不能用for循环遍历,解决这个问题有两种方案,一种是为SetMap单独新增一个用来遍历的API,另一种是为SetMapArrayObject新增一个统一的遍历API,显然,第二种更好,ES6也就顺其自然的需要一种设计标准,来统一所有可遍历类型的遍历方式。Iterator正是这样一种标准。或者说是一种规范理念
    • 就好像JavaScriptECMAScript标准的一种具体实现一样,Iterator标准的具体实现是Iterator遍历器。Iterator标准规定,所有部署了key值为[Symbol.iterator],且[Symbol.iterator]value是标准的Iterator接口函数(标准的Iterator接口函数: 该函数必须返回一个对象,且对象中包含next方法,且执行next()能返回包含value/done属性的Iterator对象)的对象,都称之为可遍历对象,next()后返回的Iterator对象也就是Iterator遍历器
    //obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
    //obj.[Symbol.iterator]() 就是Iterator遍历器
    let obj = {
      data: [ 'hello', 'world' ],
      [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
          next() {
            if (index < self.data.length) {
              return {
                value: self.data[index++],
                done: false
              };
            } else {
              return { value: undefined, done: true };
            }
          }
        };
      }
    };
    
    

    ES6SetMapArrayString都加上了[Symbol.iterator]方法,且[Symbol.iterator]方法函数也符合标准的Iterator接口规范,所以SetMapArrayString默认都是可以遍历的

    //Array
    let array = ['red', 'green', 'blue'];
    array[Symbol.iterator]() //Iterator遍历器
    array[Symbol.iterator]().next() //{value: "red", done: false}
    
    //String
    let string = '1122334455';
    string[Symbol.iterator]() //Iterator遍历器
    string[Symbol.iterator]().next() //{value: "1", done: false}
    
    //set
    let set = new Set(['red', 'green', 'blue']);
    set[Symbol.iterator]() //Iterator遍历器
    set[Symbol.iterator]().next() //{value: "red", done: false}
    
    //Map
    let map = new Map();
    let obj= {map: 'map'};
    map.set(obj, 'mapValue');
    map[Symbol.iterator]().next()  {value: Array(2), done: false}
    
    
    

    16、for…in 和for…of有什么区别?

    如果看到问题十六,那么就很好回答。问题十六提到了ES6统一了遍历标准,制定了可遍历对象,那么用什么方法去遍历呢?答案就是用for...of。ES6规定,有所部署了载了Iterator接口的对象(可遍历对象)都可以通过for...of去遍历,而for..in仅仅可以遍历对象

    • 这也就意味着,数组也可以用for...of遍历,这极大地方便了数组的取值,且避免了很多程序用for..in去遍历数组的恶习

    17、Generator函数是什么,有什么作用?

    • 如果说JavaScriptECMAScript标准的一种具体实现、Iterator遍历器是Iterator的具体实现,那么Generator函数可以说是Iterator接口的具体实现方式。
    • 执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。
    • Generator函数可以通过配合Thunk 函数更轻松更优雅的实现异步编程和控制流管理。

    18、async函数是什么,有什么作用?

    async函数可以理解为内置自动执行器的Generator函数语法糖,它配合ES6Promise近乎完美的实现了异步编程解决方案

    19、Class、extends是什么,有什么作用?

    ES6class可以看作只是一个ES5生成实例对象的构造函数的语法糖。它参考了java语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。Class类可以通过extends实现继承。它和ES5构造函数的不同点

    类的内部定义的所有方法,都是不可枚举的

    ///ES5
    function ES5Fun (x, y) {
    	this.x = x;
    	this.y = y;
    }
    ES5Fun.prototype.toString = function () {
    	 return '(' + this.x + ', ' + this.y + ')';
    }
    var p = new ES5Fun(1, 3);
    p.toString();
    Object.keys(ES5Fun.prototype); //['toString']
    
    //ES6
    class ES6Fun {
    	constructor (x, y) {
    		this.x = x;
    		this.y = y;
    	}
    	toString () {
    		return '(' + this.x + ', ' + this.y + ')';
    	}
    }
    
    Object.keys(ES6Fun.prototype); //[]
    
    
    • ES6class类必须用new命令操作,而ES5的构造函数不用new也可以执行。
    • ES6class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
    • ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

    20、module、export、import是什么,有什么作用?

    • moduleexportimportES6用来统一前端模块化方案的设计思路和实现方案。exportimport的出现统一了前端模块化的实现方案,整合规范了浏览器/服务端的模块化方法,用来取代传统的AMD/CMDrequireJSseaJScommondJS等等一系列前端模块不同的实现方案,使前端模块化更加统一规范,JS也能更加能实现大型的应用程序开发。
    • import引入的模块是静态加载(编译阶段加载)而不是动态加载(运行时加载)。
    • import引入export导出的接口值是动态绑定关系,即通过该接口,可以取到模块内部实时的值

    21、日常前端代码开发中,有哪些值得用ES6去改进的编程优化或者规范?

    • 常用箭头函数来取代var self = this;的做法。
    • 常用let取代var命令。
    • 常用数组/对象的结构赋值来命名变量,结构更清晰,语义更明确,可读性更好。
    • 在长字符串多变量组合场合,用模板字符串来取代字符串累加,能取得更好地效果和阅读体验。
    • Class类取代传统的构造函数,来生成实例化对象。
    • 在大型应用开发中,要保持module模块化开发思维,分清模块之间的关系,常用importexport方法。

    22、ES6的了解

    新增模板字符串(为JavaScript提供了简单的字符串插值功能)、箭头函数(操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs。)、for-of(用来遍历数据—例如数组中的值。)arguments对象可被不定参数和默认参数完美代替。ES6将promise对象纳入规范,提供了原生的Promise对象。增加了let和const命令,用来声明变量。增加了块级作用域。let命令实际上就增加了块级作用域。ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。。还有就是引入module模块的概念

    23、说说你对Promise的理解

    • 依照 Promise/A+ 的定义,Promise 有四种状态:
      • pending: 初始状态, 非 fulfilled 或 rejected.
      • fulfilled: 成功的操作.
      • rejected: 失败的操作.
      • settled: Promise已被fulfilled或rejected,且不是pending
    • 另外, fulfilled 与 rejected 一起合称 settled
    • Promise 对象用来进行延迟(deferred) 和异步(asynchronous ) 计算

    24、Promise 的构造函数

    • 构造一个 Promise,最基本的用法如下:
    var promise = new Promise(function(resolve, reject) {
    
            if (...) {  // succeed
    
                resolve(result);
    
            } else {   // fails
    
                reject(Error(errMessage));
    
            }
        });
    
    
    • Promise 实例拥有 then 方法(具有 then 方法的对象,通常被称为thenable)。它的使用方法如下:
    promise.then(onFulfilled, onRejected)
    
    
    • 接收两个函数作为参数,一个在 fulfilled 的时候被调用,一个在rejected的时候被调用,接收参数就是 future,onFulfilled 对应 resolve, onRejected 对应 reject

    什么是 Promise ?

    • Promise 就是一个对象,用来表示并传递异步操作的最终结果
    • Promise 最主要的交互方式:将回调函数传入 then 方法来获得最终结果或出错原因
    • Promise 代码书写上的表现:以“链式调用”代替回调函数层层嵌套(回调地狱)

    解构赋值:
    只要判定了等号两边的模式相同(解构),左边的变量就会被赋予对应的值(赋值),以前一次给一个变量赋值,解构赋值可以一次性给多个变量进行赋值。

    25、谈一谈你了解ECMAScript6的新特性?

    • 块级作用区域 let a = 1;
    • 可定义常量 const PI = 3.141592654;
    • 变量解构赋值 var [a, b, c] = [1, 2, 3];
    • 字符串的扩展(模板字符串) var sum =${a + b};
    • 数组的扩展(转换数组类型) Array.from($('li'));
    • 函数的扩展(扩展运算符) [1, 2].push(...[3, 4, 5]);
    • 对象的扩展(同值相等算法) Object.is(NaN, NaN);
    • 新增数据类型(Symbol) let uid = Symbol('uid');
    • 新增数据结构(Map) let set = new Set([1, 2, 2, 3]);
    • for…of循环 for(let val of arr){};
    • Promise对象 var promise = new Promise(func);
    • Generator函数 function* foo(x){yield x; return x*x;}
    • 引入Class(类) class Foo {}
    • 引入模块体系 export default func;
    • 引入async函数[ES7]
    async function asyncPrint(value, ms) {
          await timeout(ms);
          console.log(value)
         }
         
    
    

    26、Object.is() 与原来的比较操作符 = 的区别?

    • == 相等运算符,比较时会自动进行数据类型转换
    • === 严格相等运算符,比较时不进行隐式类型转换
    • Object.is 同值相等算法,在 === 基础上对 0 和 NaN 特别处理
    +0 === -0 //true
    NaN === NaN // false
    
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
    
    

    27、什么是 Babel

    • Babel 是一个 JS 编译器,自带一组 ES6 语法转化器,用于转化 JS 代码。
      这些转化器让开发者提前使用最新的 JS语法(ES6/ES7),而不用等浏览器全部兼容。
    • Babel 默认只转换新的 JS 句法(syntax),而不转换新的API。
    展开全文
  • es6 class、module、generator/ promise/ async/ await、let/ const、解构赋值、块级作用域、函数默认参数、箭头函数、set数据结构、es6基础 class module generator/ promise/ async/ await let/ const ...

    es6
    class、module、generator/ promise/ async/ await、let/ const、解构赋值、块级作用域、函数默认参数、箭头函数、set数据结构、es6基础


    • class

    1、class语法相对原型、构造函数、继承更接近传统语法,它的写法能够让对象原型的写法更加清晰、面向对象编程的语法更加通俗

    class和普通构造函数有何区别?

    1、js构造函数

    function MathHandle(x, y) { // 构造函数
    	this.x = x;
    	this.y = y;
    }
    MathHandle.prototype.add = function() { // 原型的一个扩展
    	return this.x + this.y;
    }
    var m = new MathHandle(1, 2); // new一个实例
    console.log(m.add())
    

    2、class基本语法

    class MathHandler{ // MathHandler是一个构造函数,构造函数都有显式原型:prototype
    	constructor(x, y) { // 构造器,java,c#的语法,面向对象高级语言的语法
    		this.x = x;
    		this.y = y;
    	}
    	add() {
    		return this.x + this.y;
    	}
    }
    const m = new MathHandle(1, 2); // 实例,所有实例都会有一个隐式原型:__proto__
    console.log(m.add());
    
    typeof MathHandle  // 'function'
    MathHandle.prototype.constructor === MathHandle; // true
    m.__proto__ === MathHandle.prototype; // true
    

    3、语法糖
    class本身就是一个语法糖
    4、继承
    (1)低级构造函数的原型,赋值成高级构造函数的实例
    (2)

    function Animal(){this.eat = function() {console.log('animal eat')}}
    function Dog(){this.bark = function() {console.log('dog dark')}}
    Dog.prototype = new Animal(); // 绑定原型,实现继承
    var hashiqi = new Dog();
    

    class的继承

    class Animal() {
    	constructor(name) {
    		this.name = name;
    	}
    	eat() {
    		alert(this.name + 'eat);
    	}
    }
    class Dog extends Animal{
    	constructor(name) {
    		super(name); // 如果有class,extends,则要用super(),super是被继承的class的constructor
    		this.name = name;
    	}
    	eat() {
    		alert(this.name + 'eat);
    	}
    }
    

    module

    common.js 和 es6 中模块引入的区别?[字节跳动]

    CommonJS 是一种模块规范,最初被应用于 Nodejs,成为 Nodejs 的模块规范。运行在浏览器端的 JavaScript 由于也缺少类似的规范,在 ES6 出来之前,前端也实现了一套相同的模块规范 (例如: AMD),用来对前端模块进行管理。自 ES6 起,引入了一套新的 ES6 Module 规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export 和 import,会经过 Babel 转换为 CommonJS 规范。在使用上的差别主要有:
    CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
    CommonJs 是单个值导出,ES6 Module可以导出多个
    CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
    CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined


    generator、promise、async/await

    • generator

    Generator原理?

    待完善

    • promise

    1、promise 有 3 种状态:pending(进行中)、fulfilled(已完成,又称为Resolved) 或 rejected(已失败)。状态改变只能是 pending->fulfilled 或者 pending->rejected,状态一旦改变则不能再变。
    2、new promise实例,要return
    3、new promise时要传入函数,函数有resolve、reject两个参数
    4、成功时执行resolve(),失败时执行reject()
    5、then监听结果

    手写一个promise?

    var promise = new Promise((resolve, reject) => {
    	if (操作成功) {
    		resolve(value);
    	} else {
    		retject(error);
    	}
    });
    promise.then(function(value) {
    	// success
    }, function(value) {
    	// failure
    })
    

    jQuery的ajax返回的是promise对象吗?

    jquery的ajax返回的是deferred对象,通过promise的resolve()方法将其转换为promise对象。
    var jsPromise = Promise.resolve($.ajax('/whatever.json'));

    分析下列程序代码,得出运行结果,解释其原因?

    const promise = new Promise((resolve, reject) => {
      resolve('success1')
      reject('error')
      resolve('success2')
    })
    
    promise
      .then((res) => {
        console.log('then: ', res)
      })
      .catch((err) => {
        console.log('catch: ', err)
      })
    

    then:success1
    构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用。因为promise 状态一旦改变则不能再变。

    分析下列程序代码,得出运行结果,解释其原因?

    Promise.resolve(1)
      .then(2)
      .then(Promise.resolve(3))
      .then(console.log)
    

    1
    .then 或者.catch 的参数期望是函数,传入非函数则会发生值穿透。

    分析下列程序代码,得出运行结果,解释其原因?

    Promise.resolve()
      .then(() => {
        return new Error('error!!!')
      })
      .then((res) => {
        console.log('then: ', res)
      })
      .catch((err) => {
        console.log('catch: ', err)
      })
    

    then: Error: error!!!
    1、.then或者.catch中return一个error对象并不会抛出错误,所以不会被后续的.catch捕获,需要改成其中的一种:
    return Promise.reject(new Error('error!!!'))
    throw new Error('error!!!')
    2、因为返回任意一个非promise的值都会被包裹成promise对象,即return new Error('error!!!') 等价于return Promise.resolve(new Error(‘error!!!’))

    分析下列程序代码,得出运行结果,解释其原因?

    Promise.resolve()
      .then(function success (res) {
        throw new Error('error')
      }, function fail1 (e) {
        console.error('fail1: ', e)
      })
      .catch(function fail2 (e) {
        console.error('fail2: ', e)
      })
    

    fail2: Error: error
    1、.then可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch是.then第二个参数的简便写法。它们在用法上有一点需要注意:.then的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的.catch可以捕获之前的错误。

    分析下列程序代码,得出运行结果,解释其原因?

    process.nextTick(() => {
      console.log('nextTick')
    })
    Promise.resolve()
      .then(() => {
        console.log('then')
      })
    setImmediate(() => {
      console.log('setImmediate')
    })
    console.log('end')
    

    end
    nextTick
    then
    setImmediate

    1、process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

    • async、await

    1、then只是将callback拆分了。
    2、async/await是最直接的同步写法

    import 'babel-polyfill';
    function load
    const load = async function() {
    	const result1 = await loadImg(src1);
    	console.log(result1);
    	const result2 = swait loadImg(src2);
    	console.log(result2);
    }
    load();
    

    ①使用await,函数必须用async标识
    ②await后面跟的是一个promise实例
    ③需要babel-polyfill

    generator, asynce/await的关系?

    待完善


    • let、const

    1、es5有两种声明变量的方法:var, function
    es6有6种声明变量的方法:var, function, let, const, import, class

    let、var的区别?

    1、块级作用域:let定义的变量有块级作用域,var声明的变量只有全局和函数作用域。
    2、变量提升:let不存在变量提升,var存在变量提升。
    3、重复声明:let不允许重复声明,var可以重复声明。
    4、暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。

      var tmp = 123;
    
      if (true) {
        tmp = 'abc'; // 执行报错:Uncaught ReferenceError: tmp is not defined
        let tmp;
      }
    

    隐蔽的死区

    function bar(x = y, y = 2) {
      return [x, y];
    }
    bar(); // 报错
    

    • 解构赋值

    请选择正确的一项?

    // A
     let {foo} = {bar: 'bar'};
     // B
     let {foo: {bar}} = {bar: 'bar'};
     // C
     let {foo, bar} = {foo: 'aaa', bar: 'bbb'};
     // D
     let {foo: baz} = {foo: 'aaa', bar: 'bbb'};
    

    B
    1、B选项报错:Uncaught TypeError: Cannot destructure property 'bar' of 'undefined' or 'null'.
    应改成:let {foo: {bar}} = {foo:{bar: 'bar'}};;


    • 块级作用域


    • 函数默认参数


    • 箭头函数

    1、在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象;
    2、不能够用作构造函数,这就是说,不能够使用new命令,否则会抛出一个错误。
    3、不能够使用arguments对象。
    4、不能使用yield命令。
    5、当要求动态上下文的时候,就不能够使用箭头函数,也就是this的固定化。

    写出下列的值?

      class Animal {
        constructor() {
          this.type = 'Animal';
        }
        say(val) {
          setTimeout(function() {
            console.log(this);
            console.log(`${this.type} say ${val}`);
          }, 200);
        }
      }
      var animal = new Animal();
      animal.say('hi');
    

    window
    undefined says hi
    1、《javaScript高级程序设计》第二版中,写到“超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下是undefined。也就是说在非严格模式下,setTimeout中所执行函数中的this,永远指向window”。
    2、箭头函数

      class Animal {
        constructor() {
          this.type = 'Animal';
        }
        say(val){
          setTimeout(() => {
            console.log(this);
            console.log(`${this.type} say ${val}`);
          }, 200);
        }
      }
      var animal = new Animal();
      animal.say('hi');
    

    (1)特点:
    ①不需要function 关键字来创建函数。
    ②省略return 关键字
    箭头函数有两种格式
    a.只包含一个表达式,可以省略{…} 和 return
    const add = (a, b) => a + b;
    b.包含多条语句,这时候就不能省略{…}和return
    const anotherAdd = (a, b) => {return a + b}
    ③继承当前上下文的this关键字


    • set数据结构

    1set本身是一个构造函数,它类似于数组,但是成员值都是唯一的。


    • es6基础

    for…of, for…in的区别?

    1、在循环对象属性的时候,使用for…in,在遍历数组的时候的时候使用for…of。
    2、for…in循环出的是key,for…of循环出的是value
    3、for…of是ES6新引入的特性。修复了ES5引入的for…in的不足
    4、for…of不能循环普通的对象,需要通过和Object.keys()搭配使用

    var student = {
      name: 'Jack',
      age: 27,
      sex: 'female'
    };
    var arr = [1, 4, 'small'];
    arr.school = 'zheda';
    // 1、for...in 遍历对象,得到key
    for (let key in student) {
      console.log(key); // name,age,sex
    }
    
    //for (let i of student) { // 报错:student is not iterable(可迭代的)
    //  console.log(i);
    //}
    
    // 2、直接使用for...of报错,可以用Object.keys(obj);
    for (let key of Object.keys(student)) {
    	// 用Object.keys()获取对象key的数组
      console.log(key); // name,age,sex
    }
    // 3、for...in遍历数组,得到index + key
    for (let i in arr) {
      console.log(i); // 0,1,2,school
    }
    // 4、for...of遍历数组,得到value
    for (let key of arr) {
      console.log(key); // 1,4,small
    }
    
    

    总结一下es6其他常用功能?

    1、let、const /'kɑnst/
    const定义常量,常量不能被重新赋值
    2、多行字符串/模板变量
    js拼接变量字符串模板

    console.log(`输出:${name}`)
    

    3、解构赋值
    整体数组或对象中拿到其中一个元素或属性值

    // obj
    const obj = {a: 10, b: 20, c:30};
    const {a, c} = obj;
    console.log(a); // 10
    console.log(c); // 30
    
    // arr
    const arr = ['xxx', 'yyy', 'zzz'];
    const [x, y, z] = arr;
    console.log(x); // xxx
    console.log(z); // zzz
    

    4、块级作用域
    for循环

    // js
    var obj = {a: 100, b: 200};
    for (var item in obj) {console.log(item)};
    console.log(item); // 'b'
    
    // es6
    const obj = {a: 100, b: 200};
    for (let item in obj) {console.log(item)};
    console.log(item); // undefined
    
    

    5、函数默认参数

    // js
    function(a, b) {
    	if(b == null) {b = 0;}
    }
    // es6
    function (a, b=0) {
    
    }
    

    6、箭头函数
    彻底解决了之前函数this指向全局window对象的问题

    function fn() {
    	console.log('real', this); // {a: 100}
    	var arr = [1, 2, 3];
    	// 普通js
    	arr.map(function(item) {
    		console.log('js',this); // window
    		return item + 1;
    	});
    	// 箭头函数
    	arr.map(item => {
    		console.log('es6', this); // {a: 100}
    		return item + 1;
    	});
    }
    fn.call({a: 100})
    

    babel原理?

    待完善
    答:ast转换

    [‘1’,‘2’,‘3’].map(parseInt) 输出什么,为什么?

    输出:[1, NaN, NaN]
    首先让我们回顾一下,map函数的第一个参数callback:
    var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])
    这个callback一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。
    而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
    parseInt(string, radix)
    接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
    了解这两个函数后,我们可以模拟一下运行情况
    parseInt(‘1’, 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
    parseInt(‘2’, 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
    parseInt(‘3’, 2) //基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN
    map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]

    展开全文
  • JavaScript、ES6基础面试题及答案解析② 1、js基本数据类型有哪些? 答:字符串String,数值Number,布尔 boolean,null,undefined,对象,数组 2、Ajax如何使用 一个完整的AJAX请求包括五个步骤: 创建...
  • JS、ES6es6的新特性 const let 模板字符串 箭头函数 函数的参数默认值 对象和数组解构 for...of 和 for...in 怎么用es5实现promise Promise是ES6才引入的一个东西,他是一种异步编程的解决方案 if有...
  • ECMAScript 6.0(简称ES6)是 JavaScript 语言的下一代标准。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。 let const 在es6之前,定义变量都是使用var,但是var存在...
  • Web前端面试题及答案——ES6

    千次阅读 2019-02-26 23:20:13
    1、let、const和var的区别 let用于声明变量,const...let和const不存在变量提升、重复声明变量、使用未经声明的变量(ES6强制开启严格模式)都会报错。var有变量提升、可以重复声明变量。 console.log(i); // ...
  • JavaScript数组的常用方法总结:遍历,复制,反转,排序,添加,删除(前端常见面试题必考必问_勤动手多动脑少说多做-CSDN博客_js数组倒序输出1. Array.push()向数组的末尾添加一个或多个元素,并返回新的数组长度...
  • 2020年最新前端面试题(包含ES6+)

    千次阅读 多人点赞 2020-08-23 12:58:39
    这是博主自己整理的一些前端所需要掌握的知识点,若有错,请评论指出。觉得博主需要补充的也可以留言哦 文章目录1.引用类型和基本类型2.JavaScript中的堆和栈3.var与let、const的区别4.JavaScript 严格模式(use ...
  • 2021年前端面试题及答案(更新中)

    千次阅读 2021-03-25 17:47:07
    不存在的对象null是表示一个空对象指针,这也是typeof操作符检测 null 值时会返回 object 的原因 (ES6 Symbol ): Symbol 类型的对象永远不相等,即便创建的时候传入相同的值。因此,可以用解决属性名冲突的问题...
  • 2021-10-16 ES6面试题

    2021-10-16 23:00:45
    一、多选 1、关于剩余参数的使用,正确的是?AB A、function fn(name, …args) {…} B、function fn(…args) {…} C、function fn(…args, name) {…} D、function fn(…name, …args) {…} 答案:AB C问题:剩余...
  • 2021 前端面试题及答案

    万次阅读 多人点赞 2021-03-04 22:17:23
    闭包 12.es6新特性es7,8 13.map和set 14.webpack 15.js的原型和原型链 16.浏览器器渲染流程 17.跨域 18.vue生命周期 19.vue双向绑定 20.vue组件通讯 21.vuex 22.vue路由 23.vuex的使用场景和全局变量的区别 24....
  • 由于本人使用的技术是以vue为主,所以面试官相应的会以vue为开始提问 Vue生命周期 答:Vue有四个生命周期,分别是beforeCreatecreated (创建前后)beforeMountmounted(挂载前后)beforeUpdateupdated (更新前后)...
  • 2021年最新Web前端面试题精选大全及答案

    万次阅读 多人点赞 2021-01-07 11:53:44
    ==n 利用ES6中提供的Object.is()方法(判断两个值是否相等) n==nan Js中null与undefined区别 相同点:用if判断时,两者都会被转换成false 不同点: number转换的值不同 number(null)为0 number(undefined)为...
  • 进大厂是大部分程序员的梦想,而进大厂的门槛也是比较高的,所以这里整理了一份腾讯、字节跳动、百度、阿里等一线互联网公司面试题大全,其中概括的知识点(HTML,CSS,JS,React,Vue,NodeJS,互联网基础知识)
  • 闭包 闭包就是一个函数,两个函数彼此嵌套,内部函数就是闭包形成闭包条件 缺点:易造成内存泄漏不会被垃圾回收机制回收 12.es6新特性es7,8 let,const,箭头函数,promise,class,解构赋值,export,模版字符串...
  • 2021前端必问面试题及答案

    万次阅读 多人点赞 2020-09-17 19:53:47
    web前端面试题【持续更新】js基础1.原型链和继承2.浅拷贝和深拷贝3.闭包4.事件委托5.this指向,call/apply/bind,回调函数6.数组的常用操作7.promise8.set和weakset,map和weakmap9.let和const10.解构赋值11.typeof...
  • es6面试题

    2021-11-11 12:22:03
    前端全部面试题 https://blog.csdn.net/qq_40055200/article/details/104637574  es6面试题 1.const let var 区别? var声明变量存在变量提升,let和const不存在变量提升 let声明变量和const声明常量,两个都可以...
  • 2021最全面、最详细web前端面试题及答案总结

    万次阅读 多人点赞 2021-02-01 09:34:00
    2021最全面、最详细web前端面试题及答案总结 总结不易,希望可以帮助到即将面试或还在学习中的web前端小伙伴,祝面试顺利,拿高薪! 本章是HTML考点的⾮重难点,因此我们采⽤简略回答的⽅式进⾏撰写,所以不会有太...
  • ★★★ 数据类型(判断,==和===)堆栈、内存泄漏垃圾回收机制 ★★★★ 了解 ES6 的 Proxy 吗? ★★★★ 深拷贝是什么?项目哪里是用到了深拷贝? ★★★ swiper 插件从后台获取数据没问题,css 代码啥的也没...
  • 点进来之后你的噩梦就要来了,接下来你要面对上百道面试题,那么,如果你—— 是个小白菜: 推荐使用2~3周的时间来消化接下来的面试题, 遇到不会的专业名词请立刻去搜; 文章中只是简答,如果想要详细了解的话还...
  • 2021前端面试题汇总,巩固基础 目录2021前端面试题汇总,巩固基础HTML相关面试题1.网络中使用最多的图片格式有哪些2. 音、视频标签的使用3. HTML5新增的内容4.HTML5新增语义化标签有哪些,以及语义化标签的优点5....
  • 第二种 字符串拼接 一道练习: Boolean详解 object对象之普通对象 普通对象 [key:value,…]任何一个对象都是由0到多组键值对(属性名:属性值)组成的(并且属性名不能重复,重复的话会覆盖) object对象之数组...
  • 前端面试常见面试题及答案h5新特性ES6新特性vuex的理解,组成以及原理介绍前端常用框架 h5新特性 html5总的来说比html4多了十个新特性,但其不支持ie8及ie8以下版本的浏览器 一、语义标签 二、增强型表单 三、视频和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,357
精华内容 4,542
关键字:

前端es6面试题及答案