ES6面试题(测试题)

ES6面试热门知识点解析
本文详细探讨了ES6在面试中的常见问题,包括vue生命周期、插槽、key的作用、Promise封装ajax、CommonJS与ES模块的区别等。还讨论了for-in、for-of、forEach的区别,深入理解this、Set的使用、Symbol的场景、var、let、const的差异以及多种数组去重和对象深拷贝的方法。此外,涵盖了Promise方法、可迭代对象、静态方法、类的继承、异步函数、代理和反射的概念。最后,比较了TypeScript与JavaScript的差异,并介绍了抽象类、接口、枚举和多态等面向对象概念。

1.简述vue生命周期?

从vue实例被创建开始到该实例最终被销毁个过程叫做vue的生命周期。在这个周期内大致发生一下几件事,我们从vue实例被创建开始,首先vue实例被创建,之后开始数据的初始化,编译模板,挂载dom,渲染dom,更新对象属性,渲染dom,解绑销毁。
(详情了解可以跳转我的另外一篇博客Vue生命周期函数详解

在这里插入图片描述

2.什么是插槽?你用过哪些插槽?

它是vue提出的一个概念,插槽用于决定将所携带的内容,插入到指定的某个位置,使得模块分块,具有模块化特质。
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

3.vue中key的作用

可以唯一的确定一个DOM元素,让diff算法更加高效

4.使用Promise封装ajax?

function fetch(method, url, data){
    return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest();
        var method = method || "GET";
        var data = data || null;
        xhr.open(method, url, true);
        xhr.onreadystatechange = function() {
            if(xhr.status === 200 && xhr.readyState === 4){
                resolve(xhr.responseText);
            } else {
                reject(xhr.responseText);
            }
        }
        xhr.send(data);
        })
}

5.CommonJS模块与ES模块的区别?

CommonJS 模块输出的是⼀个值的拷⻉,ES6 模块输出的是值的引⽤。
CommonJS 模块是运⾏时加载,ES6 模块是编译时输出接⼝。
CommonJS 模块的 require() 是同步加载模块,ES6 模块的 import 命令是异步加载,
有⼀个独⽴的模块依赖的解析阶段。

CommonJS

1.对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
2.对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
3.当使用require命令加载某个模块时,就会运行整个模块的代码。
4.当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
5.循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

6.ES6模块中的值属于【动态只读引用】。
7.对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
1.对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
2.循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

6.简述for-in、for-of、forEach区别?

forEach:(可以三个参数,第一个是value,第二个是index,第三个是数组体)

 1.定义:用于调用数组的每个元素,并将元素传递给回调函数;
 
 2.缺点:不能同时遍历多个集合,在遍历的时候无法修改和删除集合数据,方法不能使break,continue语句跳出循环,或者使用return从函数体返回,对于空数组不会执行回调函数;
 
 3.优点:便利的时候更加简洁,效率和for循环相同,不用关心集合下标的问题,减少了出错的效率。

for in:(它大部分用于遍历对象 json)

1.定义:用于循环遍历数组或对象属性,for in循环里面的index是string类型的,代码每执行一次,就会对数组的元素或者对象的属性进行一次操作;

2.缺点:某些情况下,会出现随机顺序的遍历,因为里面的值是string类型,所以增加了转换过程,因此开销较大;
 
3.优点:可以遍历数组的键名,遍历对象简洁方便。

for of:(可遍历map,object,array,set string等)

 1.定义:用来遍历数据,比如组中的值;
 
 2.缺点:不适用于处理原有的原生对象(原生对象是一个子集,包含一些在运动过程中动态创建的对象);
 
 3.优点:避免了for in的所有缺点,可以使用break,continue和return,不仅支持数组的遍历,还可以遍历类似数组的对象,支持字符串的遍历,最简洁,最直接的遍历数组的语法,支持map和Set对象遍历。

7.谈谈你对this的理解?

普通函数中的this指向:

  • 情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window(在浏览器模式下)。
  • 情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
  • 情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象。
  • 情况4:如果通过call、apply、bind更改this指向,则this指向这三者方法中的第一个参数。(第一个参数即为指定的this指向地方)。

箭头函数的this:

  • 指向箭头函数定义时所处的对象,而不是箭头函数使用时所在的对象,默认使用父级的this.

8.ES6 中的 Set如何使用,如何实现自己的 Set?

ES6提供了新的数据结构Set,它类似于数组,但是成员的值是唯一的,没有重复的值(对于基本类型来说)。Set本身是一个构造函数,用来生成Set数据结构。
1、声明

 let set = new Set();
// 即创建了一个空的set

2、赋值

let set1 = new Set(['张三', '李四', '王五']);
console.log(set1)
// 输出为:Set(3) {"张三", "李四", "王五"}

3、属性:使用size求集合的大小(长度)

let set1 = new Set(['张三', '李四', '王五']);
console.log(set1)
// 输出为:Set(3) {"张三", "李四", "王五"}
console.log(set1.size)
// 返回值:3

4、方法
(1)add方法:表示往set中新增一个元素。

let set = new Set([1, 2, 3, 4]);
set.add("aa")
console.log(set)
// 返回值: Set(5) {1, 2, 3, 4, "aa"}
// 改变自身
set.add(9).add(8).add(7)
console.log(set)
//返回值:Set(8) {1, 2, 3, 4, "aa", 9, 8, 7}

(2)delete方法:表示删除一个元素,返回结果是true或false

console.log(set.delete("aa"))
// 返回值:true3 console.log(set)
// 返回值: Set(7) {1, 2, 3, 4, 9, 8, 7}

(3)has方法:判断某个元素是否在set集合中

console.log(set.has(9))
// 返回:true
console.log(set.has("aa"))
// 返回:false

(4)clear方法:表示清楚set集合中的所有元素。没有任何返回值,直接清楚所有元素。

set.clear()
console.log(set)
// 返回值:Set(0) {}

9.ES6 中 Symbol 如何使用,用在什么地方?

我认为Up主@宛丘之上兮 讲的非常好,请移步->

10.谈谈var、let、const的区别

声明方式变量提升暂时性死区重复声明初始值作用域
var允许不存在允许不需要除块级
let不允许存在不允许不需要块级
const不允许存在不允许需要块级

可以看看这位大佬写的,请移步->

11.如何实现数组去重?[尽可能多方案]

1.利用ES6set去重

function unique(arr) {
    return Array.from(new Set(arr))
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

2.利用indexOf方法

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error')
        return
    }
    var array = []
    for (let i = 0; i < arr.length; i++) {
        if (array.indexOf(arr[i]) === -1) {
            array.push(arr[i])
        }
    }
    return array
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr)) //同样NAN和{}无法去重

3.先排序,后判断相邻值是否相等

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error')
        return
    }
    var arr = arr.sort()
    var array = [arr[0]]
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i - 1]) {
            array.push(arr[i])
        }
    }
    return array
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))

4.利用for嵌套for,然后splice去重(es5 中最常见)

function unique(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] == arr[j]) {
                arr.splice(j, 1)
                j--
            }
        }
    }
    return arr
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
// arrayObject.splice(index,howmany,item1,.....,itemX)
// index        必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
// howmany      必需。要删除的项目数量。如果设置为 0,则不会删除项目。
// item1, ..., itemX    包含被删除项目的新数组,如果有的话。

12.如何实现对象深拷贝?

1.lodash.cloneDeep()
2.递归
3.Json.stringify和Json.parse
4.Object.assign()

13.简述Promise.all()、Promise.race()、Promise.allSettled()、Promise.resolve()、Promise.reject() Promise.all(iterable)

  • Promise.all(iterable)

这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。

  • Promise.race(iterable)

当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

  • Promise.allSettled(iterable)

等到所有promises都已敲定(settled)(每个promise都已兑现(fulfilled)或已拒绝(rejected))。
返回一个promise,该promise在所有promise完成后完成。并带有一个对象数组,每个对象对应每个promise的结果。

  • Promise.resolve(value)

方法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。

  • Promise.reject(reason)

返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

14.ES6中哪些对象是可迭代的?

  1. 在ES6中,数组、Set、Map、字符串都是可迭代对象。
  2. 默认情况下定义的对象(Object)是不可迭代的,但是可以通过Symbol.Iterator创建迭代器。

15.ES6中如何创建一个静态方法?静态方法中是否可以访问成员属性,为什么?

在我们使用ES6的过程中,可以使用static定义类的静态方法。如果在父类中定义一个静态方法,在子类调用父类的静态方法,可以使用super.静态方法;
静态方法只可以访问静态属性。

16.ES6中如何完成类的继承,super的作用是什么?

ES6 的继承机制,是先创造父类的实例对象,然后再用子类的构造函数修改。
因此,如果子类中显式调用构造方法 constructor(){} ,必须要在子类构造方法中调用 super() 方法。如果不调用 super 方法,子类就得不到 this 对象。
super方法的作用:

一句话,获取子类this并且获取父类的this,但因为是在子类的构造方法中调用的super方法,所以,父类的this指向子类的this,即都为子类的this。

17.什么是异步函数?异步函数如何使用?

异步函数:如果一个异步函数被调用时,该函数会立即返回尽管该函数规定的操作任务还没有完成。
用 async/await 来处理异步@SamWeb

18.ES6中代理是什么?如何使用?请说出至少三个常见的代理方法

Proxy 可以理解成,在⽬标对象之前架设⼀层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了⼀种机制,可以对外界的访问进⾏过滤和改写。
set、get、apply、constructor

19.ES6中反射是什么?如何使用?请说出至少三个常见的反射方法

Reflflect 是⼀个内置的对象,它提供拦截 JavaScript 操作的⽅法。这些⽅法与proxy的⽅法相同。 Reflect 不是⼀个函数对象,因此它是不可构造的。
set、get、apply、constructor等

20.简述Typescript与Javascript的区别与关联?

  • TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发。2012年十月份,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript
    0.9,向未来的TypeScript 1.0版迈进了很大一步。
  • JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

JavaScript 和 TypeScript(以下简称JS、TS) 的主要差异:

1.TS 可以使用 JS 中的所有代码和编码概念,TS 是为了使 JS的开发变得更加容易而创建的。例如,TS使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序;

2.TS从核心语言方面和类概念的模塑方面对 JS 对象模型进行扩展;

3.JS 代码可以在无需任何修改的情况下与 TS 一同工作,同时可以使用编译器将 TS 代码转换为 JS;

4.TS 通过类型注解提供编译时的静态类型检查;

5.TS 中的数据要求带有明确的类型,JS不要求;

6.TS 为函数提供了缺省参数值;

7.TS 引入了 JS 中没有的“类”概念;

8.TS 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。

21.什么是抽象类?抽象类应用在什么场景中?

  • 抽象类通过abstract修饰,抽象类中可以存在抽象⽅法也可以存在⾮抽象⽅法;
  • 抽象类是⽆法实例化的,也就是我们⽆法通过new来调⽤抽象类;
  • 如果⼀个类中包含了抽象⽅法,那么这个类⼀定要声明成抽象类;
  • 抽象类存在的意义是为了让其他类继承,抽象类可以为其⼦类提供接⼝以及必要的具体⽅法。

抽象类的应用场景:
描述一类事物的时候,发现该事物确实存在着某种行为,但是目前该行为是不具体的,那么这时候我们应该抽取该方法的声明,不去实现该方法,这时候我们应该使用抽象类。eg:所有的接口都默认有abstract修饰,即都是抽象的。

抽象类要注意的细节:

     1. 如果一个方法没有方法体,那么该方法必须使用abstract修饰;

     2. 如果一个类有抽象的方法,那么该类也必须使用abstract修饰;

     3. 非抽象类继承抽象类的时候,必须要把抽象类中的所有抽象方法全部实现;

     4. 抽象类可以定义抽象方法以及非抽象方法;

     5. 抽象类是可以不存在抽象方法的;

     6. 抽象类不能创建对象;
     7. 抽象类中的抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用;
          如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
          
     8. 抽象类是存在构造方法的。
疑问: 为什么抽象类不能创建对象呢?
答:因为抽象类一旦创建了对象就可以使用对象去调用方法了,一旦调用了抽象方法就没有任何的意义了。
疑问:既然抽象类不能创建对象,那么存在构造方法的意义在那?

答:抽象类的构造方法是留给子类调用初始化从父类继续下去的成员变量的。

// 抽象类是⽆ 法实例化的,也就是我们⽆法通过new来调⽤抽象类;
// 抽象类存在的意义是为了让其他类继承,抽象类可以为其⼦类提供接⼝。 以及必要的具体⽅法。

abstract class Parent {
  // 如果⼀个类中包含了抽象⽅法,那么这个 类⼀定要声明成抽象类;
  abstract study(): string;   //抽象方法
  work() {
    console.log("工作");
  }
  eat() {
    console.log("哇,真香");
  }
}
class Child extends Parent {
  study(): string {
    super.work();
    super.eat();
    return "好好学习,找到了工作"
  }
}
let child: Parent = new Child();//以父之名
let abc: string = child.study();
console.log(abc);

//父类仅仅提供了一个study()方法,并没有实现,子类继承了父类去替他实现。

22.什么是接口?接口应用在什么场景中?如何实现接口的实现?

接口就是规定一些 “事”,在后台中,接口指的是双方规定的 API,在 TS 里接口一般指这个变量拥有什么东西。


interface Shape {
  head: string;
  arm: string;
}

interface Human {
  name: string;
  age: number;
  shape: Shape;
  say(word: string): void;
}

let jack: Human = {
  name: 'Jack';
  age: 18;
  shape: {
    head: 'head',
    arm: 'arm'
  };
  say(word: string) {
    console.log(word)
  }
}
jack.say('hi')//hi

上面规定了类型为 Shape 的变量应该有 head 和 arm,而 Human 类型的应该有 name, age, shape, say 属性。其中还对函数以及 shape 做了一些规定。
这就是接口,不过是一些规范而已。
TS: 接口@写代码的海怪

23.什么是枚举?

使⽤枚举我们可以定义⼀些带名字的常量。 使⽤枚举可以清晰地表达意图或创建⼀组有区别的⽤例。 TypeScript⽀持数字的和基于字符串的枚举 。

24.什么是多态?

/*  
* ts中类抽象类、多态
* 抽象类: abstract 修饰, 里面可以没有抽象方法。但有抽象方法(abstract method)的类必须声明为抽象类(abstract class)
* 多态:父类定义一个方法不去实现,让继承它的子类去实现  每一个子类有不同的表现
* 注意:使用多态基础是类的继承或者接口实现
* */

/**
 * Animal 是一个抽象类,里面含有一个eat()抽象方法
 */
abstract class Animal {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    //抽象方法 ,不包含具体实现,要求子类中必须实现此方法
    abstract eat(): any;
    //非抽象方法,无需要求子类实现、重写
    run() {
        console.log('非抽象方法,不要子类实现、重写');
    }
}
class Dog extends Animal {
    //子类中必须实现父类抽象方法,否则ts编译报错
    eat() {
        return this.name + "吃肉";
    }
}
class Cat extends Animal {
    //子类中必须实现父类抽象方法,否则ts编译报错
    eat() {
        return this.name + "吃鱼";
    }
}

var dog = new Dog("tom");
var cat = new Cat("kitty");
console.log(dog.eat());
console.log(cat.eat());


//多态 ,一种事物的不同表现形态。如下面的代码中 先声明变量f是Animal类型,具体是Dog还是Cat,在new 对象时才知道
//如果是Dog,则f.eat()调用的是Dog类中的eat方法;如果是Cat,则f.eat()调用的是Cat类中的eat方法,这就是多态!!!
var f: Animal;//声明变量为Animal类型
//f=new Dog("sunny");
f = new Cat("sunny");
console.log(f.eat());
export default{}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值