ES6有哪些新特性
-
新增let、const声明变量,实现了块级作用域
let 块级作用域,没有变量提升,暂时性死区,块级作用域内不允许重复声明
暂时性死区:
在块级作用域内,若存在用let命令声明的变量,则所在区块对该变量形成封闭作用域,即该变量无视外部的同名变量。
而又因为不存在变量提升,因此在未使用let声明变量前,该变量都不可使用,其在语法上称为“暂时性死区”。
let a = 1; if(true){ a = 2; //出错 not defined let a; }var btns=document.getElementsByTagName('button') console.log(btns) for(var i=0;i<btns.length;i++){//如果使用var,每个打印的都是3,let则是123 btns[i].addEventListener('click',function(){console.log(i)}) } -
新增箭头函数
-
引入promise、await/async解决异步回调问题
-
引入class作为对象的模板,实现更好的面向对象编程
-
引入模块方便模块化编程
-
引入新的数据类型symbol,新的数据结构set和map
async函数
async函数返回一个Promise对象,可以使用then方法添加回调函数
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<fulfilled>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
async函数中可能会有await表达式,async函数执行时,如果遇到await就会先暂停执行,等到触发的异步操作完成之后,(await如果后面的出错 了,那么之后的语句就不会执行了)恢复async函数的执行并返回解析值
await关键字仅在async function中有效,如果在async function函数体外使用,只会得到一个语法错误
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(){
const x = await testAwait('helloworld')
console.log(x)
}
helloAsync()//helloworld
function testAwait(x){
setTimeout(()=>{
console.log(x)
},3000)
}
async function helloAsync(){
await testAwait('helloworld')
console.log('11')
}
helloAsync()//先输出11,await期待的是promise,不是的话,按照普通顺序
await其实就是promise then的语法糖
如果要捕获错误的话得用try/catch
function testAwait(x){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(x)
},2000)
})
}
async function helloAsync(){
try{
const x = await testAwait('helloworld')
console.log(x)
}catch(err){
console.error(err)
}
}
helloAsync()//helloworld
但是我尝试直接捕获好像也可以成功,不用加try catch
function testAwait(x){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(x)
},2000)
})
}
async function helloAsync(){
const x = await testAwait('helloworld').catch(err=>{console.error(err)})
console.log(x)
}
helloAsync()//helloworld(err版本的)
Symbol的作用
唯一
a.js
const PASSWORD = Symbol()
class Login { constructor(username, password) { this.username = username this[PASSWORD] = password } checkPassword(pwd) { return this[PASSWORD] === pwd } } export default Login
b.js
import Login from './a'
const login = new Login('admin', '123456') login.checkPassword('123456') // true login.PASSWORD // oh!no! login[PASSWORD] // oh!no! login["PASSWORD"] // oh!no!
私有化:由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
普通函数和箭头函数的区
-
定义不同
箭头函数带箭头,普通函数不带
-
箭头函数全都是匿名函数
普通函数有匿名函数,也可以是具名函数
-
this指向不同
var b=33
let obj={
a:12,
fn(){
console.log(this.a)
},
arrowfn:()=>{
console.log('arrow',this.a)
console.log('arrow',this.b)
}
}
obj.fn()//12
obj.arrowfn()//undefined 33
var test=obj.fn
var testarrow=obj.arrowfn
test()//无调用者,this隐式丢失指向window
testarrow()//与之前的输出结果一样,没有影响
普通函数的this指向调用者,箭头函数的this指向其定义位置的上下文this,即此处this指向obj的外层
- 箭头函数不具有prototype属性
function Foo(){}
let Foo2=()=>{}
let Foo3=function(){}
console.log(Foo.prototype)//{constructor: ƒ}
console.log(Foo2.prototype)//undefined
console.log(Foo3.prototype)//{constructor: ƒ}
5 . 箭头函数不能用于构造函数
ES6模块化
-
ES6模块中自动采用严格模式,规定:
- 变量必须先声明
- 函数参数不能有同名属性
- 禁止this指向全局
- 对只读属性赋值、删除不可删除属性直接报错
- arguments不可重新赋值,不会自动反应函数参数变化
- 增加保留字static、interface、producted等
-
export export语句输出的接口是对应值的引用,也就是一种动态绑定关系,通过该接口可以获取模块内部实时的值;export命令要处于模块顶层
- 把export直接加到声明前面
- export {a, b, c}
- export default默认导出(一个js文件中只能有一个默认导出,但可以导出多个方法)
-
import import是静态执行,Singleton模式;import命令要处于模块顶层
- import {XX} from ‘./test.js’
- import {XX as YY} from ‘./test.js’
- import * as YY from ‘./test.js’
CommonJS与ES6模块化的区别
-
对于模块的依赖,CommonJS是动态的,ES6 Module 是静态的
对于模块的依赖,何为动态?何为静态?
动态是指对于模块的依赖关系建立在代码执行阶段; 静态是指对于模块的依赖关系建立在代码编译阶段;
-
CommonJS导入的是值的拷贝,ES6 Module导入的是值的引用
JS的new操作符做了哪些事情
- 创建一个类的实例:创建一个空对象obj,将obj.proto设置为构造函数的prototype
- 初始化实例:构造函数被传入参数并调用,this指针被设定为指向该实例obj
- 返回实例obj
JS单例模式
- 定义 1.只有一个实例 2.可以全局访问
- 主要解决的问题:一个全局使用的类 频繁的创建和销毁
- 何时使用:当你想控制实例的数目 节省系统化资源的时候
- 如何实现:判断系统是否有这个单例,如果有则返回,没有则创建
- 单例模式的优点:内存中只有一个实例 减少了内存的开销 尤其是频繁的创建和销毁实例(比如首页页面的缓存)
ES6创建单例模式
class Person{
constructor(name,sex,hobby){
this.name = name
this.sex = sex
this.hobby = hobby
}
static getInstance(name,sex,hobby){//必须加static
if(!this.instance){
this.instance = new Person(name,sex,hobby)
}
return this.instance
}
}
let person1 = Person.getInstance('胡图图','男','吃瓜')
let person2 = Person.getInstance('胡英俊','女','打豆豆')
console.log(person1==person2)//true
console.log(person1)//'胡图图','男','吃瓜'
console.log(person2)
手写map
var arr=[1,2,3]
function map(arr,mapCallback){
//检查参数是否正确
if(!Array.isArray(arr)||!arr.length||typeof mapCallback!=="function"){return []}
else{
let result = []
for(let i=0;i<arr.length;i++){
result.push(mapCallback(arr[i],i,arr))//1每一项 2索引 3传入数组
}
return result
}
}
var res = map(arr,(item)=>{
return item*2
})
console.log(res)//2,4,6
原型链写法+reduce
let arr=[1,2,3,4]
Array.prototype.mymap=function(callback){
return this.reduce((pre,cur)=>{
return pre.concat(callback(cur))
},[])
}
原型链+for
let arr=[1,2,3,4]
Array.prototype.mymap=function(callback){
let newarr=[]
for(let value of this){
newarr.push(callback(value))
}
return newarr
}
手写reduce
let arr=[1,2,3,4]
Array.prototype.myreduce=function(callback,initValue){
var total=initValue?initValue:this[0]
for(let i=initValue?0:1;i<this.length;i++){
total=callback(total,this[i],i,this)
}
return total
}
console.log(arr.myreduce((pre,cur)=>{
return pre+cur
},10))
手写Promise
class mPromise{
constructor(process){
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this),this.reject.bind(this))//一定要bind,不然then中无法使用箭头函数,为啥要用bind,因为这个地方如果用call,那么resolve这个函数直接就在参数里面执行了
return this
}
resolve(val){
this.status = 'fulfilled'
this.msg = val
}
reject(err){
this.status = 'rejected'
this.msg = err
}
then(resolve,reject){
if(this.status=='fulfilled'){resolve(this.msg)}
if(this.status=='rejected'){reject(this.msg)}
}
}
var my_promise = new mPromise(function(resolve, reject) {
resolve('OK');
});
my_promise.then(function(val) {
console.log(`success: ${val}`);
}, function(err){
console.log(`failed: ${err}`);
});
Promise实现sleep
function mysleep(time){
return new Promise((resolve)=>setTimeout(resolve,time))
}
mysleep(5000).then(()=>{
console.log("i sleep 5s")
})
setTimeout实现setInterval
function mySetInterval(fn, millisec){
function interval(){
setTimeout(interval, millisec);//由于为异步,所以先执行后面的
fn();
}
mySetInterval.timer=setTimeout(interval, millisec)
}
mySetInterval.clear=function(){
clearTimeout(mySetInterval.timer)
}
mySetInterval(()=>{
console.log(1)
},1000)
mySetInterval.clear()
Promise链式调用
可以用来解决回调地狱
class Task{
constructor(){
this.do()
}
do(){
this.log(1).then(res=>{
return this.log(2)
}).then(res=>{
return this.sleep(3)
}).then(res=>{
return this.log(4)
}).then(res=>{
return this.sleep(2)
}).then(res=>{
return this.sleep(3)
}).then(res=>{
return this.log(5)
})
}
log(count){
return new Promise(resolve=>{
console.log(count)
resolve()
})
}
sleep(time){
return new Promise(resolve=>{
setTimeout(()=>{
resolve()
},time*1000)
})
}
}
const t = new Task()
Promise.all
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promise.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
Promise的几种方法
Promise.all
-
所有成功才成功,一个失败就失败
失败返回失败的值,成功返回成功一个数组,数组中记录着成功的值 eg:['成功了', 'success']
手写
Promise.myall=function(iterator){ let count=0//用于计数,等于len的时候就resolve let len = iterator.length let res=[] return new Promise((resolve,reject)=>{ for(let i in iterator){//一定不能var Promise.resolve(iterator[i]) .then(data=>{ res[i]=data if(++count===len){resolve(res)} }) .catch(err=>{ reject(err) }) } }) } var p1=Promise.resolve(1) var p2=Promise.resolve(2) var p3=Promise.resolve(3) Promise.myall([p1,p2,p3]).then(res=>{ console.log(res) }).catch(err=>{ console.error(err) })
Promise.allSettled
- 返回一个对象数组
- 每一个promise的状态都包含在其中
Promise.race
-
所有的promise进行赛跑,返回最快成功或者失败的那个
手写
Promise.myrace=function(iterators){ return new Promise((resolve,reject)=>{ for(let p of iterators){ Promise.resolve(p) .then(res=>{ resolve(res) }) .catch(err=>{ reject(err) }) } }) } var p1=Promise.reject(1) var p2=Promise.resolve(2) var p3=Promise.resolve(3) Promise.myrace([p1,p2,p3]).then(res=>{ console.log(res) }).catch(err=>{ console.error(err) })
Promise.any
- 与Promise.all相反
- 一个成功就成功,所有失败才失败
一旦有一个成功,则返回这个成功的值,如果全部失败,返回一个AggregateError: All promises were rejected
模拟实现数组push
let arr=[1,2,3,4,5]
Array.prototype.mypush=function(){//如果用箭头函数里面不能使用arguments
for(let i=0;i<arguments.length;i++){
this[this.length]=arguments[i]
}
return this.length
}
arr.mypush(2)