1. 认识 ES6
1.1 ECMAScript 和 JavaScript 关系
ECMA
是 “European Computer Manufacturers Association"
的缩写,中文称欧洲计算机制造联合会
ECMA
是标准
,JavaScript
是实现
ECMAScript
是跨多平台的许多厂商(包括浏览器厂商在内的各方组成)实施的不断发展的标准ES6(ECMAScript 2015)
花费六年的时间敲定,是一个很大的发行版
2. ES6 兼容性解决
var fn = (
v => console. log ( v) ;
)
转为
"use strict"
var fn = function fn ( v ) {
return console. log ( v) ;
} ;
2.1 使用 Babel 工具搭建ES6项目环境
2.1.1 nodejs 安装
2.1.2 初始化工程项目
# 使用默认配置初始化项目
npm init - y
2.1.3 安装对应的依赖包
# 本地安装babel- cli及对应的转化规则
npm install -- save- dev babel- cli babel- preset- es2015
2.1.4 创建项目目录
src
目录用于存放编写的 ES6 代码dist
目录用于存放由ES6转化后的代码
2.1.5 配置babel
{
"presets" : [ "es2015" ]
}
2.2 Babel 兼容性列表
ES6 特性 兼容性 箭头函数 支持 类的声明和继承 部分支持,IE8不支持 增强的对象字面量 支持 字符串模版 支持 解构 支持,但注意使用方式 参数默认值,不定参数,拓展参数 支持 let 与 const 支持 for of IE 不支持 iterator,generator 不支持 模版 module,Proxies,Symbol 不支持 Map,Set 和 WeakMap,WeakSet 不支持 Promises,Math,Number,String,Object 的新 API 不支持 export & import 支持 生成器函数 不支持 数组拷贝 支持
3. let 和 const
3.1 var 命令
var 可以重复声明
var 无法限制修改
var 没有块级作用域,会进行变量提升
var a = 10 ;
var a = 20 ;
console. log ( a) ;
var a = 10 ;
a = 30 ;
if ( true ) {
var a = 10 ;
console. log ( a) ;
}
console. log ( a) ;
3.2 let 命令
let 不能重复声明
let 具有块级作用域,暂存性死区,不会变量提升
let a = 10 ;
let a = 20 ;
console. log ( a) ;
例子一:
if ( true ) {
let a = 10 ;
console. log ( a) ;
}
console. log ( a) ;
例子二:
for ( var i= 0 ; i< 10 ; i++ ) {
console. log ( i) ;
}
console. log ( i) ;
for ( let i= 0 ; i< 10 ; i++ ) {
console. log ( i) ;
}
console. log ( i) ;
< input type = " button" value = " one" >
< input type = " button" value = " two" >
< input type = " button" value = " three" >
< script>
window. onload = function ( ) {
var btns = document. getElementsByTagName ( "input" ) ;
for ( var i= 0 ; i< btns. length; i++ ) {
( function ( i ) {
btns[ i] . onclick = function ( ) {
console. log ( i + 1 ) ;
}
} ) ( i) ;
}
for ( let i= 0 ; i< btns. length; i++ ) {
btns[ i] . onclick = function ( ) {
console. log ( i + 1 ) ;
}
}
}
</ script>
3.3 const 命令
const 命令声明一个只读
的常量,如声明的是简单类型的数据,常量的值就不能改变
、不可重复声明
,一旦声明,就必须立即初始化
const 具有块级作用域,暂存性死区,不会变量提升
const PI = 3.1415 ;
console. log ( 'PI=' + PI ) ;
PI = 3 ;
const a = 10 ;
const foo;
3.4 const 命令本质
const
实际上保证的,并不是变量的值不得改动
,而是变量指向的那个内存地址不得改动
简单类型
的数据(数值,字符串,布尔值
)
复合类型
的数据(主要是对象
和数组
)
变量指向的内存地址
,保存的只是一个指针
,const
只能保证这个指针
是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了
const foo = { } ;
foo. prop = 123 ;
console. log ( foo. prop) ;
foo = { } ;
分析:上面的代码中,常量 foo 存储的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 foo 指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性
4. 数据类型 Symbol
4.1 Symbol 类型介绍
ES6 之前的JavaScript数据类型
Number(数字) String(字符串) Boolean(布尔值) Object(对象) Null(空对象指针) Undefined(声明的变量未被初始化时) 引入的背景
对象的属性名容易产生命名冲突,为保证健名的唯一性
ES6引入Symbol这种新的原始数据类型,确保创建的每个变量都是独一无二的
特点
Symbol类型的数据是类似字符串的数据类型,由于Symbol函数返回的值是原始类型的数据,不是对象,故Symbol函数前不能使用new命令,否则会报错
可选参数,由于控制台输出不同的Symbol变量时都是Symbol(),故为了区分,可在创建Symbol变量时传入参数进行区分
let a1 = Symbol ( 'a1' )
let a2 = Symbol ( 'a2' )
let b1 = Symbol. for ( 'kk' )
let b2 = Symbol. for ( 'kk' )
console. log ( b1 === b2)
5. 解构赋值
5.1 介绍
解构赋值可以理解为赋值操作的语法糖,它是针对数组或者对象进行模式匹配,然后对其中的变量进行赋值 代码书写上言简意赅,语义明确,也方便了对象数据的读取操作
5.2 实质
ES6中只要某种数据有Iterator接口(也就是可以循环迭代),都可以进行数组的解构赋值
5.3 使用场景
5.3.1 数组解构
{
let a, b, c
[ a, b, c] = [ 1 , 2 ]
console. log ( a, b, c)
}
{
let a, b, c
[ a, b, c = 6 ] = [ 1 , 2 ]
console. log ( a, b, c)
}
{
let a, other
[ a, ... other] = [ 1 , 2 , 3 ]
console. log ( a, other)
}
{
let a, b
[ a, , b] = [ 1 , 2 , 3 ]
console. log ( a, b)
}
5.3.2 对象解构
{
let a, b
( { a, b} = { a: 2 , b: 3 } )
console. log ( a, b)
}
{
let num, total
( { a: num, b: total} = { a: 2 , b: 3 } )
console. log ( num, total)
}
{
function fn ( ) {
return {
name: 'Nick' ,
nameList: [ {
name: 'kk'
} ]
}
}
let b = fn ( ) ;
let { name: person, nameList: [ { name: otherPerson} ] } = b;
console. log ( person, otherPerson)
}
6. 字符串和模版字符串
6.1 ES5 处理 Unicode 的缺陷
6.2 加强对 Unicode 的支持
在ES5中JavaScript允许采用\uxxxx
形式表示一个字符,其中xxxx表示字符的Unicode码点
这种表示法只限于码点在\u0000-\uFFFF之间的字符
超出这个范围的字符,必须用两个双字节的形式表示,但是ES5却无法正确的识别这个有两个字节组成的字符
ES6中,JavaScript增加了对超出\u0000-\uFFFF Unicode范围的字符支持 ES6的方案:将超过两个字节的组成的字符的码点放在一对花括号李就可以正确的识别
{
const str1 = 'a'
const str2 = '\u20bb7'
console. log ( str2)
}
{
const str3 = '\u{20bb7}'
console. log ( str3)
}
6.3 字符串的遍历
{
const str3 = '\u{20bb7}'
for ( let i = 0 ; i < str3. length; i++ ) {
console. log ( str3[ i] )
}
for ( let word of str3) {
console. log ( word)
}
}
6.4 扩展的 API
方法 描述 includes(string, position) 判断字符串中是否包含指定字符串,返回值是布尔值 startsWith(string, position) 判断字符串的开头是否包含指定字符串,返回值是布尔值 endsWith(string, position) 判断字符串的尾部是否包含指定字符串,返回值是布尔值 repeat(n) repeat()方法返回一个新字符串,表示将原字符串重复n次 字符串补全 第一个参数是补全后的字符串长度,第二个参数是用于补全的字符串 padStart(length, str) 用于头部补全 padEnd(length, str) 用于尾部补全
{
let str = '123Nick321'
console. log ( 'includes' , str. includes ( 'Nick' ) )
console. log ( 'startsWith' , startsWith ( 'Nick' , 3 ) )
console. log ( 'endsWith' , endsWith ( 'Nick' , 7 ) )
}
{
let str = '123Nick321'
str = str. repeat ( 3 )
console. log ( 'repeat' , str)
}
{
let str = 'Apple'
str = padStart ( 8 , 'asdsd' )
console. log ( 'padStart' , str)
}
6.5 模版字符串
$ ( '#result' ) . append (
'There are <br>' + basket. count + '</b>' +
'items in your basket,' +
'<em>' + basket. onSale +
'</em> are on sale'
) ;
$ ( '#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 line2 ` ) ;
var name = "Bob" , time = "today" ;
console. log ( ` Hello ${ name} , how are you ${ time} ? ` ) ;
使用模版字符串的注意事项
在模版字符串中如需使用反引号,反引号前要用反斜杠转义 使用模版字符串表示多行字符串时,所有的空格和缩进都会保留在输出之中 模版字符串中引入变量,要用${变量名}这样的形式引入才可以 大括号中的值不是字符串时,将按照一般的规则转为字符串
比如,大括号中是一个对象,将默认调用对象的toString方法 模版字符串中的${…}大括号内部可以放入任意的JavaScript表达式,可以进行运算、可以引用对象属性、可以调用函数、可以嵌套,甚至还能调用自己本身
7. 数组的扩展方法及扩展运算符的使用
7.1 扩展运算符的使用
7.1.1 复制数组
const list = [ 1 , 2 , 3 , 4 , 5 ]
let list2 = [ ... list]
console. log ( list2)
7.1.2 分割数组
const list = [ 1 , 'a' , 'b' , 'c' ]
let [ , ... list2] = list
console. log ( list2)
7.1.3 将数组转化成参数传递给函数
function add ( x, y ) {
return x + y
}
let addList = [ 1 , 2 ]
console. log ( add ( ... addList) )
7.2 新增的常用方法
7.2.1 fill 填充数据
const list = [ 1 , 2 , 3 , 4 , 5 ]
let list2 = [ ... list] . fill ( 3 )
let list3 = [ ... list] . fill ( 3 , 1 , 4 )
console. log ( list2, list3)
7.2.2 find & findIndex
const list = [ { title: 'es6' } , { title: 'web' , id: 2 } , { title: 'vue' } , { title: 'web' , id: 3 } ]
let result = list. find ( function ( item ) {
return item. title === 'webpack'
} )
let resultIndex = list. findIndex ( function ( item ) {
return item. title === 'webpack'
} )
console. log ( result, resultIndex)
7.2.3 includes & indexOf
const list = [ 1 , 2 , 3 , 4 , 5 , 6 ]
let result = list. includes ( 2 )
console. log ( 'includes' , result)
7.2.4 flat
const list = [ 1 , 2 , 3 , [ '2nd' , 4 , 5 , 6 ] ]
let flatList = [ ] . concat ( ... list)
console. log ( flatList)
let flatList2 = list. flat ( 2 )
console. log ( 'flat' , flatList2)
7.2.5 map 数据映射
const json = [ { title: 'es6' , status: 1 } , { title: 'react' , status: 0 } , { title: 'webpack' , status: 1 } , { title: 'vue' , status: 1 } }
let video = json. map ( function ( item ) {
let obj = { }
Object. assign ( obj, item)
obj. status = item. status ? '已上线' : '未上线'
return obj
} )
console. log ( 'json' , json)
console. log ( 'video' , video)
7.2.6. reduce 汇总
const letterList = 'abcadefrd'
const result = letterList. split ( '' ) . reduce ( function ( acc, cur ) {
acc[ cur] ? acc[ cur] ++ : acc[ cur] = 1
return acc
} , { } )
console. log ( result)
const list = [ 1 , [ '2nd' , 2 , 3 , [ '3rd' , 4 , 5 ] ] , [ '2nd' , 6 , 7 ] ]
const deepFlat = function ( list ) {
return list. reduce ( function ( acc, cur ) {
return acc. concat ( Array. isArray ( cur) ? deepFlat ( cur) : cur)
} , [ ] )
}
let flatList = deepFlat ( list)
console. log ( 'reduce-flat' , flatList)
7.2.7. filter 过滤器
let arr = [ 2 , 4 , 6 , 8 , 10 ] ;
let narr = arr. filter ( item => item% 3 == 0 ) ;
console. log ( narr) ;
7.2.8. forEach 循环(迭代)
let arr = [ 2 , 4 , 6 , 8 , 10 ] ;
arr. forEach ( ( value, index ) => {
console. log ( index, value) ;
} ) ;
7.2.9. some()
let arr = [ 2 , 4 , 6 , 8 , 10 ] ;
let narr = arr. some ( item => item> 10 ) ;
console. log ( narr) ;
7.2.10. every()
let arr = [ 2 , 4 , 6 , 8 , 10 ] ;
let narr = arr. every ( item => item> 2 ) ;
console. log ( narr) ;
7.2.11. Array.from
const str = 'hello'
const strList = Array. from ( str)
console. log ( strList)
8. 对象的新特性及的新增方法
8.1 扩展运算符的使用
注意
简单类型的时候,使用扩展运算符是没问题的 但是如果扩展运算符展开对象以后,还是一个对象的话,我们复制的只是一个指针
const obj = { name: 'Nick' , video: 'es6' }
const initObj = { color: 'red' }
let videoObj = { ... obj }
console. log ( videoObj)
let obj2 = { ... obj, name: 'Jack' }
console. log ( obj2)
let obj3 = { ... obj, ... initObj }
console. log ( obj3)
8.2 属性初始化的简写
let name = 'zs'
let age = 18
let es5Obj = {
name: name,
age: age,
sayHello : function ( ) {
console. log ( 'this is es5Obj' )
}
}
let es6Obj = {
name,
age,
sayHello ( ) {
console. log ( 'this is es6Obj' )
}
}
8.3 可计算的属性名
let key = 'name'
let es5Obj = { }
es5Obj[ key] = 'zs'
let es6Obj = {
[ key] : 'xh'
}
console. log ( es5Obj, es6Obj)
8.5 新增方法
8.5.1 Object.is
let result = Object. is ( NaN , NaN )
console. log ( result, NaN === NaN )
8.5.2 Object.assign
const person = { name: 'zs' , age: 19 , info: { height: 190 } }
let person2 = { }
Object. assign ( person2, person)
person. info. height = 180
console. log ( person2)
8.5.3 Object.keys
const json = { name: 'Nick' , video: 'es6' , date: 2021 }
let obj = { }
for ( const key of Object. keys ( json) ) {
obj[ key] = json[ key]
}
console. log ( obj)
8.5.4 Object.values
const json = { name: 'Nick' , video: 'es6' , date: 2021 }
let obj = { }
for ( const key of Object. values ( json) ) {
obj[ key] = json[ key]
}
console. log ( obj)
8.5.5 Object.entries
const json = { name: 'Nick' , video: 'es6' , date: 2021 }
let obj = { }
for ( const key of Object. entries ( json) ) {
obj[ key] = json[ key]
}
console. log ( obj)
8.6 Map与WeakMap结构的特点
8.6.1 背景
JavaScript中的对象,实质就是键值对的集合(Hash结构),但是在对象里却只能用字符串作为键名 在一些特殊的场景里就满足不了我们的需求了,正因如此,Map这一数据提出了,它是JavaScript中的一种更完善Hash结构
8.6.2 Map对象
用于保存键值对,任何值(对象或者原始值)都可以作为一个键或一个值 使用介绍
var m = new Map ( )
8.6.3 内置API
属性/方法 作用 例子 size 返回键值对的数量 m.size clear() 清除所有键值对 m.clear() has(key) 判断键值对中是否有指定的键名,返回值是布尔值 m.has(key) get(key) 获取指定键名的键值对,如不存在则返回undefined m.get(key) set(key, value) 添加键值对,如键名已存在,则更新键值对 m.set(key, value) delete(key) 删除指定键名的键值对 m.delete(key)
let map = new Map ( )
map. set ( [ 1 , 2 , 3 ] , 'number' )
console. log ( map)
let map2 = new Map ( [ [ 'name' , 'Nick' ] , [ 'sex' , 'male' ] ] )
console. log ( map2)
console. log ( map2. size)
map2. set ( 'name' , 'Jack' ) . set ( 'hobbies' , [ 'swimmin' , 'running' ] )
console. log ( 'set' , map2)
console. log ( 'get' , map2. get ( 'hobbies' ) )
console. log ( 'get2' , map2. get ( 'age' ) )
console. log ( 'has' , map2. has ( 'age' ) )
map2. delete ( 'hobbies' )
console. log ( map2)
map2. clear ( )
const map = new Map ( [
[ 'name' , 'zs' ] ,
[ 'age' , 20 ]
] )
for ( let key of map) {
console. log ( key)
}
8.6.4 WeakMap
let weakmap = new WeakMap ( {
[ { name: 'hah' } , 'jack' ]
] }
console. log ( weakmap)
const ulObj = document. getElementById ( 'test' )
let obj = { name: 'jack' }
let array = [ obj, 'person' ]
array[ 0 ] = null
8.7 Set与WeakSet结构的特点
8.7.1 介绍
Set是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合 它和数组的最大的区别就在于
特点
8.7.2 基本使用
let set = new Set ( )
let set2 = new Set ( [ 1 , 2 , 3 ] )
set. add ( 1 )
8.7.3 属性及方法
属性/方法 作用 例子 size 返回成员个数 s.size clear 清除所有成员 s.clear has(value) 判断键值对中是否有指定的值,返回值是布尔值 s.has(value) delete(value) 删除指定值 s.delete(value)
let set = new Set ( [ '1' , 2 , 3 , 4 , 5 ] )
set. add ( 1 )
console. log ( set)
console. log ( 'size' , set. size)
let set = new Set ( )
const item = { fruit: 'apple' }
set. add ( item)
console. log ( 'has' , set. has ( { fruit: 'apple' } )
const set = new Set ( [ 1 , 2 , 3 , 4 , 5 ] )
for ( const keys of set) {
console. log ( keys)
}
for ( const value of set) {
console. log ( value)
}
for ( const value of set. entries ( ) ) {
console. log ( value)
}
const array = [ 1 , 2 , 3 , 4 , 5 , 3 , 2 , 1 , 7 , 4 ]
let unique = new Set ( array)
let uniqueArray = Array. from ( unique)
console. log ( uniqueArray)
8.7.4 WeakSet 和 Set 的区别
元素只能是对象,对象也是弱引用 无法遍历,没有size,也没有clear
let obj = { }
let weakSet = new WeakSet ( )
weakset. add ( obj)
console. log ( weakset)
8.8 Map、Set、Array及Object的区别
let array = [ ]
let obj = { }
let map = new Map ( )
let set = new Set ( )
const gooditem = { fruit: 'apple' }
array. push ( gooditem)
obj[ 'fruit' ] = 'apple'
map. set ( 'fruit' , 'apple' )
set. add ( gooditem)
console. log ( 'add' , array, obj, map, set)
const resultArray = array. includes ( gooditem)
const resultObj = 'fruit' in obj
const resultMap = map. has ( 'fruit' )
const resultSet = set. has ( gooditem)
console. log ( 'search' , resultArray, resultObj, resultMap, resultSet)
array. forEach ( function ( item ) {
item. fruit = item. fruit ? 'orange' : ''
} )
obj[ 'fruit' ] = 'orange'
map. set ( 'fruit' , 'orange' )
set. forEach ( function ( item ) {
item. fruit = item. fruit ? 'orange' : ''
} )
console. log ( 'update' , array, obj, map, set)
const index = array. findIndex ( funciton ( item ) {
return item. fruit
} )
array. splice ( index, 1 )
delete obj. fruit
map. delete ( 'fruit' )
set. delete ( gooditem)
console. log ( 'delete' , array, obj, map, set)
let obj = {
name: 'Nick' ,
hobbies: 'swimming'
}
console. log ( Object. entries ( obj) )
let map = new Map ( Object. entries ( obj) )
console. log ( 'map' , map)
let obj2 = Object. fromEntries ( map)
console. log ( 'obj' , obj2)
let array = [ 1 , 2 , 3 , 4 , 5 ]
let set = new Set ( array)
console. log ( 'set' , set)
let array2 = Array. from ( set)
console. log ( 'array' , array2)
8.9 代理Proxy和反射Reflect
8.9.1 Proxy
概述
正如Proxy的英译“代理”所示,Proxy是ES6为了操作对象引入的API 它不直接作用在对象上,而是作为一种媒介,如果需要操作对象的话,需要经过这个媒介的同意 使用方式
let p = new Proxy ( target, handler)
let accout = {
id: 001 ,
name: 'admin' ,
_private: 'test' .
phone: '12345678901' ,
create_item: '2019'
}
let accoutProxy = new Proxy ( account, {
get : function ( target, key ) {
switch ( key) {
case 'phone' :
return target[ key] . substring ( 0 , 3 ) + '****' + target[ key] . substring ( 7 )
case 'create_item' :
return target[ key] . replace ( '2019' , 2020 )
default :
break :
}
} ,
set : function ( target, key, value ) {
if ( key === 'id' ) {
return target[ key]
} else {
return target[ key] = value
}
} ,
has : function ( target, key ) {
if ( key in target) {
console. log ( ` ${ key} : ` , target[ key] )
return true
} else {
console. log ( '并无此属性' )
return false
}
}
deleteProperty : function ( ) {
if ( key. indexOf ( '_' ) === 0 ) {
console. warn ( '私有属性不能被删除' )
return false
} else {
delete target[ key]
return true
}
}
ownKeys ( target ) {
return Object. keys ( target) . filter ( function ( item ) {
return item !== 'id' && item. indexOf ( '_' ) !== 0
} )
}
} )
console. log ( '拦截读取' , accountProxy. phone, accountProxy. create_item)
accountProxy. id = 1234
accountProxy. name = 'guast'
console. log ( '拦截设置' , accountProxy. id, accountProxy. name)
console. log ( '拦截in' , 'sex' in accountProxy)
console. log ( '删除' , delete accountProxy[ '_private' ] )
console. log ( '拦截Object.keys()' , Object. keys ( accountProxy) )
8.9.2 Reflect
概述
与Proxy相同,ES6引入Reflect也是用来操作对象的,它将对象里一些明显属于语言内部的方法移植到Reflect对象上,它对某些方法的饭后结果进行了修改,使其更合理,并且使用函数的方式实现了Object的命令式操作 使用方法
let obj = {
name: 'Nick' ,
age: '21' ,
sex: 'male' ,
hobbies: 'swimming'
}
console. log ( Reflect. get ( obj, 'name' ) )
Reflect. set ( obj, 'name' , 'Jack' )
console. log ( obj. name)
'name' in obj
Reflect. has ( obj, 'name' )
8.9.3 双向数据绑定
< h1> 使用Proxy和Reflect实现双向数据绑定</ h1>
< input type = " text" id = " input" >
< h2> 您输入的是:< i id = " txt" > </ i> </ h2>
const inputObj = document. getElementById ( 'input' )
const txtObj = document. getElementById ( 'txt' )
const obj = { }
const handler = {
get : function ( target, key ) {
return Reflect. get ( target, key)
} ,
set : function ( target, key, value ) {
if ( key === 'text' ) {
inputObj. value = inputObj. value === value ? inputObj. value : value
txtObj. innerHTML = value
}
return Reflect. set ( target, key, value)
}
}
let objProxy = new Proxy ( obj, handler)
inputObj. addEventListener ( 'keyup' , function ( e ) {
objProxy. text = e. target. value
} )
9. 函数的扩展、类、模块化
9.1 函数的扩展
9.1.1. 默认参数
function es5Print ( x, y ) {
y = y || 'world'
console. log ( 'es5' , x+ y)
}
es5Print ( 'hello' , '' )
function es6Print ( x, y= 'world' ) {
console. log ( 'es6' , x+ y) ;
}
es6Print ( 'hello' , '' )
9.1.2. 参数扩展
function add ( ... rest) {
let sum = 0
console. log ( rest) ;
for ( let value of rest) {
sum += value
}
console. log ( sum) ;
}
add ( 1 , 2 , 3 , 4 , 5 )
9.1.3 扩展运算符
console. log ( ... [ 1 , 2 , 3 , 4 , 5 ] )
9.1.4 尾调用
function step2 ( x ) {
console. log ( '尾调用' , x) ;
}
function step1 ( x ) {
return step2 ( x)
}
9.2 深入浅出箭头函数
## 允许使用箭头( => )定义
var f = function ( v ) {
return v;
}
var f = v => v;
=== === === === === === === === === === === === === === === =
## 不需要参数或需要多个参数,就用圆括号代替
var f = function ( ) {
return 5
} ;
var f = ( ) => 5 ;
var sum = function ( num1, num2 ) {
return num1 + num2;
}
var sum = ( num1, num2 ) => num1+ num2;
=== === === === === === === === === === === === === === === =
## 代码块部分多于一条语句,就用大括号括起来,并且用 return 返回
var sum = ( num1, num2 ) => { return num1+ num2; }
=== === === === === === === === === === === === === === === =
## 箭头函数返回对象时,必须在对象外面加上括号
var getTempItem = id => ( { id: id, name: 'Temp' } ) ;
console. log ( getTempItem ( 2 ) ) ;
=== === === === === === === === === === === === === === === =
## 箭头函数能够简化回调函数
[ 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) ;
9.3 this 指向问题
9.3.1 普通函数中的 this
this
总是代表它的直接调用者
(js
的 this
是执行上下文
)
例如 obj.func
,那么 func
中的 this
就是 obj
非严格模式下
,未使用 use strict
没找到直接调用者
,则 this
指的是 window
在严格模式下
没有直接调用者
,函数中的 this
是 undefined
使用 call
,apply
,bind
(ES5
新增)绑定的,this
指的是绑定的对象
9.3.2 箭头函数中的 this
箭头函数没有自己的 this
,它的 this 是继承
而来的 默认指向在定义它时所处的对象(宿主对象)
,而不是执行时的对象,定义它的时候,可能环境是 window 箭头函数可以方便地让我们在 setTimeout
,setInterval
中方便的使用 this
箭头函数中,this
指向的固定化,并不是因为箭头函数内部有绑定 this
的机制,实际原因是箭头函数根本没有自己的 this ,导致内部的 this 就是外层代码块的 this
function Person ( ) {
this . name = 'little bear123' ,
this . age = 1234
setInterval ( ( ) => {
console. log ( this ) ;
console. log ( '我叫' + this . name + '我今年' + this . age + '岁' )
} , 10000 )
}
let p = new Person ( )
Person{ name: "little bear123" , age: 1234 }
我叫little bear123我今年1234 岁
这段函数中,this 指向的是 ` Person ` 对象,可以看到控制台打印结果,此时的箭头函数所处的宿主对象是 ` Person ` ,所以 this 指向的是 ` person `
9.4 类的概念
9.4.1 简介
传统的JavaScript中只有对象,没有类的概念 它是基于原型的面向对象语言 原型对象特点就是将自身的属性共享给新对象
9.4.2 new 操作符做了什么
返回(产生)了一个新的对象 将这个空对象的_proto_指向了构造函数内部的prototype 将构造函数的this绑定到这个对象上(即new创建的对象,其函数体内的this指向的是这个对象) 返回到这个新对象上
const obj = { }
obj. _proto_ = 构造函数 . prototype
构造函数 . call ( obj)
function Person ( name, age ) {
this . name = name
this . age = age
}
Person . prototype. sayHello = function ( ) {
console. log ( this . name, this . age)
}
const p = new Person ( 'zs' , 12 )
console. log ( p)
9.4.3 ES6 class的概念
class Person {
constructor ( name, age ) {
this . name = name
this . age = age
}
sayHello ( ) {
console. log ( this . name, this . age)
}
}
const p = new Person ( 'ls' , 12 )
console. log ( p)
class Parent {
constructor ( name = 'Nick' ) {
this . name = name
}
}
class Child extends Parent {
constructor ( name = 'Jack' ) {
super ( name)
this . name = name
}
}
console. log ( '继承' , new Child ( ) )
class Person {
constructor ( name = 'Nick' ) {
this . name = name
}
get fullName ( ) {
return this . name
}
set fullName ( ) {
this . name = value
}
}
const p = new Person ( )
console. log ( 'get' , p. fullName)
p. fullName = 'Jack'
console. log ( 'set' , p. fullName)
class Person {
constructor ( name = 'Nick' ) {
this . name = name
}
static sayHello ( obj ) {
console. log ( 111 , obj. name)
}
}
const p = new Person ( 'xx' )
Person. sayHello ( p)
class Person {
static prop = 'test'
constructor ( name = 'Nick' ) {
this . name = name
}
static sayHello ( obj ) {
console. log ( 111 , obj. name)
}
}
Person. prop = 'test'
console. log ( Person. prop)
9.5 模块化开发
9.5.1 背景
ES6之前是没有类的概念的,也就是没有模块这个说法 理想情况下,开发者应该只注重核心业务的开发,对于其他有一定通用性的业务可以直接引入别人的代码,也就是模块 多人开发,本应如此
9.5.2 提出的方案
Common JS
Commonjs是作为Node中模块化规范以及原生模块面世的 AMD和Requirejs
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义" 它采用异步方式加载模块,模块的加载不影响它后面语句的运行 所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后(前置依赖),这个回调函数才会运行 import 和 export
## a. js
export let a = 3 ;
export function sayHello ( ) {
console. log ( 'hello' )
}
export class test {
say ( ) {
console. log ( 'test' )
}
}
## b. js
import * as test from 'a.js'
console. log ( test)
## a. js
export let a = 3 ;
function sayHello ( ) {
console. log ( 'hello' )
}
export default {
a,
sayHello
}
## b. js
import test from 'a.js'
console. log ( test. a)
10. 深入解析JavaScript中的异步编程
10.1 异步编程及JavaScript的异步实现
10.1.1 什么是同步
当一个“调用”发出时,在没有得到结果之前,这个“调用”就会阻塞后面代码的执行,得到结果的时候才会返回 换句话说,“调用者”要主动等待代码的执行结果,得到返回结果后,程序才会继续执行
10.1.2 什么是异步
“调用”发出的时候,就直接返回了,对应的结果会通过状态、通知来告诉“调用者”或通过回调函数处理这个调用 异步调用发出后,不会阻塞后面的代码
10.1.3 为什么引入异步
JavaScript是单线程 同步代码会阻塞后面的代码 异步不会阻塞程序的运行
10.1.4 JavaScript异步实现
回调函数 setTimeout 和 setInterval Promise Generator async
10.2 Promise 应用
10.2.1 什么是Promise
ES6
中一个非常重要和好用的特性就是Promise
Promise
作用
Promise 是异步编程的一种解决方案
Promise保存着某个未来才会结束的事件
通常是一个 异步操作--接口--Ajax--setTimeout--img
的结果 ES6
规定,Promise
对象是一个构造函数
,用来生成Promise
实例 什么时候处理异步事件
常见的就是网络请求
封装一个网络请求
的函数,不能立即拿到结果返回
所以往往会传入另外一个函数
,在数据请求成功
时,将数据通过传入的函数回调出去
如果只是简单的网络请求
,该方案可行 但是当网络请求非常复杂
时,就会出现回调地狱
10.2.2 网络请求的回调地狱
场景
需要通过一个url1
从服务器加载一个数据data1
,data1
中包含了下一个请求的url2
需要通过data1
取出url2
,从服务器加载数据data2
,data2
中包含了下一个请求的url3
需要通过data2
取出url3
,从服务器加载数据data3
,data3
中包含了下一个请求的url4
发送网络请求url4
,获取最终的数据data4
$. ajax ( 'url1' , function ( data ) {
$. ajax ( data1[ 'url2' ] , function ( data2 ) {
$. ajax ( data2[ 'url3' ] , function ( data3 ) {
$. ajax ( data3[ 'url4' ] , function ( data4 ) {
console. log ( data4) ;
} )
} )
} )
} )
问题
代码难看而且不容易维护
需要更加优雅
的方式来进行这种异步操作
使用Promise
const promise = new Promise ( ( resolve, reject ) => {
if ( ) {
resolve ( value) ;
} else {
reject ( error) ;
}
} ) ;
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
,它们是两个函数,由JavaScript引擎
提供,不用自己部署resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从pending变为rejected
),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去Promise
实例生成后,可以用 then
方法分别指定 resolved
状态和 rejected
状态的回调函数
promise. then ( function ( value ) {
} , function ( error ) {
} ) ;
then
方法可以接受两个回调函数作为参数。
第一个回调函数是 Promise
对象的状态变为 resolved
时调用 第二个回调函数是 Promise
对象的状态变为 rejected
时调用。 其中,第二个函数是可选的,不一定要提供。这两个函数都接受 Promise
对象传出的值作为参数
10.2.3 Promise 的使用
let one = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'aa' ) :
} , 1000 ) ;
} )
let two = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'bb' ) :
} , 1000 ) ;
} )
let three = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'cc' ) :
} , 1000 ) ;
} )
Promise. all ( [ one, two, three] ) . then ( arr => {
let [ one, two, three] = arr;
console. log ( one) ;
console. log ( two) ;
console. log ( three) ;
let one = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'aa' ) :
} , 1100 ) ;
} )
let two = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'bb' ) :
} , 1050 ) ;
} )
let three = new function ( ) {
setTimeout ( ( ) => {
resolve ( 'cc' ) :
} , 1300 ) ;
} )
Promise. race ( [ one, two, three] ) . then ( arr => {
console. log ( arr) ;
}
function foo ( ) {
return new Promise ( ( resolve, reject ) => {
setTimeout ( ( ) => {
resolve ( ) ;
} , 1000 )
} ) ;
}
foo ( ) . then ( ( ) => {
console. log ( 'aa' ) ;
return foo ( ) ;
} ) . then ( ( ) => {
console. log ( 'bb' ) ;
console, log ( data) ;
return foo ( ) ;
} ) . catch ( rea => {
console. log ( 'error' ) ;
console. log ( rea) ;
} ) ;
async function demo ( ) {
console. log ( 'this is a async demo' ) ;
}
console. log ( demo ( ) ) ;
function demo ( ) {
return new Promise ( ( resolve, reject ) => {
setTimeout ( ( ) => {
resolve ( 2 * num) ;
} , 2000 )
} )
}
async function test ( ) {
await demo ( 20 ) ;
}
test ( ) ;
10.2.4 定时器的异步事件
这里,我们用一个定时器
来模拟异步事件
:
假设下面的data
是从网络上1秒后请求的数据 console.log
就是我们的处理方式 这是我们过去的处理方式,我们将它换成Promise代码
setTimeout ( function ( ) {
let data = 'hello world'
console. log ( content) ;
} , 1000 )
new Promise ( ( resolve, reject ) => {
setTimeout ( function ( ) {
resolve ( 'hello world' )
reject ( 'Error Data' )
} , 1000 )
} ) . then ( data => {
console. log ( data) ;
} ) . catch ( error => {
console. log ( error) ;
} )
10.2.5 定时器异步事件解析
我们先来认认真真的读一读这个程序到底做了什么?
new Promise
很明显是创建一个Promise对象
小括号中((resolve, reject) => {})
也很明显就是一个函数
,而且我们这里用的是箭头函数
我们先知道一个事实:在创建Promise
时,传入的这个箭头函数
是固定
的(一般我们都会这样写) resolve
和reject
它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个成功还是失败?
如果是成功
的,那么通常我们会调用resolve(messsage)
,这个时候,我们后续的then
会被回调
如果是失败
的,那么通常我们会调用reject(error)
,这个时候,我们后续的catch
会被回调
。
10.2.6 Promise三种状态
首先, 当我们开发中有异步操作
时, 就可以给异步操作
包装一个Promise
异步操作
之后会有三种状态 我们一起来看一下这三种状态:
pending
:等待
状态,比如正在进行网络请求
,或者定时器
没有到时间。fulfill
:满足
状态,当我们主动回调了resolve
时,就处于该状态,并且会回调.then()
reject
:拒绝
状态,当我们主动回调了reject
时,就处于该状态,并且会回调.catch()
new Promise ( ( resolve, reject ) => {
setTimeout ( function ( ) {
reject ( 'error data' )
} , 1000 )
} ) . then ( data => {
console. log ( data) ;
} , error => {
console. log ( error) ;
} )
10.2.7 Promise链式调用
我们在看Promise
的流程图时,发现无论是then
还是catch
都可以返回一个Promise对象
所以,我们的代码其实是可以进行链式调用
的: 这里我们直接通过Promise
包装了一下新的数据,将Promise对象
返回了
Promise.resovle()
:将数据包装成Promise对象
,并且在内部回调resolve()函数
Promise.reject()
:将数据包装成Promise对象
,并且在内部回调reject()函数
new Promise ( ( resolve, reject ) => {
setTimeout ( function ( ) {
resolve ( 'hello world' )
} , 1000 )
} ) . then ( data => {
console. log ( data) ;
return Promise. resolve ( data + '111' )
} ) . then ( data => {
console. log ( data) ;
return Promise. resolve ( data + '222' )
} ) . then ( data => {
console. log ( data) ;
return Promise. reject ( data + 'error' )
} ) . then ( data => {
console. log ( data) ;
return Promise. resolve ( data + '333' )
} ) . catch ( data => {
console. log ( data) ;
return Promise. resolve ( data + '444' )
} ) . then ( data => {
console. log ( data) ;
链式调用简写
简化版代码 :
如果我们希望数据直接包装成Promise.resolve
,那么在then
中可以直接返回数据
注意下面的代码中,我讲return Promise.resovle(data)
改成了return data
结果依然是一样的
new Promise ( ( resolve, reject ) => {
setTimeout ( function ( ) {
resolve ( 'hello world' )
} , 1000 )
} ) . then ( data => {
console. log ( data) ;
return data + '111'
} ) . then ( data => {
console. log ( data) ;
return data + '222'
} ) . then ( data => {
console. log ( data) ;
return Promise. reject ( data + 'error' )
} ) . then ( data => {
console. log ( data) ;
return data + '333'
} ) . catch ( data => {
console. log ( data) ;
return data + '444'
} ) . then ( data => {
console. log ( data) ;
10.3 数据结构中的遍历接口 Iterator 和 for of 循环
10.3.1 什么是 Iterator
Iterator(遍历器)是一种接口,目的是为了给不同的数据结构提供统一的循环方式,任何数据结构如果部署了 iterator 接口,就能够实现遍历的操作
const arr = [ 1 , 2 , 3 ]
const fn = arr[ Symbol. iterator] ( )
console. log ( fn. next ( ) )
console. log ( fn. next ( ) )
console. log ( fn. next ( ) )
console. log ( fn. next ( ) )
10.3.2 Iterator 的作用
为不同的数据结构,提供一个统一的、简便的访问接口 将数据成员按照一定的顺序输出 提供给ES6中的for of 的这个循环语句进行使用
10.3.3 什么结构具备原生的 iterator 接口
Array String Set Map 函数的 argument 对象
const obj = {
color: 'red' ,
price: 18 ,
size: 'small' ,
[ Symbol. iterator] ( ) {
let index = 0
const values = Object. values ( this )
return {
next ( ) {
if ( index < values. length) {
return {
value: values[ index ++ ] ,
done: false
}
} else {
return {
done: true
}
}
}
}
}
}
for ( const value of obj) {
console. log ( value)
}
10.4 generator 应用
10.4.1 generator 和 yield 的使用
Generator
主要用于异步编程,就是封装一个异步任务,或者说是异步任务的容器特点是可以交出函数执行全(展停执行) 在声明函数的 function
关键字与函数名之间有一*(用于区别普通函数) yield
在 Generator
函数体内使用,可以定义不同的内部状态(可以设置不同时候不一样的值)yield
命令是异步不同阶段的分界线,有时也会把 yield
当成是 return
(当然有本质区别)使用启动 next()
方法,分阶段执行 Generator
函数
function * demo ( ) {
console. log ( '1' ) ;
yield
console. log ( '2' ) ;
yield
console. log ( '3' ) ;
}
let genob = demo ( ) ;
genobj. next ( ) ;
genobj. next ( ) ;
genobj. next ( ) ;
let obj = {
a: 1 ,
b: 2 ,
c: 3
}
obj[ Symbol. iterator] = function ( ) {
for ( const key of Object. keys ( obj) ) {
yield obj[ key]
}
}
for ( const value of obj) {
console. log ( value)
}
const state = function ( ) {
whilw ( 1 ) {
yield 'success'
yield 'fail'
yield 'pending'
}
}
const stateData = state ( )
console. log ( stateData. next ( ) )
console. log ( stateData. next ( ) )
console. log ( stateData. next ( ) )
console. log ( stateData. next ( ) )
function fn1 ( ) {
return new Promise ( resolve => {
setTimeout{ ( ) => {
console. log ( '查询中' )
resolve ( { code: 0 } )
} , 1000 )
} )
}
const getStatus = function ( ) {
yield fn1 ( )
}
function autoGetStatus ( ) {
const gen = getStatus ( )
const status = gen. next ( )
status. value. then ( res => {
if ( res. code === 0 ) {
console. log ( '用户付款成功' )
} else {
console. log ( '暂未付款' )
setTimeout ( ( ) => autoGetStatus ( ) , 500 )
}
}
}
autoGetStatus ( )
const ajax = function ( ) {
console. log ( 'start' )
yield function ( cb ) {
setTimeout ( ( ) => {
console. log ( '异步任务结束' )
cb && cb ( )
} , 1000 )
}
console. log ( 'end' )
}
const runAjax = ajax ( )
const first = runAjax. next ( )
first. value ( ( ) => runAjax. next ( ) )
11. ES7-ES9 新特性
11.1 ES7 新功能
Array.prototype.includes
检查数组中是否存在值;(区别ES6
字符串的includes
方法)Exponentiation Operator
求幂运算(a**b等价于Math.pow(a,b)
)
11.2 ES8 部分功能
Object.values/entries/getOwnProertyDescriptors
String.prototype.padStart/padEnd
函数参数列表和调用中的尾逗号(trailing commas
) Async Functions
异步函数(async/await
)
11.3 ES9 新增部分功能
异步迭代 Promise.finally()
Rest/Spread
属性正则表达式命名捕获组 正则表达式反向断言 非转义序列的模版字符串