ES6面试题大全

以下题目是根据网上多份面经收集而来的,题目相同意味着被问的频率比较高,有问题欢迎留言讨论,喜欢可以点赞关注。

image.png
1.let const var比较区别,ES6中let块作用域是怎么实现的

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/133

先说说这三者的区别吧:

可以结合https://www.jianshu.com/p/47b49d7bdfc2中的第一节来看。

(一)var

  • var 命令会发生“变量提升”现象,即变量可以在声明之前使用,值为 undefined
  • 内层变量可能覆盖外层变量
  • 用来计数的循环变量泄露为全局变量

(二)let

  • 声明的全局变量不会挂在顶层对象下面
  • 所声明的变量一定要在声明后使用,否则报错,报错 ReferenceError
  • 暂时性死区,只要块级作用域内存在 let 命令,它所声明的变量就“绑定”( binding )这个区域,不再受外部的影响,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。
  • 不允许重复声明

(三)const

  • 声明的全局变量不会挂在顶层对象下面
  • const 声明之后必须马上赋值,否则会报错
  • const 简单类型一旦声明就不能再更改,复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。
  • const 一旦声明变量,就必须立即初始化,不能留到以后赋值。
  • const 命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用

如 const 声明了一个复合类型的常量,其存储的是一个引用地址,不允许改变的是这个地址,而对象本身是可变的。

//      const arr 数组可以赋值
        const arr = ['apple', 'banana']
        arr.push('orange')
        console.log(arr)//["apple", "banana", "orange"]
然后是原理,原理确实没认真研究过,在网上翻了一番资料,结合自己的理解简单说下(纯属个人愚见,高手轻喷)

变量与内存之间的关系,主要由三个部分组成:

  1. 变量名
  2. 内存地址
  3. 内存空间

JS 引擎在读取变量时,先找到变量绑定的内存地址,然后找到地址所指向的内存空间,最后读取其中的内容。当变量改变时,JS 引擎不会用新值覆盖之前旧值的内存空间(虽然从写代码的角度来看,确实像是被覆盖掉了),而是重新分配一个新的内存空间来存储新值,并将新的内存地址与变量进行绑定,JS 引擎会在合适的时机进行 GC,回收旧的内存空间。

const 定义变量(常量)后,变量名与内存地址之间建立了一种不可变的绑定关系,阻隔变量地址被改变,当 const 定义的变量进行重新赋值时,根据前面的论述,JS 引擎会尝试重新分配新的内存空间,所以会被拒绝,便会抛出异常。

var的话会直接在栈内存里预分配内存空间,然后等到实际语句执行的时候,再存储对应的变量,如果传的是引用类型,那么会在堆内存里开辟一个内存空间存储实际内容,栈内存会存储一个指向堆内存的指针

ES6中let块作用域是怎么实现的

let的话,是不会在栈内存里预分配内存空间,而且在栈内存分配变量时,做一个检查,如果已经有相同变量名存在就会报错。也就是暂时性死区,只要块级作用域内存在 let 命令,它所声明的变量就“绑定”( binding )这个区域,不再受外部的影响,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。

const的话,也不会预分配内存空间,在栈内存分配变量时也会做同样的检查。不过const存储的变量是不可修改的,对于基本类型来说你无法修改定义的值,对于引用类型来说你无法修改栈内存里分配的指针,但是你可以修改指针指向的对象里面的属性

2.反引号(`)标识(模板字符串)

https://blog.csdn.net/zwt_guiji/article/details/81979299

模板字符串是在ES6中兴起

用法

// 普通字符串
`In JavaScript '\n' is a line-feed.` 
// 多行字符串
`In JavaScript this 
isnot legal.`
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?` // Hello Bob, how are you today?

模板字符串都是用反引号表示,如果在模板字符串中需要使用反引号,则前面需要用反斜杠转义。

如果使用模板字符串表示多行字符串,则所有的空格、缩进和换行都会被保留在输出中。比如

    标签前面会有一个换行。如果想把行首和行尾的换行、空格等去掉,则使用trim方法即可。

    模板字符串中嵌入变量,要将变量名写在${}之中。大括号内可以放入任意的JavaScript表达式,可以进行运算,以及引入对象属性。

    模板字符串之中还可以调用函数。

    如果大括号中的值不是字符串,则将按照一般的规则转换为字符串。如,若大括号中是一个对象,则将默认调用对象的toString方法,把对象转换为字符串。

    如果模板字符串中的变量没有声明,则会报错。

    // 变量place没有声明
    var msg = `Hello, ${place}`;
    // ReferenceError: place is not defined
    

    模板字符串之间还可以进行嵌套。

    标签模板

    模板字符串的功能,不仅是上面那些,它还可以紧跟在一个函数后面,该函数将被调用来处理这个模板字符串,这种称为“标签模板”功能(Tagged template)。

    alert`123`// 等同于alert(123)
    

    标签模板其它是一种特殊的函数调用形式,“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。

    var a = 1,    b = 2;
     tag`Helo ${a + b} world ${a * b}`;
    

    上面代码中,模板字符串前面有一个标识名tag,它是一个函数。整个表达式的返回值就是tag函数处理模板字符串后的返回值。

    函数tag依次会接收到多个参数。

    tag函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。tag函数的其他参数,都是模板字符串各个变量被替换的值。本例中,模板字符串含有两个变量,因此tag会接收到value1和value2两个参数。

    tag函数所有参数的实际值如下:

    • 第一个参数: [‘Hello ‘, ’ world’, ”]
    • 第二个参数: 3
    • 第三个参数: 2

    也就是说,tag函数实际上是用下面的形式调用:

    tag(['Hello ',' world', ''], 3, 2);
    

    String对象的raw方法

    String.raw方法用来充当模板字符串的处理函数,返回一个除表达式和变量会被替换,其它都保持原样的字符串。

    String.raw方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。

    3.函数默认参数和剩余(rest)参数

    详细解析点击链接

        1. 函数默认参数
            function show({x=0,y=0}={}){
                console.log(x,y);
            }
            show()
        2. 函数参数默认已经定义了,不能再使用let,const声明
            function show(a=18){
                let a = 101;  //错误
                console.log(a);
            }
            show()
    
    扩展运算符、Reset运算符:
        ...
    
        展开数组
    
        ... :
            [1,2,3,4]  -> ... [1,2,3,4]  ->  1,2,3,4,5
        ...:
            1,2,3,4,5  -> ...1,2,3,4,5  ->  [1,2,3,4,5]
    
        剩余参数: 必须放到最后
    

    rest参数和 arguments对象的区别

    image.png

    4.箭头函数this指向问题,箭头函数与普通函数的区别,你说箭头函数没有自己的this,那(()=>{}).bind(this)可以么?

    https://juejin.im/post/5b14d0b4f265da6e60393680

    image.png

    六没
    1、没有this
    箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值。这就意味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。,因为箭头函数没有 this,所以也不能用 call()、apply()、bind() 这些方法改变 this 的指向,可以看一个例子:

    var value = 1;
    var result = (() => this.value).bind({value: 2})();
    console.log(result); // 1
    

    2、没有arguments
    箭头函数没有自己的 arguments 对象,这不一定是件坏事,因为箭头函数可以访问外围函数的。通过命名参数或者 rest 参数的形式访问参数

    3、没有new关键字调用
    箭头不能被用作构造函数,如果通过 new 的方式调用,会报错

    var Foo = () => {};
    var foo = new Foo(); // TypeError: Foo is not a constructor
    

    4、没有new.target
    因为不能使用 new 调用,所以也没有 new.target 值。关于 new.target,可以参考 es6.ruanyifeng.com/#docs/class…

    5、没有原型
    由于不能使用 new 调用箭头函数,所以也没有构建原型的需求,于是箭头函数也不存在 prototype 这个属性。

    var Foo = () => {};
    console.log(Foo.prototype); // undefined
    

    6、没有super
    连原型都没有,自然也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的,不过跟 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。

    5.属性简写

    1.对象的方法简写

    const o = {
      method() {
        return "Hello!";
      }
    };
    
    // 等同于
    
    const o = {
      method: function() {
        return "Hello!";
      }
    };
    
    let birth = '2000/01/01';
    const Person = {
      name: '张三',
      //等同于birth: birth
      birth,
      // 等同于hello: function ()...
      hello() { console.log('我的名字是', this.name); }
    };
    
    2.对象的属性简写
    
    const a= 'a';
    const b= {a};                    
    b// {a: "a"}
    // 等同于
    const b= {a: a};             //前面的a是对象属性的名字,后面的a代表变量a
    
    实际应用:
    function f(x, y) {
      return {x, y};
    }
    
    // 等同于
    
    function f(x, y) {
      return {x: x, y: y};
    }
    
    f(1, 2) // {x: 1, y: 2}
    
    3.扩展
    
    
    import App from './App.vue'
    new Vue({
       router,
       store,
       render: h => h(App)
     })
    
    等同于
    
    new Vue({
      router:router,
      store:store,
      render:function(h){
          return  h(App);
      }
    })
    
    6.方法简写

    同上

    7.Object.keys()方法,获取对象的所有属性名或方法名

    一、语法

    Object.keys(obj)
    参数:要返回其枚举自身属性的对象
    返回值:一个表示给定对象的所有可枚举属性的字符串数组

    二、处理对象,返回可枚举的属性数组

    let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
    Object.keys(person) // ["name", "age", "address","getName"]
    
    image

    三、处理数组,返回索引值数组

    let arr = [1,2,3,4,5,6]
    Object.keys(arr) // ["0", "1", "2", "3", "4", "5"]
    
    image

    四、处理字符串,返回索引值数组

    let str = "saasd字符串"
    Object.keys(str) // ["0", "1", "2", "3", "4", "5", "6", "7"]
    
    image

    五、常用技巧

    let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
    Object.keys(person).map((key)=>{
    person[key] // 获取到属性对应的值,做一些处理
    }) 
    

    六、Object.values()和Object.keys()是相反的操作,把一个对象的值转换为数组

    8.Object.assign ()原对象的属性和方法都合并到了目标对象

    https://www.jianshu.com/p/f9ec860ecd81

        Object.assign():   用来合并对象
        let 新的对象 = Object.assign(目标对象, source1, srouce2....)
    
        function ajax(options){  //用户传
            let defaults={
                type:'get',
                header:
                data:{}
                ....
            };
    
            let json = Object.assign({}, defaults, options);
            .....
        }
        
        用途:
            1. 复制一个对象
            2. 合并参数
    
        let json = { a: 3, b: 4 };
        let json2 = { ...json };
        console.log(json2 === json)//false
        console.log(Object.assign({}, json) === json)//false
        console.log(Object.assign(json) === json)//true
    
    

    注意:
    1.如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性
    2.Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标
    对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如
    果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到
    原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。

    9.for...of 循环

    https://www.jianshu.com/p/2e6dd8906e97

    一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。

    for...of 遍历 非 Iterator 的类数组对象

    并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组。

    let arrayLike = { length: 2, 0: 'a', 1: 'b' };
    
    // 报错
    for (let x of arrayLike) {
      console.log(x);
    }
    
    // 正确
    for (let x of Array.from(arrayLike)) {
      console.log(x);
    }
    

    for...of 遍历 对象
    对于普通的对象,for...of结构不能直接使用,会报错。使用 for...in 可以遍历对象的键名。

    let obj = {
        a: 1,
        b: 2,
        c: 3
    }
    
    for (let e in obj) {
        console.log(e);  // 'a'  'b'  'c'
    }
    

    总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。

    一种解决方法是,使用Object.keys方法将对象的键名生成一个数组,然后再用 for...of 遍历这个数组。

    for(let key of Object.keys(obj)) {
        console.log(key + ': ' + obj[key]);
    }
    

    另一个方法是使用 Generator 函数将对象重新包装一下。

    function* entries(obj) {
        for(let key of Object.keys(obj)) {
            yield [key, obj[key]];
        }
    }
    for(let [key, value] of entries(obj)) {
        console.log(key, '->', value);
    }
    

    9、for...of 可以与 break / continue / return 配合使用

    for (var n of arr) {
        if (n > 10) {
            break;
        }
        console.log(n);
    }
    
    10.import和export,CommonJS 中的 require/exports 和 ES6 中的 import/export 区别?

    1、CommonJS模块输出的是一个值的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值。

      ES6模块输出的是值的引用,JS引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,在根据引用到被加载的那个模块里面去取值。ES6模块是动态引用,并且不会缓存运行结果,而是动态的去被加载的模块取值,模块里面的变量绑定其所在的模块。
    

    2、CommonJS模块是运行时加载,ES6模块是编译时输出接口(ES6可以在编译时就完成模块加载,效率要比CommonJS模块的加载方式高)。

    1. ES6模块是通用的,同一个模块不用修改,就可以用在浏览器

    4、require/exports是CommonJS在Node中实现的。

      import/export是ES6的模块,对ES6只要使用babel就可以了
    

    5、ES6模块的设计思想,是尽量静态化,使得编译时就能确定模块的依赖关系以及输入和输出的变量。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

    12.解构赋值

    非常有用,特别在做数据交互 ajax

    let [a,b,c] =[12,5, 6];

    注意: 左右两边,结构格式要保持一致

            // let [a, b, c] = [12, 3, 'k']
            // console.log(a, b, c)//12 3 "k"
    
            // 属性名称要一一对应
            // let { name, age, job, f, arr } = {
            //     name: 'Strive',
            //     age: 18,
            //     job: '码畜',
            //     arr: [
            //         { name: 'liu' },
            //         { name: 'zhao' }
            //     ]
            // };
            // console.log(name, age, job, f)//Strive 18 码畜 undefined
            // console.log(name, age, job, arr)//Strive 18 码畜 (2) [{…}, {…}]
    
            // let json = {
            //     name: 'Strive',
            //     age: 18,
            //     job: '码畜',
            //     arr: [
            //         { name: 'liu' },
            //         { name: 'zhao' }
            //     ]
            // };
            // let { name: n, age: g, job: f, arr } = json;
            // console.log(n, g, f, arr)//Strive 18 码畜 (2) [{…}, {…}]
    
            // let [a, b, c] = ['a', 'b']
            // console.log(a, b, c)//a b undefined
    
            // 传undefined和上面不传参数是一样的 传null和‘’都是有值
            // let [a, b, c] = ['a', 'b', undefined]
            // console.log(a, b, c)//a b undefined
    
            // let [a, b, c = 'no-data'] = ['a', 'b']
            // console.log(a, b, c)//a b no-data
    
            // let a;
            // //先用let定义没有赋值  要用小括号括起来  不然大括号直接暴露在外面  会起到块级作用域的
            // ({ a } = { a: 'apple', b: 'banana' });
            // console.log(a)
    
            // a,b相互赋值 不用找中间值
            // let a = 12;
            // let b = 10;
            // [a, b] = [b, a]
            // console.log(a)//10
    
            // function fn() {
            //     return {
            //         leftVal: 10,
            //         topVal: 20
            //     }
            // }
    
            // let { leftVal, topVal: t } = fn();
            // console.log(leftVal, t)//10 20
    
            function fn({ a, b = '默认值' }) {
                console.log(a)//1
            }
            fn({
                a: 1,
                b: undefined
            })
    
    13.set数据结构(可用于快速去重)
    set数据结构:
        类似数组,但是里面不能有重复值
    
    let arr  = ['a','b','a'];
    
    let arr = new Array();
    
    set用法:
        let setArr = new Set(['a','b']);
    
        setArr.add('a');   往setArr里面添加一项
        setArr.delete('b'); 删除一项
        setArr.has('a') 判断setArr里面有没有此值
        setArr.size 个数
    
        setArr.clear(); 清空
    
    
    14.Spread Operator 展开运算符(...)
    ... [1,2,3,4]  ->  1,2,3,4,5
    ... 1,2,3,4,5  ->  [1,2,3,4,5]
    
    15.字符串新增方法

    https://www.cnblogs.com/weimingmei/p/11488458.html
    1、String.fromCodePoint()
    2、String.raw()
    3、codePointAt()
    4、normalize()
    5、includes(), startsWith(), endsWith()
    6、repeat()
    7、padStart(),padEnd()
    8、trimStart(),trimEnd()
    9、matchAll()

    关于字符串一些东西:
    字符串查找:
            str.indexOf(要找的东西)   返回索引(位置) ,没找到返回-1
            str.includes(要找的东西)   返回值  true/false
    
            判断浏览器:  includes
    
            http://www.xxx.xx
    
            字符串是否以谁开头:
                str.startsWith(检测东西)
    
                检测地址
            字符串是否以谁结尾:
                str.endsWith(检测东西)
    
                .png
    
            重复字符串:
                str.repeat(次数);
    填充字符串:
            str.padStart(整个字符串长度, 填充东西)   往前填充
            str.padEnd(整个字符串长度, 填充东西)    往后填充
    
            str.padStart(str.length+padStr.length, padStr)
    
    16.声明类与继承:class、extend,ES6 class与ES5 function区别及联系
    image.png

    区别
    https://blog.csdn.net/u012657197/article/details/77542404

    17.symbol应用
    数据类型:
        number、string、boolean、Object、undefined、function
    
        用typeof检测出来数据类型: symbol
    
        new Number(12)
        new String()
        new Array()
    
    symbol  使用情况一般
    
    定义:
        let syml = Symbol('aaa');
    
    注意:
    1. Symbol 不能new
    2. Symbol() 返回是一个唯一值坊间传说, 做一个key,定义一些唯一或者私有一些东
    3. symbol是一个单独数据类型,就叫 symbol, 基本类型
    4. 如果symbol作为key,用for in循环,出不来
    5.Symbol值作为对象属性时,不能使用点运算符。
    
    
    json  ->  for in
    
    18.es6和es7中新增加了什么

    ES2016(es7)添加了两个小的特性来说明标准化过程:
    数组includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
    a ** b指数运算符,它与 Math.pow(a, b)相同。


    image.png
    19.ES6怎么编译成ES5,css-loader原理,过程

    Babel 是一个 JavaScript 编译器,Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。Webpack也有自动编译转换能力。
    ①引入脚本babel库②如下

    image.png

    css-loader原理,过程
    loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。css-loader是分析各个css文件的关系并合并成一个css。
    关于es6及以上的js编译成es5

    20.ES6转成ES5的常见例子

    https://www.cnblogs.com/slongs/p/11238574.html

    21.使用解构,实现两个变量的值的交换
    let [a,b]=[b,a]
    
    22.利用数组推导,计算出数组 [1,2,3,4] 每一个元素的平方并组成新的数组
        var arr1 = [1, 2, 3, 4];
        var arr2 = arr1.map(i => i * i)
        console.log(arr2);
    
    23.map和set有没有用过,如何实现一个数组去重,map数据结构有什么优点?ES6的Set内部实现

    Map 对象
    Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

    Maps 和 Objects 的区别

    1、一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
    2、Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
    3、Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
    4、Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

    for…of

    var myMap = new Map();
    myMap.set(0, "zero");
    myMap.set(1, "one");
     
    // 将会显示两个 log。 一个是 "0 = zero" 另一个是 "1 = one"
    for (var [key, value] of myMap) {
      console.log(key + " = " + value);
    }
    for (var [key, value] of myMap.entries()) {
      console.log(key + " = " + value);
    }
    /* 这个 entries 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。 */
     
    // 将会显示两个log。 一个是 "0" 另一个是 "1"
    for (var key of myMap.keys()) {
      console.log(key);
    }
    /* 这个 keys 方法返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键。 */
     
    // 将会显示两个log。 一个是 "zero" 另一个是 "one"
    for (var value of myMap.values()) {
      console.log(value);
    }
    /* 这个 values 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */
    

    forEach()

    var myMap = new Map();
    myMap.set(0, "zero");
    myMap.set(1, "one");
     
    // 将会显示两个 logs。 一个是 "0 = zero" 另一个是 "1 = one"
    myMap.forEach(function(value, key) {
      console.log(key + " = " + value);
    }, myMap)
    

    Map 与 Array的转换

    var kvArray = [["key1", "value1"], ["key2", "value2"]];
     
    // Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
    var myMap = new Map(kvArray);
     
    // 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
    var outArray = Array.from(myMap);
    

    Set 对象
    Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

    Set 中的特殊值
    Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:

    +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
    undefined 与 undefined 是恒等的,所以不重复;
    NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。
    let mySet = new Set();

    mySet.add(1); // Set(1) {1}
    mySet.add(5); // Set(2) {1, 5}
    mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性
    mySet.add("some text"); 
    // Set(3) {1, 5, "some text"} 这里体现了类型的多样性
    var o = {a: 1, b: 2}; 
    mySet.add(o);
    mySet.add({a: 1, b: 2}); 
    // Set(5) {1, 5, "some text", {…}, {…}} 
    // 这里体现了对象之间引用不同不恒等,即使值相同,Set 也能存储
    

    类型转换

    // Array 转 Set
    var mySet = new Set([“value1”, “value2”, “value3”]);
    // 用…操作符,将 Set 转 Array
    var myArray = […mySet];
    String
    // String 转 Set
    var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}
    // 注:Set 中 toString 方法是不能将 Set 转换成 String
    

    数组去重

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

    map优点
    Map保存[key-value]值,value可多值。

    ES6的Set内部实现
    https://www.cnblogs.com/hui-fly/p/9459152.html

    24.取数组的最大值(ES5、ES6)
    // ES5 的写法
    Math.max.apply(null, [14, 3, 77, 30]);
     
    // ES6 的写法
    Math.max(...[14, 3, 77, 30]);
     
    // reduce
    [14,3,77,30].reduce((accumulator, currentValue)=>{
        return accumulator = accumulator > currentValue ? accumulator : currentValue
    });
    
    25.const obj 的属性如何不可变

    const定义的基本数据类型的变量确实不能修改。
    因为对象是引用类型的,保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。我们试着修改一下指针,让P指向一个新对象,结果如下图:

    image

    即使对象的内容没发生改变,指针改变也是不允许的。

    26.ES6新的特性有哪些?x4

    1、let const命令
    2、解构赋值
    3、字符串方法模板字符串
    4、默认函数、剩余函数、箭头函数
    5、Symbol
    6、Set和Map
    7、模块化
    8、promise
    9、class继承
    10、数组的扩展

    27.Promise对象
    image.png
    28.用es6 promise实现封装XMLHttpRequest

    只需创建一个Promise对象,调用它的resolve()和reject()以及then()方法,then()里面也可以写箭头函数;

    function verifyIdCard(idCardOptions) {
        let options = {
            // url: config.idCardKey.idCardHost + '?idCard=' + idCardOptions.idCard + '&name=' + idCardOptions.name,
            url: 'https://idcert.market.alicloudapi.com/idcard',
            method: 'GET', 
            qs: {
                idCard: idCardOptions.idCard,
                name: idCardOptions.name
            },
            headers: { 
                'cache-control': 'no-cache',
                Authorization: 'APPCODE ' + config.idCardKey.idCardAppCode,
                'Content-Type': 'application/json'
            },
            json: true
        };
    
        console.info(options)
        return new Promise((resolve, reject) => {
            request(options, (error, response, body) => {
                if (!error && (response.statusCode === 200 || response.statusCode === 304)) {
                    let IdCardInfo = {
                        reqInfo: idCardOptions,
                        resInfo: body
                    }
                    console.info(IdCardInfo)
                    let idCardModel = new IdCardModel(IdCardInfo);
                    idCardModel.save();
                    resolve(body);
                } else {
                    reject(error);
                }
            })
        })
    }
    
    
    29.generator(异步编程、yield、next()、await 、async)

    Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。Generator 有两个区分于普通函数的部分:一是在 function 后面,函数名之前有个 * ;函数内部有 yield 表达式。其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。

    function* sendParameter(){
        console.log("strat");
        var x = yield '2';
        console.log("one:" + x);
        var y = yield '3';
        console.log("two:" + y);
        console.log("total:" + (x + y));
    }
    
    var sendp1 = sendParameter();
    sendp1.next();
    // strat
    // {value: "2", done: false}
    sendp1.next();
    // one:undefined
    // {value: "3", done: false}
    sendp1.next();
    // two:undefined
    // total:NaN
    // {value: undefined, done: true}
    var sendp2 = sendParameter();
    sendp2.next(10);
    // strat
    // {value: "2", done: false}
    sendp2.next(20);
    // one:20
    // {value: "3", done: false}
    sendp2.next(30);
    // two:30
    // total:50
    // {value: undefined, done: true}
    }
    

    一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数。

    async
    async 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联的。async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。

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

    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() {
      var x = await testAwait ("hello world");
      console.log(x); 
    }
    helloAsync ();
    // hello world
    

    正常情况下,await 命令后面是一个 Promise 对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数。await针对所跟不同表达式的处理方式:Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。非 Promise 对象:直接返回对应的值。


    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

你可能感兴趣的:(ES6面试题大全)