目录
· ES6-Symbol属性
· ES6-迭代器
· ES6-生成器
ES6中引入了一种新的原始数据类型Symbol,表示独一无二的值,我们可以将其理解为一个独一无二的字符串,它是JavaScript的第七种数据类型(Number,String,Boolean,Undefined,Null,Object,Symbol),是一种类似于字符串的数据类型。Symbol的特点如下:
// 1)创建Symbol类型的值
let s1 = Symbol('为Symbol类型添加的标识') //一种独一无二的值
let s2 = Symbol('为Symbol类型添加的标识') //一种独一无二的值
// 标识只是为了区分Symbol类型
// 具有相同的标识的Symbol类型的值仍旧不一样
console.log(s1 === s2 ) //false
console.log(s1,typeof s1) // Symbol(为Symbol类型添加的标识) 'symbol'
// 2) 使用Symbol.for()创建Symbol类型的值
let s3 = Symbol.for('1024')
let s4 = Symbol.for('1024')
//使用Symbol.for()创建的类型,若标识值一样,则Symbol值也一样
console.log(s3 === s4 ) //true
// 3)使用Symbol属性为对象创建独一无二的属性名
let onlyOne = Symbol("此Symbol类型的标识")
obj = {
//可以将Symbol类型理解为独一无二的字符串,即onlyOne中可以理解为存储的独一无二的字符串
//这里注意使用Symbol类型的值作为属性时,因为变量中存储的是值,需要用[]将变量包裹
[onlyOne]:1024,
fun:function(){
console.log('我是对象中的方法!')
}
}
// 4)使用for...in遍历对象键名
for (let key in obj)
{
// Symbol类型的定义属性名不能用for...in遍历
console.log(key) //fun
}
//使用Reflect.ownKeys获取对象所有属性名
//Reflect.ownKeys将对象以数组类型返回,使用for...of遍历可迭代对象的值
for (let key of Reflect.ownKeys(obj))
{
console.log(key) //fun Symbol(此Symbol类型的标识)
}
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署有iterator接口,就可以使用for...of完成遍历操作。原生具备iterator接口的数据结构如下:
for...of遍历的是当前数据结构中的键值,而for...in遍历的是当前数据结构中的键名,包括设置原型中的方法,所有for...in一般是用来遍历对象的属性名的。
let arr=[1024,200,404]
for(let val of arr)
{
//for...of遍历的当前数据结构中的键值
console.log(val) //1024 200 404
}
我们输出以上代码段定义的arr数据结构如下图,发现包含有Symbol类型的一个迭代器方法,其中标识为Symbol.iterator的预定义值,内部声明为Symbol.iterator = Symobl(Symbol.iterato),该方法调用返回一个迭代器对象,其中包含有next()方法,不断调用next()方法就可以遍历此数据结构。因此,for...of其实就是调用数据结构的iterator对象的next()方法去进行数据遍历的。
let arr=[1024,200,404]
//for...of其实就是调用数据结构的iterator对象的next()方法
let iterator = arr[Symbol.iterator]() //返回迭代器对象
//使用next()方法遍历数据结构
//next方法返回当前值以及遍历完成状态
console.log(iterator.next()) // {value: 1024, done: false}
console.log(iterator.next()) // {value: 200, done: false}
console.log(iterator.next()) // {value: 404, done: false}
console.log(iterator.next()) // {value: undefined, done: true}
除了使用for...of对原生具有迭代器接口的数据类型进行遍历,有了以上学习,我们还可以自定义迭代器:
以下代码段是使用自定义迭代器对obj对象中的lesson属性值进行遍历。
let obj = {
name:'Web',
lessons: [
'Html5',
'Css3',
'JavaScript',
'Ajax',
'React',
'Vue'
],
[Symbol.iterator]:function(){
let index = 0 //初始指针,指向遍历的索引
let _this = this //_this指向obj对象
return {
next:function(){
if(index < _this.lessons.length){
const result = {value:_this.lessons[index],done:false}
index++
return result
}
else{ //遍历完成,返回 undefined true
return {value:undefined,done:true}
}
}
}
}
}
//使用自定义迭代器对obj对象中的lesson属性值进行遍历
for(let lesson of obj)
{
console.log(lesson) //Html5 Css3 JavaScript Ajax React Vue
}
生成器其实就是一个特殊的函数,它会返回一个迭代器对象,生成器主要是为了解决异步编程(定时器操作、文件操作、网络操作:Ajax,request、数据库操作等)的问题,其中使用关键字yield进行函数代码的分割。
我们先引出这样一个例子:
function fun1(){
setTimeout(function(){
console.log('延迟3s')
setTimeout(function(){
console.log('延迟2s')
setTimeout(function(){
console.log('延迟1s')
},1000)
},2000)
},3000)
}
fun1() //延长3s 延长2s 延迟1s
由于定时器函数是异步执行的,其中使用了时间循环模型。若想先延迟3s,再延迟2s,最后再执行延迟1s的函数,需要 如上代码的嵌套,很容易产生回调地狱的问题,即代码结构混乱,不易维护。接下来我们使用生成器函数解决此问题。
function fun1(){
setTimeout(function(){
console.log('延迟3s')
iterator.next('延迟3s')
},3000)
}
function fun2(){
setTimeout(function(){
console.log('延迟2s')
iterator.next('延迟2s')
},2000)
}
function fun3(){
setTimeout(function(){
console.log('延迟1s')
iterator.next('延迟1s')
},1000)
}
function * gen(){
let p1 = yield fun1() //通过next()参数传参得到的返回结果
console.log(p1) //延迟3s
let p2 = yield fun2() //通过next()参数传参得到的返回结果
console.log(p2) //延迟2s
let p3 = yield fun3() //通过next()参数传参得到的返回结果
console.log(p3) //延迟1s
}
let iterator = gen()
iterator.next() //生成器函数返回的迭代器对象使用next()方法进行遍历执行yield后边的方法
上述输出为:延迟3s 延迟2s 延迟1s。使用生成器函数解决了回调地狱的问题,而且由上述代码段可知next()是可以传参的,参数作为上一个yield的返回结果。
既然生成器函数返回的是一个迭代器对象,那么就可以使用for...of遍历。
function * gen(){
yield '1024'
yield '200'
yield '404'
}
for(let v of gen()){
console.log(v) // 1024 200 404
}