ECMAScript简介
ECMASciprt是JavaScript的语言本身
通常看作JavaScript的标准化规范
实际上JavaScript是ECMAScript的扩展语言
ECMAScipt只提供了最基本的语法
JavaScript=ECMAScript + Web APIs(BOM + DOM)
JavaScipt实际上是由ECMAScript、BOM和DOM三部分组成
Node.js = ECMAScript + Node APIs (fs + net + etc)
Node.js是由ECMAScript、fs、net、etc组成
ES6变化简介
官方网址 http://www.ecma-international.org/ecma-262/6.0/
ES6变化简单归纳为4类:
作用域
作用域就是某个成员能够起到作用的范围
在ES6之前,只有两种作用域
- 全局作用域
变量在函数或者代码块 {}
外定义,即为全局作用域。不过,在函数或者代码块 {}
内未定义的变量也是拥有全局作用域的(不推荐)。
- 函数作用域
在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!
ES6新增作用域
- 块级作用域
在其他编程语言中,块状作用域是很熟悉的概念,但是在JavaScript中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识 {}
就可以了。
Let
ES6新增了let命令,用来声明变量,有以下几个特性:
- let声明的全局变量不是全局对象window属性
- 用let定义变量不允许重复声明
- let声明的变量不存在变量提升
- let声明的变量具有暂时性死区
- let声明的变量具有块级作用域
常见场景
给DOM循环添加事件
var elements = [{}, {}, {}]
for (var I = 0 ; i < elemrntd.length; i++){
elements[i].onclick = function () {
console.log(i)
}
}
elements[0].onclick()
当点击DOM元素的时候,无论点击哪一个,打印出来的是3,因为 for 循环里面是同步操作,点击事件是异步操作。当执行点击事件后,for 以及执行完毕,此时console.log打印出来的i是全局作用域里面的 i ,全局作用域的 i 为3
如果希望的值是0、1、2,也就是每次保存循环时候 i 的值,应该如何做呢?
ES5的解决方案——闭包
var elements = [{}, {}, {}]
for (var i=0;i<elements.length;i++){
elements[i].onclick = (function (i) {
return function() {
console.log(i)
}
})()
}
elements[0].onclick()
ES6解决方案——let
var elements = [{}, {}, {}]
for (let i = 0 ; i < elemrntd.length; i++){
elements[i].onclick = function () {
console.log(i)
}
}
elements[0].onclick()
事实上,上述代码内部也是一个闭包机制,使用 babel 转化成 ES5 语法,会把代码转化成闭包形式 。
补充:
for循环有两层作用域
循环层内层是一个独立的作用域,
外层是它本身的作用域
for(let i= 0;i<3;I++){
i = 'foo'
}
所以执行上述代码不会报错。
Const
const用来声明一个只读的常量,它的特性在let基础上多个一个只读特性
使用const注意事项
- const定义的时候必须要赋一个初始值
- const实际上不允许修改变量的地址,但地址里面的内容是可以修改的
例如:
const obj = {}
obj.name = 'zce' //允许
obj = {} //不允许
声明变量最佳实践:不用var,主用const,配合let。
解构赋值
解构赋值就是允许按照一定模式,从数组和对象中提取值,对变量进行赋值
数组解构赋值
- 基本用法 ```javascript const arr = [100, 200, 300] //获取数组里面的成员
//以前的方法 const foo = arr[0] const bar = arr[1] const bae = arr[2] console.log(foo, bar, bae)
//新方法 const [foo, bar ,bae] =arr console.log(foo, bar, bae)
- **rest参数**
```javascript
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]
我们可以使用 rest 来接收赋值数组的剩余元素,不过要确保这个 rest 参数是放在被赋值变量的最后一位。
- 默认值
如果数组的内容少于变量的个数,并不会报错,没有分配到内容的变量会是undefined。
let [firstName, surname] = []
console.log(firstName) // undefined
console.log(surname) // undefined
可以给变量赋予默认值,防止undefined的情况出现:
// default values
let [name = "Guest", surname = "Anonymous"] = ["Julius"]
console.log(name) // Julius (from array)
console.log(surname) // Anonymous (default used)
对象解构赋值
对象解构赋值需要根据属性名进行解构。
- 基本用法 ```javascript let options = { title: “Menu”, width: 100, height: 200 }
let {title, width, height} = options
console.log(title) // Menu console.log(width) // 100 console.log(height) // 200
> 在这个解构赋值的过程中,左侧的“模板”结构要与右侧的 Object 一致,但是属性的顺序无需一致。
- **默认值**
赋值过程中可以指定默认值。
```javascript
let options = {
title: "Menu"
}
let {width = 100, height = 200, title} = options
console.log(title) // Menu
console.log(width) // 100
console.log(height) // 200
- rest运算符
如果我们象相操作数组一样,只关心指定的属性,其他可以暂存到一个变量下,这就要用到 rest 运算符了。
let options = {
title: "Menu",
height: 200,
width: 100
}
let {title, ...rest} = options
// now title="Menu", rest={height: 200, width: 100}
console.log(rest.height) // 200
console.log(rest.width) // 100
- 注意事项 ```javascript const obj = {name: ‘zce’, age: 18}
const { name } = obj
const name = ‘tom’ const { name } = obj console.log(name)
当解构的变量名与当前作用域的变量名相同时,就会产生冲突,这个情况就会报错。<br />使用重命名方法解决:
> const { name: objName } = obj
<a name="VunND"></a>
### 字符串解构赋值
可以当做是数组的解构
```javascript
let str = 'imooc'
let [a, b, c, d, e] = str
console.log(a, b, c, d, e)
String
模板字符串
- 使用一对” ` “进行标识
- 支持多行字符串
- 支持插值表达式
const name = 'tom'
const msg = `hey, ${name}`
console.log(msg)
ES2015带标签的模板字符串
const name = 'tom'
const gender = ture
function myTagFunc (strings) {
console.log(strings)
}
//返回正常内容
function myTagFunc(strings, name, gender) {
//console.log(strings, name, gender)
return string[0] + name + strings[1] + gender + strings[2]
}
const result = maTagFunc `hey, ${name} is a ${gender}.`
扩展方法
String.prototype.includes()
ES6提供了includes方法来判断一个字符串是否包含在另一个字符串中,返回boolean类型的值。
Sting.prototype.stratsWith()
判断参数字符串是否在原字符串的头部, 返回boolean类型的值。
Sting.prototype.endsWith()
判断参数字符串是否在原字符串的尾部, 返回boolean类型的值。
const message = 'Error: foo is not defined'
console.log{
message.startsWith('Error')//true
message.endsWith('.')//true
message.includes('foo')//true
}
Function
默认参数
对于函数而言,经常会用到参数,关于参数的默认值通常都是写在函数体中,如在 ES5 的时候大家都会这么写:
function foo (enable) {
enable = enable === undefined || true
console.log('foo invoked - enable: ')
console.log(enable)
}
foo()
在 ES6 中改变了对这种知识的写法:
function foo (enable = true) {
console.log('foo invoked - enable: ')
console.log(enable)
}
foo()
需要注意的是传递多个参数是,带有默认值的参数一定要放在最后。
剩余参数
function foo (…args){
// 执行代码
}
foo(1, 2, 3, 4)
形参会以数组的形式,去接收从当前参数位置开始往后所有的形参,只能出现在参数的最后一位,而且只能使用一次。
扩展运算符
把固定数组内容“打散”到对应的参数。
const arr = ['foo', 'bar', 'baz']
//以前的方法
console.log.apply(console,arr)
//新方法
console.log(…arr)
箭头函数
const inc = n => n + 1
箭头的左边是参数列表,右边是函数体。
箭头函数与this
const person = {
name = 'zce'
//普通函数 this 指向调用时的对象
sayHi: function (){
console.log(`hi, my name is ${this.anme}`)//正常打印
}
//因为箭头函数中对this的处理是定义时,this的指向也就是person外层所指向的window
//而window没有name属性,所以结果是 undefined
sayHi () => {
console.log(`hi, my name is ${this.name}`)//undefined
},
//setTimeout在全局作用域执行,this会丢失,指向window
//需要用一个变量保存this
sayHiAsync: function () {
const _this = this
setTimeout(function(){
console.log(_this.name)
},1000)
//箭头函数不会丢失this
sayHiAsync: function () {
setTimeout(()=>{
console.log(this.name)
},1000)
}
}
总结
- 箭头函数中this指向定义是所在的对象,而不是调用时所在的对象
- 箭头函数不可以当作构造函数
- 箭头函数不可以使用arguments对象
Object
对象字面量增强
在 ES6 之前 Object 的属性必须是 key-value 形式,在 ES6 之后是可以用简写的形式来表达:
const bar = '345'
const obj = {
foo: 123
bar: bar
methods: function(){
console.log('1')
}
}
新方法
const bar = '345'
const obj = {
foo: 123,
bar,
method1(){
console.log('1')
}
}
对象扩展方法
Object.assign()
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象
const source1 = {
a: 123,
b:123
}
const target = {
a: 456,
c: 456
}
const result = Object.assign(taeget, source1)
console.log(target)//{a:123,c:456,b:123}
Object.is()
判断两个对象是否相等
let obj1 = { // new Object()
name: 'zhangsan',
age: 34
}
let obj2 = { // new Object()
name: 'zhangsan',
age: 34
}
console.log(obj1 == obj2) // false
console.log(Object.is(obj1, obj2)) // false
let obj2 = obj1
console.log(Object.is(obj1, obj2)) // true
Proxy
Object.defineProperty监视对象的读写
Proxy为对象设置访问代理器
Proxy基本语法
let p = new Proxy(target, handler)
解释
参数 | 含义 | 必选 |
---|---|---|
target | 用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理) | Y |
handler | 一个对象,其属性是当执行一个操作时定义代理的行为和函数 | Y |
第一个参数target就是用来代理的“对象”,被代理之后它是不能直接被访问的,而handler就是实现代理的过程。
const person = {
name: 'zce',
age: 20
}
const personProxy = new Proxy(person,{
get (target, property){
return property in target ? target[property] : 'default'
},
set(target, property, value){
if(property === 'age'){
if(!Number.isInteger(value)){
throw new TypeError(`${value} is not an int`)
}
}
}
})
Proxy vs. Object.defineProperty()
defineProperty 只能监视属性的读写,Proxy能够监视到更多对象操作。
const person = {
name: 'zce',
age: 20
}
const personProxy = new Proxy(person, {
deleteProperty (target, property) {
console.log('delete', property)
delete target[property]
}
})
delete personProxy.age
Proxy更好的支持数组对象的监视,重写数组的操作方法(通过自定义的方法去覆盖掉数组原生对象上的方法,以此来劫持对应事件方法调用过程)。**
使用Proxy对象监视数组
const list = []
const listProxy = new Proxy(list, {
ser(target, property, value) {
console.log('set', property, value)
target[property] = value
return true //表示设置成功
}
})
listProxy.push(100)
Proxy是以非侵入的方式监管了对象的读写
补充:
handler方法 | 触发方式 |
---|---|
get | 读取某个属性 |
set | 写入某个属性 |
has | in操作符 |
deleteProperty | delete 操作符 |
getPrototypeOf | Object.getPrototypeOf() |
setPrototypeOf | Object.setPrototypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() |
apply | 调用一个函数 |
construct | 用 new 调用一个函数 |
Reflect
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。
设置目的
- 将Object属于语言内部的方法放到Reflect上
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
- 统一提供一套用于操作对象的API
例如:
const obj = {
name: 'zce'
age: 18
}
console.log('name' in obj)
console.log(delete obj['age'])
console.log(Object.keys(obj))
// 判断是否包含一个值,删除元素和获取键值这些操作看起来不统一,新手难以学习
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
// Reflect方法就统一许多
注意事项
- Reflect是一个静态类,不能通过new Reflect()去构建一个实例化对象
- Reflect内部封装了一些列对对象的底层操作 13个
- Reflect成员方法就是Proxy处理对象的默认实现 ```javascript const obj = { // … }
const proxy = new Proxy(obj, { get() })
在proxy里面没有定义get方法,但是能使用get方法,这是因为proxy默认调用Reflect成员方法。
<a name="l8QZm"></a>
## Promise
---
一种更优的异步编程解决方案,解决了传统异步编程中回调函数嵌套过深的问题。<br />在JavaScript异步编程中有详细介绍。
<a name="vriVG"></a>
## Class
---
<a name="p9Ror"></a>
### 声明类
```javascript
class Animal {
constructor(type) {
this.type = type
}
walk() {
console.log( `I am walking` )
}
}
let dog = new Animal('dog')
let monkey = new Animal('monkey')
静态成员
实例方法 vs. 静态方法
实例方法:通过这个类型构造出来的实例对象去调用
静态方法:直接通过这个类本身去调用
需要注意的是,因为静态方法是挂载在类上面的,在静态方法内部它的this就不会指向某个实例对象,而是指向当前这个类
类的继承
class Animal {
constructor(type) {
this.type = type
}
walk() {
console.log( `I am walking` )
}
static eat() {
console.log( `I am eating` )
}
}
class Dog extends Animal {
constructor () {
super('dog')
}
run () {
console.log('I can run')
}
}
Set
基本语法
生成Set实例
let s =new Set()
可以定义一个空的 Set 实例,也可以在实例化的同时传入默认的数据。
let s = new Set([1, 2, 3, 4])
初始化的参数必须是可遍历的,可以是数组或者自定义遍历的数据结构。
添加数据
s.add('hello')
s.add('goodbye')
//或者
s.add('hello').add('goodbye')
Set数据结构不允许数据重复,所以添加重复的数据是无效的。
删除数据
// 删除指定数据
s.delete('hello') // true
// 删除全部数据
s.clear()
统计数据
Set可以快速进行统计数据,如数据是否存在、数据的总数
// 判断是否包含数据项,返回 true 或 false
s.has('hello') // true
// 计算数据项总数
s.size // 2
数组去重
let arr = [1, 2, 3, 4, 2, 3]
let s = new Set(arr)
console.log(s)
合并去重
let arr1 = [1, 2, 3, 4]
let arr2 = [2, 3, 4, 5, 6]
let s = new Set([...arr1, ...arr2])
console.log(s)
console.log([...s])
console.log(Array.from(s))
交集
let s1 = new Set(arr1)
let s2 = new Set(arr2)
let result = new Set(arr1.filter(item => s2.has(item)))
console.log(Array.from(result))
差集
let arr3 = new Set(arr1.filter(item => !s2.has(item)))
let arr4 = new Set(arr2.filter(item => !s1.has(item)))
console.log(arr3)
console.log(arr4)
console.log([...arr3, ...arr4])
遍历方式
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
- for…of:可以直接遍历每个成员 ```javascript console.log(s.keys()) // SetIterator {“hello”, “goodbye”} console.log(s.values()) // SetIterator {“hello”, “goodbye”} console.log(s.entries()) // SetIterator {“hello” => “hello”, “goodbye” => “goodbye”} s.forEach(item => { console.log(item) // hello // goodbye })
for (let item of s) { console.log(item) }
for (let item of s.keys()) { console.log(item) }
for (let item of s.values()) { console.log(item) }
for (let item of s.entries()) { console.log(item[0], item[1]) }
<a name="MScVN"></a>
## Map
---
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
> 语法上可以把键值设置位对象,布尔值,数字。但如果我们的键值不是字符串,对象内部会使用toString()结构作为键,如果使用对象作为键,不管对象如何不同,toString后结果都为'[object Object]'。
<a name="9Vym5"></a>
### 基本语法
**实例化**
```javascript
let map = new Map([iterable])
Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’ ], [ 2, ‘two’ ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
添加数据
let keyObj = {}
let keyFunc = function() {}
let keyString = 'a string'
// 添加键
map.set(keyString, "和键'a string'关联的值")
map.set(keyObj, '和键keyObj关联的值')
map.set(keyFunc, '和键keyFunc关联的值')
删除数据
// 删除指定的数据
map.delete(keyObj)
// 删除所有数据
map.clear()
统计数据
// 统计所有 key-value 的总数
console.log(map.size) //2
// 判断是否有 key-value
console.log(map.has(keyObj)) // true
查询数据
- ge()方法返回某个Map对象中的一个指定元素
console.log(map.get(keyObj)) // 和键keyObj关联的值
遍历方式
- keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
- values() 方法返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
- entries() 方法返回一个新的包含 [key, value] 对的 Iterator ? 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
- forEach() 方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
- for…of 可以直接遍历每个成员 ```javascript map.forEach((value, key) => console.log(value, key))
for (let [key, value] of map) { console.log(key, value) }
for (let key of map.keys()) { console.log(key) }
for (let value of map.values()) { console.log(value) }
for (let [key, value] of map.entries()) { console.log(key, value) }
<a name="KB1Ql"></a>
## Symbol
---
一种全新的原始数据类型,表示一个独一无二的值。可以传入字符串作为描述文本,使用Symbol()作为属性名。
<a name="hXAKi"></a>
### 声明方式
```javascript
let s = Symbol()
typeof s
// "symbol"
变量s就是一个独一无二的值。typeof的结果说明s是 Symbol 数据类型。
既然是独一无二的,那么两个Symbol()就一定是不相等的:
let s1 = Symbol()
let s2 = Symbol()
console.log(s1)
console.log(s2)
console.log(s1 === s2) // false
Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始数据类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类型于字符串的数据类型。
Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo')
let s2 = Symbol('foo')
console.log(s1)
console.log(s2)
console.log(s1 === s2) // false
Symbol.for()
Symbol.for()
接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
let s1 = Symbol.for('foo')
let s2 = Symbol.for('foo')
console.log(s1 === s2) // true
Symbol.for()与Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。
这个方法维护的是字符串和Symbol的关系,如果传入的不是字符串,这个方法会自动把它转化成字符串。
Symbol.keyFor()
Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
const s1 = Symbol('foo')
console.log(Symbol.keyFor(s1)) // undefined
const s2 = Symbol.for('foo')
console.log(Symbol.keyFor(s2)) // foo
作为属性名
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
可以使用Symbol模拟实现私有成员。
const name = Symbol()
const person = {
[name]:'zce',
say() {
console.log(this[name])
}
}
由于外部无法创建一个完全相同的Symbol,所以无法使用person[Symbol()]来拿到这个值,就实现了私有成员
自定义字符串字符标签
const obj = {
[Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString())
//[object XObject]
为对象实现迭代器时会经常用到
遍历属性
使用Symbol作为属性名时,无法使用for in循环访问到Symbol属性值,也不能使用Object.keys()访问到
使用JSON.stringify时,Symbol也会被忽略掉,所以Symbol适合作为对象的私有属性。
可以使用Object.getOwnPropertySymbols()获取到Symbol类型的属性名,不过只能获取到Symbol类型的属性名。
for…of循环
for 适合遍历普通数组,for in 适合遍历键值对。ES5一些对象的遍历方法,这些遍历方式都有一定的局限性,引入了全新的遍历方式for…of循环,作为遍历所有数据结构的统一方式。
const arr = [100, 200, 300, 400]
for(const item of arr) {
console.log(item)
}
取代forEach循环
arr.forEach(item => {
console.log(item)
})
相比于forEach方法,for…of可以使用break跳出循环。
for(const item of arr){
console.log(item)
if(item > 100){
break
}
}
arr.forEach()不能跳出循环。以前为了解决这个问题,使用arr.some()和arr.every()方法,arr.some()返回true,arr.every()返回false,用来中止遍历。
伪数组也可以使用for…of遍历
for…of可以遍历set,与普通数组没有区别
**
遍历Map得到包含键值对字符串的数组,可以配合数组的结构语法,直接拿到数组的键和值
for(const [key, value] of m) {
console.log(key, value)
}
for…of遍历对象
const obj = {foo: 123, bar: 456}
for(const item of obj){
console.log(item)
}
打印出:TypeError: obj is not iterable。Iterator章节解答这个疑惑。
Iterator
for…of循环是一种数据结构统一遍历方式,ES中能够表示有结构的数据类型越来越多,为了个各种各样的数据结构提供统一遍历方式,ES2015提供了Iterable接口,实现Iterable接口就是for…of的前提。
所有可以直接被for…of遍历的数据类型都要实现Iterable这个接口,它的内部都需要挂载一个Symblo.Iterator的方法,这个方法会返回next()方法,我们不断遍历这个方法就可以实现遍历。
cosnt set = new Set(['foo','bar','baz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
//set数据就会被遍历
实现可迭代接口
之所以说for…of循环可以作为遍历所有数据结构的统一方式,因为它内部就是调用被遍历对象的iterator方法得到一个迭代器,从而去遍历内部所有的数组,换句话说,只要我们的对象实现的Iterable接口,就可以使用for…of遍历。
const obj = {
store:['foo','bar','baz']
[Symbol.iterator]:function(){
let index = 0
const self = this
return {
next: function(){
const result = return{
value:self.store[index]
done:index>=self.store.length
}
index++
return result
}
}
}
}
//Iterable可迭代接口-->Iterator迭代器接口-->IterationResult迭代结果接口
迭代器模式
场景:你我协同开发一个任务清单应用
//我的代码------------------
const todos = {
life:['吃饭','睡觉','打豆豆']
learn:['语文','数学','外语']
work:['喝茶']
each: function(callback){
cosnt all = [].concat(this.lif,this.learn,this.work)
for(const item of all){
callback(item)
}
}
[Symbol.iterator]: function(){
const all = […this.lif, …this.learn, …this.work]
let index = 0
return {
next: funtion () {
return {
value: all[index].
done: index++ >= all.lenth
}
}
}
}
}
//你的代码------------------
todos.each(function (item) {
console.log(item)
})
for(const item of todos){
console.log(item)
}
对外提供统一遍历接口,让外部不再关心数据内部结构是什么样的。
Generator
避免异步编程中回调嵌套过深,提供更好的异步编程解决方案。
functioon * foo () {
console.log('zce')
return 100
}
const result = foo()
console.log(result) //打印一个Generator对象
function * foo () {
console.log('111')
yield 100
console.log('222')
yield 200
console.log('333')
yield 300
}
调用生成器函数会一个返回Generator对象,调用这个对象的next()方法才会执行,在执行过程中。遇到yield关键字就会停下来,yield的值会作为next的结果返回回来,继续调用next会继续执行
生成器应用
//发号器
function * createIDMaker () {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createIDMaker()
idMaker.next()
//使用Generator函数实现iterator方法
const todos = {
life:['吃饭','睡觉','打豆豆'],
learn:['语文','数学','外语'],
work:['喝茶'],
[Symbol.iterator]:function * () {
const all = […this.life, …this.learn, …this.work]
for(const item of all){
yield item
}
}
}
for (const item of todos) {
console.log(item)
}
ES Modules
语法层面的模块化标准。
ECMAScript 2016
Array.prototype.includes
const arr = ['foo', 1, NaN, false]
//之前的方法
console.log(aee.indexOf('foo'))
//会返回数组的下标
//缺陷:不能查找数组里面的NaN
console.log(aee.includes('foo'))
//返回布尔值
//可以查找NaN
指数运算符
//以前的方法
console.log(Math.pow(2,10))
//现在的方法
console.log(2 ** 10)
ECMAScript 2017
object.values
返回对象所有的值的数组,Object.keys返回对象所有的键的数组。
Object.entries
//返回对象中所有的键值对的数组
for (const [key, value] of Object.entries(obj)){
console.log(key, value)
}
//可以使用for…of遍历Object.entires返回的数组
console.log(new Map(Object.entries(obj)))
//Map就需要这种格式的数组,使用这种方法就可以将对象转换成Map类型的对象
Object.getOwnPropertyDescriptors
ES5之后,就可以给对象添加get() set()属性。
const p1 = {
firstName: 'lei',
lastName:'Wang',
get fullName(){
return this.firstName + '' + this.lastName
}
}
console p2 = Object.assign({}, p1)
p2.firstNmae = 'zce'
console.log(p2)
//拿到的结果是p1的firztName,这是因为assign在复制时,他只是把fullNma当作一个普通的属性去复制了
//解决方法
const descript = Object.getOwnPropertyDescriptors(p1)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName)
String.prototype.padStart / String.prototype.padEnd
字符串的填充方法
const books = {
html: 5,
css: 15,
javascript: 128
}
for(const [name, count] of Object.entries(books)) {
console.log(`${name.padEnd(16, '-')}|${count.tostring().padStrat(3, '0')}`)
}
//html------------|005
//css--------------|016
//javascript-----|128
//去填充目标字符串的开始或结束位置
尾逗号Trailing commas
ES8 允许函数的最后一个参数有尾逗号(Trailing comma)。
此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。
async/await
async 和 await 是一种更加优雅的异步编程解决方案,是Promise 的拓展。
javascript异步编程中有详细介绍。