面试题 - 前端 - ES6-ES13

ER6 新增方法

  1. let 和 const,解构赋值、模板字符串、箭头函数。
  2. Symbol、Map、Set 三种常用的数据类型。
  3. Proxy 重新定义了数据劫持的能力. (相对于 Object.defineProperty, Proxy 更多功能,例如可以劫持数组)。
  4. Reflect 定义了一套标准化的数据操作的方式。
  5. Promise 确实的解决了异步逻辑嵌套及回调地狱问题。定义了异步逻辑的三种状态 pending、rejected、fullfilled, 搭配 then、catch、all、race 等方法以及 async await 语法糖,大量简化了异步操作。
  6. Generator 函数,可以将异步逻辑划片执行(async/await 是它的语法糖)。
  7. 新增了 class 类的概念
  8. ES6 Modules

var, let, const 区别

ES6 新增了定义变量的关键字 let 和 const, 分别用于定义块级变量和常量。
var 有提前声明的特性。而 let, const 不会声明提前, 存在暂时性死区。
外部无法使用到内部的 let 和 const 定义的变量, 存在块级作用域限制。
const 定义的常量, 无法更改。

解构赋值

按照对象或者数组的结构,把对应的属性值或项提取出来。例如后端返回了一个超大的对象,而前端不需要这个对象里面的所有属性值。这时用解构赋值就使代码非常简洁。

1
let { type, payload } = data; // {type:"",payload:""}

模版字符串什么时候用?

在动态创建 HTML 页面,一旦涉及到换行,使用模版字符串就比用\n 方便。

箭头函数

函数类型 语法格式 new 和原型 arguments
super
new.target
this 指向 call, apply 和 bind
普通函数 function() {}
函数声明
函数表达式
函数被调用的时候决定
谁调用就指向谁
修改 this 值
箭头函数 () => {}
函数表达式
没有
可调用外围
没有
可调用外围
函数被定义的时候决定
指向上一层
不可修改 this 值

new 和原型:

1
2
3
4
5
6
7
function Normal() {}
Normal.prototype.name = '原型name';
const normal = new Normal();
console.log(normal.name); // 原型name

const Func = () => {};
const func = new Func(); // TypeError: Func is not a constructor

arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function normal() {
return arguments.length;
}
console.log('今天吃了' + normal(1, 2, 3) + '碗饭'); // 今天吃了3碗饭

let arrow = () => arguments.length;
console.log(arrow(1, 2, 3)); // ReferenceError: arguments is not defined

// 调用外围
function normal2() {
return () => arguments.length;
}
let arrow2 = normal2(1, 2, 3);
console.log('今天吃了' + arrow2() + '碗饭'); // 今天吃了3碗饭

this 指向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let normal = {
name: 'normal',
introduce: function () {
// console.log(this.name);
return function () {
return this.name;
};
},
};
normal.introduce();

let arrow = {
name: 'arrow',
introduce: () => {
console.log(this);
},
};
arrow.introduce();

箭头函数是 ES6 推出的,所以在低版本浏览器是有兼容问题的,语法简介明了,逻辑更清晰。

传统的函数

箭头函数没有自己的 this,this 指向外部的 this,并且 this 会在创建的时候就绑定好.

人话:谁调用箭头函数,箭头函数的 this 就指向谁。

1
2
3
4
5
6
7
8
9
10
11
12
const fn1 = function () {
console.log(this);
};
fn1(); // window
const obj = {
name: 'tom',
fn2() {
fn1(); // window
console.log(this); // obj
},
};
obj.fn2();

细说 this 指向

普通函数的 this 在调用的时候决定

人话:该普通函数的调用者,它是谁点出来,该普通函数的 this 就是谁。找不到就是 window 对象;构造函数的 this 就是对象本身。

独立调用的函数都归属于 window

1
2
3
4
function test() {
console.log(this); //this指向window。test的调用者就是它本身。找不到它是谁点出来的,那this就指向window。
}
test();

用对象调用

1
2
3
4
5
6
7
let obj = {
name: 'obj',
foo: function () {
console.log(this); //this指向obj。this所在匿名函数,该匿名函数的调用者是foo,foo是obj点出来的。所以this指向obj。
},
};
obj.foo();
1
2
3
4
5
6
7
8
9
10
11
let obj = {
name: 'obj',
foo: function () {
console.log(this); //this指向obj。理由同上
function test() {
console.log(this); //this指向window。test的调用者就是它本身。找不到它是谁点出来的,那this就指向window。
}
test();
},
};
obj.foo();
1
2
3
4
5
6
7
8
let obj = {
name: 'obj',
foo: function () {
console.log(this); //this指向window。this所在匿名函数,该匿名函数的调用者是bar, 找不到它是谁点出来的,那this就指向window。
},
};
let bar = obj.foo;
bar();

函数作为参数

自定义函数作参数,this 指向 window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo() {
console.log(this); //this指向window
}
function bar(fn) {
fn();
}
const obj1 = {
name: 'obj1',
foo: foo,
};
const obj2 = {
bar: bar,
};
obj2.bar(obj1.foo);

setTimeout 的函数参数,this 指向 window

1
2
3
setTimeout(function () {
console.log(this); //this指向window
}, 0);

DOM 元素事件的函数参数,this 指向 DOM 元素。

1
2
3
4
5
6
7
document.getElementById('btn').addEventListener(
'click',
function () {
console.log(this); //this指向 HTMLButtonElement 元素
},
true,
);

forEach 里第一个参数是一个函数,第二个参数就是 this 绑定的对象,不写默认绑定 window。

1
2
3
4
5
6
7
const numbers = [1, 2, 3, 4];
numbers.forEach(
function (currentValue) {
console.log(currentValue + this.name); //this 指向{ name: 'test' }
},
{ name: 'test' },
);

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

人话:箭头函数在定义的时候,一层一层向上找,找到离自己最近的 this,就指向它。(只要有对象出现,对象里就有 this)

箭头函数独立调用的函数都归属于 window

1
2
3
4
const test = () => {
console.log(this); //this指向window。因为箭头函数定义的时候,向上一层找,上一层就是window对象,所以this指向window.
};
test();

箭头函数用对象调用

1
2
3
4
5
6
7
let obj = {
name: 'obj',
foo: () => {
console.log(this); //this指向window。因为箭头函数定义的时候,向上一层找,上一层就是window对象,所以this指向window.
},
};
obj.foo();
1
2
3
4
5
6
7
8
9
10
11
let obj = {
name: 'obj',
foo: function () {
console.log(this); //this指向obj。this所在匿名函数,该匿名函数的调用者是foo,foo是obj点出来的。所以this指向obj。
const test = () => {
console.log(this); //this指向obj。因为箭头函数定义的时候,向上一层找,上一层就是obj对象,所以this指向obj.
};
test();
},
};
obj.foo();

用过哪些异步处理方案

  1. 回调函数
  2. Promise
  3. generator 生成器 yield
  4. async await

Promise 怎么解决回调地狱

链式回调:

1
2
3
4
5
6
7
8
9
axios.get("1.php").then(res=>{
return axios.get(2.php,{res})
}).then(res=>{
return axios.get(3.php)
}).then(res=>{
console.log(res.data)
}).catch(error=>{
console.log(error)
})

async await:

1
2
3
4
5
6
7
8
async function test() {
var a = await axios.get(1);
var b = await axios.get(2, { a });
var c = await axios.get(3, { b });
console.log(c);
}

test();

Promise 对象有什么好用的方法?

  • Promise.all()中的 Promise 序列会全部执行通过才认为是成功,否则认为是失败;
  • Promise.race()中的 Promise 序列中第一个执行完毕的是通过,则认为成功,如果第一个执行完毕的 Promise 是拒绝,则认为失败;
  • Promise.any()中的 Promise 序列只要有一个执行通过,则认为成功,如果全部拒绝,则认为失败;

class 是 构造函数的语法糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
say = () => {};
}

class Test extends person {
constructor(name, age, location) {
super(name, age);
this.location = location;
}
}

ES6 模块化规范是什么?

人话:引入一个模块用 import … from 或者 import {某几个方法} from。导出用 export default 或者 export {某几个方法 }。

1
2
3
4
5
6
import obj from './a';
export default aaa;

import { test } from './b';
export { test };
export var test = function () {};

模块化发展历史

  1. AMD 规范:前端 异步加载 - 提前下载, 提前加载 require.js
  2. CMD 规范:异步加载 - 提前下载 , 按需加载 – 玉伯 -sea.js
  3. CommonJs 规范:同步加载,用在 NodeJS 环境
1
2
3
require('./b');
module.exports;
exports;
  1. ES6 模块化规范

CommonJs 不可以按需饮用,ES6 模块化可以。

异步遍历器生成函数(大厂面试)

Generator 函数返回一个同步遍历器,异步 Generator 函数的作用,是返回一个异步遍历器对象。在语法上,异步 Generator 函数就是 async 函数与 Generator 函数的结合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function timer(t) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(t);
}, t);
});
}

async function* fn() {
yield timer(1000); //任务1
yield timer(2000); //任务2
yield timer(3000); //任务3
}

// 使用一下 for await ...of
async function fn1() {
for await (const val of fn()) {
console.log('start', Date.now());
console.log(val);
console.log('end', Date.now());
}
}
fn1();