箭头函数可以与变量解构结合使用。


const full = ({ first, last }) => first + ' ' + last;

// 等同于function full(person) {

  return person.first + ' ' + person.last;}

箭头函数使得表达更加简洁。


const isEven = n => n % 2 == 0;

const square = n => n * n;

上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。

箭头函数的一个用处是简化回调函数。


// 正常函数写法

[1,2,3].map(function (x) {

  return x * x;});

// 箭头函数写法

[1,2,3].map(x => x * x);

另一个例子是


// 正常函数写法var result = values.sort(function (a, b) {

  return a - b;});

// 箭头函数写法var result = values.sort((a, b) => a - b);

使用注意点

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

[](

)四、字符串的扩展


字符串的遍历器接口

ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。


for (let codePoint of 'foo') {

  console.log(codePoint)

}

// "f"

// "o"

// "o"

includes(), startsWith(), endsWith()

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。

  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

let s = 'Hello world!';

s.startsWith('Hello') // true

s.endsWith('!') // true

s.includes('o') // true

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。


'x'.repeat(3) // "xxx"'

hello'.repeat(2) // "hellohello"

'na'.repeat(0) // ""

padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。


'x'.padStart(5, 'ab') // 'ababx'

'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'

'x'.padEnd(4, 'ab') // 'xaba'

模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。


$('#result').append(

  'There are <b>' + basket.count + '</b> ' +

  'items in your basket, ' +

  '<em>' + basket.onSale +

  '</em> are on sale!');

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。


$('#result').append(`

  There are <b>${basket.count}</b> items

   in your basket, <em>${basket.onSale}</em>

  are on sale!

`);

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${}之中。


// 普通字符串

`In JavaScript '/n' is a line-feed.`

// 多行字符串

`In JavaScript this is

 not legal.`

console.log(`string text line 1

string text line 2`);

// 字符串中嵌入变量

let name = "Bob", time = "today";

`Hello ${name}, how are you ${time}?`

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


function fn() {

  return "Hello World";}

`foo ${fn()} bar`

// foo Hello World bar

[](

)五、对象的扩展


属性的简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。


const foo = 'bar';

const baz = {foo};

baz // {foo: "bar"}

// 等同于const baz = {foo: foo};

方法的简写

除了属性简写,方法也可以简写。


const o = {

  method() {

    return "Hello!";

  }};

// 等同于

const o = {

  method: function() {

    return "Hello!";

  }};

这种写法用于函数的返回值,将会非常方便。


function getPoint() {

  const x = 1;

  const y = 10;

  return {x, y};}

getPoint()

// {x:1, y:10}

[](

)六、set和map结构


1.set

基本用法

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值

Set 本身是一个构造函数,用来生成 Set 数据结构。


const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {

  console.log(i);}

// 2 3 5 4

上面代码通过add方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。


const set = new Set([1, 2, 3, 4, 4]);

上面代码也展示了一种去除数组重复成员的方法。

Set 实例的属性和方法

Set 结构的实例有以下属性。

  • Set.prototype.constructor:构造函数,默认就是Set函数。

  • Set.prototype.size:返回Set实例的成员总数。

Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • add(value):添加某个值,返回 Set 结构本身。

  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

  • has(value):返回一个布尔值,表示该值是否为Set的成员。

  • clear():清除所有成员,没有返回值。

  • Array.from方法可以将 Set 结构转为数组。

遍历操作

Set 结构的实例有四个遍历方法,可以用于遍历成员。

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

  • forEach():使用回调函数遍历每个成员

    需要特别指出的是,Set的遍历顺序就是插入顺序。

由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。

这意味着,可以省略values方法,直接用for...of循环遍历 Set。

2.Map

含义和基本用法

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。

这给它的使用带来了很大的限制。

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

实例的属性和操作方法

Map 结构的实例有以下属性和操作方法。

(1)size 属性

size属性返回 Map 结构的成员总数。

(2)set(key, value)

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

(3)get(key)

get方法读取key对应的键值,如果找不到key,返回undefined

(4)has(key)

has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。

(5)delete(key)

delete方法删除某个键,返回true。如果删除失败,返回false

(6)clear()

clear方法清除所有成员,没有返回值。

遍历方法

Map 结构原生提供三个遍历器生成函数和一个遍历方法。

  • keys():返回键名的遍历器。

  • values():返回键值的遍历器。

  • entries():返回所有成员的遍历器。

  • forEach():遍历 Map 的所有成员。

需要特别注意的是,Map 的遍历顺序就是插入顺序。

Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。

结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有mapfilter方法)。

与其他数据结构的互相转换

(1)Map 转为对象

如果所有 Map 的键都是字符串,它可以无损地转为对象。

如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。


function strMapToObj(strMap) {

  let obj = Object.create(null);

  for (let [k,v] of strMap) {

    obj[k] = v;

  }

  return obj;}

const myMap = new Map()

  .set('yes', true)

  .set('no', false);strMapToObj(myMap)

// { yes: true, no: false }

(2)对象转为 Map


function objToStrMap(obj) {

  let strMap = new Map();

  for (let k of Object.keys(obj)) {

    strMap.set(k, obj[k]);

  }

  return strMap;}

objToStrMap({yes: true, no: false})

// Map {"yes" => true, "no" => false}

(3)Map 转为 JSON

Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。


function strMapToJson(strMap) {

  return JSON.stringify(strMapToObj(strMap));}

let myMap = new Map().set('yes', true).set('no', false);strMapToJson(myMap)

// '{"yes":true,"no":false}'

(4)JSON 转为 Map

JSON 转为 Map,正常情况下,所有键名都是字符串。

JSON 转为 Map,正常情况下,所有键名都是字符串。


function jsonToStrMap(jsonStr) {

  return objToStrMap(JSON.parse(jsonStr));}

jsonToStrMap('{"yes": true, "no": false}')

// Map {'yes' => true, 'no' => false}

[](

)七、Promise对象


Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

基本用法

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

下面代码创造了一个Promise实例。


const promise = new Promise(function(resolve, reject) {

  // ... some code

  if (/* 异步操作成功 */){

    resolve(value);

  } else {

    reject(error);

  }});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

为了表述方便,后面的resolved统一只指fulfilled状态,不包含rejected状态。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。


promise.then(function(value) {

  // success}, function(error) {

  // failure});

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。


function timeout(ms) {

  return new Promise((resolve, reject) => {

    setTimeout(resolve, ms, 'done');

  });}

timeout(100).then((value) => {

  console.log(value);});

Promise.prototype.then()

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。


getJSON("/posts.json").then(function(json) {

  return json.post;}).then(function(post) {

  // ...});

上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数.

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。


etJSON('/posts.json').then(function(posts) {

  // ...}).catch(function(error) {

  // 处理 getJSON 和 前一个回调函数运行时发生的错误  console.log('发生错误!', error);});

上面代码中,getJSON方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。

Promise.prototype.finally()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。


const p = Promise.all([p1, p2, p3]);

上面代码中,不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。

Promise.all()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。


const p = Promise.all([p1, p2, p3]);

Promise.all方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race()

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。


const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

[](

)八、模块化


概述

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。


 { stat, exists, readFile } from 'fs';// ES6模块import

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载。

严格模式

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。

严格模式主要有以下限制。

  • 变量必须声明后再使用

  • 函数的参数不能有同名属性,否则报错

  • 不能使用with语句

  • 不能对只读属性赋值,否则报错

  • 不能使用前缀 0 表示八进制数,否则报错

  • 不能删除不可删除的属性,否则报错

  • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]

  • eval不会在它的外层作用域引入变量

  • evalarguments不能被重新赋值

  • arguments不会自动反映函数参数的变化

  • 不能使用arguments.callee

  • 不能使用arguments.caller

  • 禁止this指向全局对象

  • 不能使用fn.callerfn.arguments获取函数调用的堆栈

  • 增加了保留字(比如protectedstaticinterface

export 命令

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。


// profile.jsexport var firstName = 'Michael';

export var lastName = 'Jackson';

export var year = 1958;

export的写法,除了像上面这样,还有另外一种。


/ profile.jsvar firstName = 'Michael';var lastName = 'Jackson';var year = 1958;

export {firstName, lastName, year};

上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。

export命令除了输出变量,还可以输出函数或类(class)。

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。



function v1() { ... }function v2() { ... }

export {

  v1 as streamV1,

  v2 as streamV2,

**这里分享一份由字节前端面试官整理的「2021大厂前端面试手册」,内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题、数据结构与算法。全部整理在下方文档中,共计111道**

### HTML

*   HTML5有哪些新特性?

*   Doctype作?? 严格模式与混杂模式如何区分?它们有何意义?

*   如何实现浏览器内多个标签页之间的通信?

*   ?内元素有哪些?块级元素有哪些? 空(void)元素有那些??内元 素和块级元素有什么区别?

*   简述?下src与href的区别?

*   cookies,sessionStorage,localStorage 的区别?

*   HTML5 的离线储存的使用和原理?

*   怎样处理 移动端 1px 被 渲染成 2px 问题?

*   iframe 的优缺点?

*   Canvas 和 SVG 图形的区别是什么?

![](https://s2.51cto.com/images/20210914/1631605377120210.jpg)

### JavaScript

**[CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://ali1024.coding.net/public/P7/Web/git)**

*   问:0.1 + 0.2 === 0.3 嘛?为什么?

*   JS 数据类型

*   写代码:实现函数能够深度克隆基本类型

*   事件流

*   事件是如何实现的?

*   new 一个函数发生了什么

*   什么是作用域?

*   JS 隐式转换,显示转换

*   了解 this 嘛,bind,call,apply 具体指什么

*   手写 bind、apply、call

*   setTimeout(fn, 0)多久才执行,Event Loop

*   手写题:Promise 原理

*   说一下原型链和原型链的继承吧

*   数组能够调用的函数有那些?

*   PWA使用过吗?serviceWorker的使用原理是啥?

*   ES6 之前使用 prototype 实现继承

*   箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?

*   事件循环机制 (Event Loop)

![](https://s2.51cto.com/images/20210914/1631605378612071.jpg)