JavaScript --ES6总结
1.var、let、const 区别
1.1作用域不同:块级作用域:声明的变量只在该代码块作用域内有效
- var没有块级作用域,let、const有块级作用域
1.2暂时性死区:
- 只有块级作用域没有let、const命令,声明的变量绑定在这个区域内,不受外部影响
1.3变量提升:变量在声明之前使用
- var存在变量提升,let,const不存在变量提升
1.4能否重复声明:
- var声明可以重复,会覆盖值,let、const不可以重复声明
1.5变量是否被修改
- var、let声明的变量可以被修改,const不可以,但如果是对象,引用的地址不变,内容可以修改。
- const声明之后必须赋值
面试问题
const声明的值一定是不可更改的吗?
const 对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。(const的值并非不可更改)
变量提升,class 声明一个类时,存在变量提升么?为什么?
ES6引入了类的概念,有了class关键字,其实是语法糖的表现,类的实质还是函数对象(腾讯云一面),但是class不存在变量提升,声明定义必须在使用之前,因为要方便类的继承,必须保证子类在父类之后定义。
const声明一个对象,如何让内部属性的值无法改变?
使用Object.freeze()锁死,冻结对象的属性不能修改,不能添加属性
2.解构赋值
阮一峰老师的解析全面又生动,强推
解构赋值语法是JavaScript的表达式,可以将属性/值从对象/数组中取出,赋值给其他变量,本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
2.1数组解构
可以从数组中提取值,按照对应位置,对变量赋值。
2.2对象解构
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值,否则返回undefined
2.3字符串解构
字符串被转换为类似数组的对象
2.4数值和布尔值的解构
如果等号右边是数值和布尔值,则会先转为对象。只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
3.展开运算符
ES6中引入三个点(…)作为展开运算符,把数组或者类数组对象展开成一系列用逗号隔开的值,除此之外还可以用作rest参数剩余运算符。
- 复制数组:
let arr1 = [1,2,3];
let arr2 = [...arr1];
let [...arr2] = arr1//[1,2,3]
- 合并数组
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr3 = [7,8,9];
[...arr1,...arr2,...arr3]//[1,2,3,4,5,6,7,8,9]
- 将字符串转换成数组
[...'xiao']//["x","i","a","o"]
- 除了对数组操作,还可以用于对象合并
4.模板字符串
模板字符串用反引号(``)标识,可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
//普通字符串
`my name is xiao`
//多行字符串
`I am
xiao`
//字符串中嵌入变量,需要用${}括入变量
let name="xiao",age=18;
`My name is ${name}, i am ${age} years old`
- 如果在模板字符串中需要使用反引号,需要用反斜杠转义。
- 空格和换行在模板字符串中是保留的,可以用trim()去掉
5.箭头函数
ES6用()=>{}取代普通函数function(){}的方式,语法上相对更简洁,日常开发应用较多。
- 当参数为空或者参数多个时,需要写()
let fn = a => a;
//普通函数
let fn = function(a){
return a;
}
- 函数体多行语句时,用{}包裹起来,表示代码块,只有一行语句,并且需要返回结果时,可以省略{}
let fn = (a,b) => {
return a-b;
}
- 返回对象时,需要用()把对象括起来
let fn = (a,b) => ({
name:xiao,
age:18
})
箭头函数和普通函数区别:
-
外形不同:箭头函数使用()=>{}定义
-
普通函数可以使用匿名函数,也可以有具体名函数,但是箭头函数只能是匿名函数
-
箭头函数不能用于构造函数,不能使用new;普通函数可以,还可以创建对象实例
-
在普通函数中,this指向调用它的对象,用作构造函数时,指向创建的对象实例;箭头函数this指向不同:
(1)箭头函数没有prototype原型,本身没有this,在定义时可以捕获所在上下文的this,可以是全局window,也可以是继承自外层第一个普通函数的this
(2)结合call,apply,bind使用时,只传入一个参数,对this没有影响,不能改变this指向 -
箭头函数不能绑定arguments,但this指向普通函数时,继承普通函数的arguments,用rest参数或者命名参数的形式访问
-
箭头函数不支持重命名函数参数,普通函数支持,后面的会覆盖前面的
-
其他区别:箭头函数不能用于generator函数,不能使用yeild关键字;没有原型对象;不能用super
注意 面试问题:遇到浏览器不支持箭头函数的情况怎么办?
(1)script标签的type值设为:
<script type="text/babel"><script src="browser.min.js"></script>
如果问到babel原理:
babel转译分为三个阶段:parsing、transforming、generating
例如ES6转ES5:
- ES6代码输入
- babylon进行解析得到AST
- plugin用babel-traverse对AST树进行遍历转译,得到新的AST树
- 用babel-generator通过AST树生成ES5代码
(2)在头部增加polyfill.js
安装方式:
npm install babel-polifill --save
6.rest参数,arguments对象
6.1arguments参数
arguments是函数(除箭头函数)中一个比较特殊的局部变量,是函数的内置属性。
- 所有的函数都内置一个了arguments对象,存储传递的所有实参。
- 它只是一个伪数组,可以进行遍历,拥有length属性,但并不是真正的数组,不能调用数组的方法。
- 可以使用数组的slice方法,Array.prototype.slice.call(arguments),转换为数组。
- arguments的callee属性表示对当前函数的引用,可以实现匿名函数的递归
6.2rest参数
ES6引入rest参数,在参数名称前面加**…,例如…args**表示一个未知数量的参数作为函数的一个数组,因此解决了arguments的很多问题,可以正常使用数组方法。
二者区别:
- rest参数只包含没有对应形参的实参,可以是参数的一部分,而arguments对象包含传给函数的所有实参,是参数的全部
- arguments对象是一个伪数组,rest参数是真正的数组
- arguments对象有附加属性,例如callee属性
- 需要将arguments转化为数组,才可以使用数组方法
- 箭头函数和普通函数都可以使用rest参数
- rest参数可以自定义接受参数的数量,必须是函数最后一位参数,函数的length属性不包括rest参数
7.set、map
7.1set
set是ES6新增的叫做集合的数据结构,类似于数组,但是成员值是唯一的,本身是一个构造函数,用来生成set数据结构。(可以进行数组去重)
方法:
- add()添加值,返回set
- delete()删除值,返回布尔值,表示删除是否成功
- has()判断值是否存在,返回布尔值
- clear(),清除所有成员,没有返回值
遍历:
- keys():返回键名
- value():返回键值
- entries():返回键值对
- forEach():使用回调函数遍历每个成员
扩展运算符和Set 结构相结合实现数组或字符串去重
实现并集、交集、和差集
7.2map
map是一种叫做字典的数据结构,以键值对的形式存储,任何值(对象或者原始值) 都可以作为一个键或一个值。
属性/方法:
- clear() :清除所有元素
- delete(key)删除值,返回布尔值,表示删除是否成功
- get(key):返回指定key的键值,找不到返回undefined
- has(key):判断是否包含某个元素,返回布尔值
- set(key,value):添加元素
- toString():返回字符串表现形式
- valueOf():返回指定对象的原始值
- size属性返回成员总数
遍历:
- keys():返回键名
- value():返回键值
- entries():返回键值对
- forEach():使用回调函数遍历每个成员
map数据结构转数组,用扩展运算符(…)
8.promise
Promise是ES6中新增的一种异步编程的方式,用于解决回调的方式的各种问题,有人说promise是构造函数,有人说是对象,在JavaScript中万物皆对象。
(1)一个promise只有三个状态:
- pending 异步任务正在进行
- resolved(fulfilled)异步任务执行成功
- rejected异步任务失败
(2)创建时必须传递一个参数,否则会报错;
(3)传递函数中参数是两个回调函数,resolve和reject
static resolve(value){
if (value instanceof Promise) {
return value;
}
return new Promise((resolve,reject)=>{
resolve(value);
})
}
static reject(value){
return new Promise((resolve,reject)=>{
reject(value);
})
}
(4)状态一旦发生改变不会再发生变化
(5)同一个promise对象可以添加多个then监听,状态改变时按监听顺序依次执行
(6)then方法每次执行完毕后返回一个新的promise对象
(7)上一个Promise对象的then方法可以给返回的新的Promise的then传递参数
(8)无论上一个是在then成功或者失败的回调中传参,都会传递给新的Promise对象then方法中成功的回调
(9)如果上一个传递的是一个Promise对象,那么传给下一个的成功还是失败由传递的Promsie状态决定
(10)then返回的promise可以捕捉当前then方法的异常
(11)catch就是then方法的语法糖
class Promise{
//....
catch(onRejected){
return this.then(null,onRejected)
}
}
8.1promise.all
汇总多个promise的结果,异步请求并行操作:
- 当所有的结果成功返回时按照请求顺序返回成功
- 当其中一个失败,进入失败方法
static all(promiseArr){
//返回新的promise实例
return new Promise((resolve,reject)=>{
var length = promiseArr.length
var result = [];
for(var i = 0;i<length;i++){
let _promise = promiseArr[i];
function handlerResolve(index,item){
result[index] = item;
//因为promise完成的时间不尽相同 所以用--length来判断合适全部处理完成
--length;
if(length == 0){
resolve(result);
}
}
Promise.resolve(_promise).then((value)=>{
handlerResolve(i,value);
},reason=>{
//有失败则返回
reject(reason)
return;
})
}
})
}
应用场景:
1.多个请求结果合并在一起:一个页面有多个请求,我们需要把所有的请求都返回数据后再一起渲染;
2.合并请求结果并处理错误;
3.验证多个请求结果是否都满足条件:表单验证,多个字段,全部验证通过才可以提交
8.2promise.race
race 函数返回一个 promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolved),也可以是失败(rejected),这要取决于第一个完成的方式是两个中的哪个。
应用场景:
1.图片请求超时;
2.请求超时提示:上一秒刷新闻,下一秒进入电梯,提示网络不佳;
static race(promiseArr){
return new Promise((resolve,reject)=>{
var length = promiseArr.length
for(var i = 0;i<length;i++){
let _promise = promiseArr[i];
//谁先返回结果就返回谁
Promise.resolve(_promise).then(value=>{
resolve(value)
return;
},reason=>{
reject(reason)
return;
})
}
})
}
8.3promise.prototype.then
promise的链式调用
应用场景:
1.下一个请求依赖上个请求的结果;
2.中间件功能使用:返回接口数量比较大,在一个then里面处理显得臃肿,多个渲染数据分别给then。
8.4promise.allSettled
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。
static MyallSettled(promises){
let resArr=new Array(promises.length);
let num=0;
retrun new Promise(resolve=>{
promises.forEach((item,key)=>{
let obj={};
item.then(v=>{
obj['status']="fulfilled";
obj.value=v;
resArr[key]=obj;
num++;
if(num===promises.length){
resolve(resArr);
}
},r=>{
obj['status']="rejected";
obj.reason=r;
resArr[key]=obj;
num++;
if(num===promises.length){
resolve(resArr);
}
})
})
});
}
应用场景:
- 彼此不依赖,其中一个被reject,对其他没有影响
- 期望知道每个promise执行结果
9.迭代、生成
9.1迭代器iterator
迭代器是一种特殊对象,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value,表示下一个将要返回的值;另一个是done,它是一个布尔类型的值,当没有更多可返回数据时返回true。迭代器还会保存一个内部指针,用来指向当前集合中值的位置,每调用一次next()方法,都会返回下一个可用的值
如果在最后一个值返回后再调用next()方法,那么返回的对象中属性done的值为true,属性value则包含迭代器最终返回的值,这个返回值不是数据集的一部分,它与函数的返回值类似,是函数调用过程中最后一次给调用者传递信息的方法,如果没有相关数据则返回undefined
9.2生成器generator
Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
Generator 有两个区分于普通函数的部分:一是在 function 与函数名之间加 * ;函数内部有 yield 表达式。其中 * 表示函数为 Generator 函数,yield 定义函数内部的状态。
一般情况下,yield表达式在next 方法不传入参数时的返回值是 undefined ,当 next 传入参数的时候,参数会作为上一步yield的返回值。yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数。
10.async、await
10.1async
async 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联的。async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
async声明方式:
- 函数声明:
async function fn(){}
- 表达式声明:
let fn = async function(){}
- 对象声明:
let obj = {
async fn(){
}
}
- 箭头函数声明:
let fn = async() => {}
10.2await
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
11.class
ES6引入class,使用原型对象模仿面向对象的类和继承,通过class类创建对象,可以避免重复性代码,满足面向对象的特性。两个或多个对象的结构类似,可以抽象出一个模板,依照模板复制相似的对象。(实质就是继承)
但是JS 中并没有一个真正的 class 原始类型, class 仅仅只是对原型对象运用语法糖。
function声明会存在变量提升,而class不会,需要先声明再使用
11.1constructor
constructor是一个特殊的方法,用来创建并初始化一个对象,在一个class中只能有一个命名为constructor的特殊方法,如果包含多个将会报错。
constructor中可以通过super关键字,调用父类的constructor方法。
11.2继承
ES6提供extends实现类的继承class 子类 extends 父类
class Parent{
constructor(name,age){
this.name=name;
this.age=age;
}
}
class Son extends Parent{
constructor(name,age,score){
super(name,age)
this.score=score;
}
}
- 子类必须在constructor方法中调用super方法
- 在调用super后才可以使用this
也就是先将父类的属性和方法拿过来,才可以继续添加自己的属性和方法
12.symbol
ES6新增数据类型
- 实例是唯一且不可改变的,一种唯一标识符,可用作对象的唯一属性名
- 隐藏性,作为对象属性key使用时,for…in不会遍历到symbol的key
symbol类似一种构造函数,可以创建一个symbol对象,symbol 接收一个参数,表示对生成的 symbol 值的一种描述
let name = Symbol("name")
后续再继续补充…