以下题目是根据网上多份面经收集而来的,题目相同意味着被问的频率比较高,有问题欢迎留言讨论,喜欢可以点赞关注。
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"]
然后是原理,原理确实没认真研究过,在网上翻了一番资料,结合自己的理解简单说下(纯属个人愚见,高手轻喷)
变量与内存之间的关系,主要由三个部分组成:
- 变量名
- 内存地址
- 内存空间
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?
模板字符串都是用反引号表示,如果在模板字符串中需要使用反引号,则前面需要用反斜杠转义。
如果使用模板字符串表示多行字符串,则所有的空格、缩进和换行都会被保留在输出中。
比如<ul>
标签前面会有一个换行。如果想把行首和行尾的换行、空格等去掉,则使用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对象的区别
4.箭头函数this指向问题,箭头函数与普通函数的区别,你说箭头函数没有自己的this,那(()=>{}).bind(this)可以么?
https://juejin.im/post/5b14d0b4f265da6e60393680
六没
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"]
三、处理数组,返回索引值数组
let arr = [1,2,3,4,5,6]
Object.keys(arr) // ["0", "1", "2", "3", "4", "5"]
四、处理字符串,返回索引值数组
let str = "saasd字符串"
Object.keys(str) // ["0", "1", "2", "3", "4", "5", "6", "7"]
五、常用技巧
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模块的加载方式高)。
- 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区别及联系
区别
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)相同。
19.ES6怎么编译成ES5,css-loader原理,过程
Babel 是一个 JavaScript 编译器,Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。Webpack也有自动编译转换能力。
①引入脚本babel库②如下
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指向一个新对象,结果如下图:
即使对象的内容没发生改变,指针改变也是不允许的。
26.ES6新的特性有哪些?x4
1、let const命令
2、解构赋值
3、字符串方法模板字符串
4、默认函数、剩余函数、箭头函数
5、Symbol
6、Set和Map
7、模块化
8、promise
9、class继承
10、数组的扩展
27.Promise对象
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 {<resolved>: "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 对象:直接返回对应的值。