通俗易懂理解ES6 - ES6的变量类型及Iterator

引言

万丈高楼平地起,欲练此功,必先打好基本功: )java

在了解 ES6 新增的变量类型前,咱们必须先知道 JavaScript 在ES6以前,有以下六种基本数据类型:Null、Undefined、Number、String、Boolean和Object。而 ES6 中,新增了第七种数据类型:Symbol。
上述七种数据类型做以下类型划分:数组

基本类型: Undefined、Null、Boolean、String、Number,这五种类型的变量都是直接把实际值存储在栈内存当中,操做或访问的时候都是直接对实际值进行的.数据结构

引用类型: Object。Object 类型的变量是把指向堆内存的地址值存储在栈内存当中的一类数据。(关于堆栈的知识将会在后面的文章中做介绍。)函数

有关基本类型和引用类型的说明,网上已经有不少文章有说明介绍,为免篇幅过长,这里就再也不重复叙述了。测试

Symbol类型:

这里咱们着重说一下 Symbol 类型:


Symbol 是一个函数,调用该函数,返回的惟一值就是 Symbol 类型值;this

不容许在 Symbol 前使用 new,symbol 类型的值可经过直接调用 Symbol 函数建立

let symbol = Symbol();
symbol;         //Symbol()
typeof symbol;  //symbol
let symbol1 = new Symbol();     //Uncaught TypeError: Symbol is not a constructor

Symbol 函数调用时,可接受一个参数,该参数会经过 toString 方法变为字符串,做为 symbol 值的说明,传入的参数不能够为 symbol 类型的值

let testStr = 'this is a string',
    testObj = {obj: 'this is a object'},
    testArr = ['this','is','a','array'],
    testFn = () => {
        console.log('this is a function');
    },
    testSym = Symbol('this is a symbol'),
    symbolStr = Symbol(testStr),            //Symbol(this is a string)
    symbolObj = Symbol(testObj),            //Symbol([object Object])
    symnolArr = Symbol(testArr),            //Symbol([object Object])
    symbolFn = Symbol(testFn),              //Symbol(() => {console.log('this is a function');})
    symbolSym = Symbol(testSym);            //Uncaught TypeError: Cannot convert a Symbol value to a string

Symbol 函数调用后生成的值是惟一的

let symbol1 = Symbol('test'),
    symbol2 = Symbol('test');
symbol1 == symbol2;         //false
symbol1 === symbol2;        //false

Symbol 值不能与其余类型值进行运算、隐式转换,不然会报错;但能经过 toString 方法显式转为字符串。

let symbol = Symbol('this is symbol'),
    str = 'this is string',
    num = 2,
    symStr = symbol.toString();
    
let newStr = symbol + str;          //Uncaught TypeError: Cannot convert a Symbol value to a string
let newNum = Symbol + num;          //Uncaught TypeError: Cannot convert a Symbol value to a number
symStr;                             // Symbol(this is symbol)

Symbol 值的惟一性,用于 Object 的属性中,能够确保不会出现同名属性

let symbol = Symbol('this is symbol'),
    symbol1 = Symbol('this is symbol');
let obj = {
    [symbol]: 'this is a',
    [symbol1]: 'this is b'
};
obj;            //{Symbol(this is symbol): "this is a", Symbol(this is symbol): "this is b"}

let str = 'test',
    str1 = 'test',
    obj = {};
obj[str] = '测试非symbol类型命名的属性';
obj;            //{test: "测试非symbol类型命名的属性"}
obj[str1] = '再次测试非symbol类型命名的属性';
obj;            //{test: "再次测试非symbol类型命名的属性"}

Symbol 命名的属性可经过 Object.getOwnPropertySymbols 获取

let symbol = Symbol('this is symbol'),
    symbol1 = Symbol('this is symbol');
let symbolObj = {
    [symbol]: 'this is a',
    [symbol1]: 'this is b',
}
Object.getOwnPropertySymbols(symbolObj);    //[Symbol(this is symbol), Symbol('this is symbol')]

Symbol 值命名的属性不会出如今 Object.keys、for...in...中,经过Object.getOwnPropertyNames()、JSON.stringify() 也没法获得返回值。但该属性是公开属性,不是私有属性。

let symbol = Symbol('this is symbol'),
    symbol1 = Symbol('this is symbol');
let obj = {
    [symbol]: 'this is a',
    [symbol1]: 'this is b',
    d: 'test'
};
for(key in obj){
    console.log(key);               // d
}
Object.keys(obj);                   // ['d']
Object.getOwnPropertyNames(obj);    // ['d']
JSON.stringify(obj);                //{d:'test'}

//仍可访问到Symbol值定义的属性键和属性值
let symKeys = Object.getOwnPropertySymbols(obj);  
symKeys;                    //[Symbol(this is symbol), Symbol(this is symbol)]
symKeys[0];                 //Symbol(this is symbol)
obj[symKeys[0]];            //this is a

可经过 Symbol.for('xxx') 得到以'xxx'做为入参而生成的 Symbol 值,若不存在以'xxx'做为入参生成的 Symbol 值,则会自动建立一个以'xxx'为入参的 Symbol 值。

let symbol = Symbol('this is symbol'),
    symbol1 = Symbol.for('this is symbol'),
    symbol2 = Symbol.for('this is symbol');
symbol === symbol1      //false
symbol === symbol2      //false
symbol1 === symbol2     //true

12. 经过 Symbol.keyFor 方法可返回一个已“登记”的 Symbol 类型值的修饰词。

经过 Symbol 建立的值有以下两种状况:
被登记的与未被登记的。


什么是被登记的?在设计

let s1 = Symbol.for("foo");
console.log(Symbol.keyFor(s1)); // "foo"

var s2 = Symbol("foo");
console.log(Symbol.keyFor(s2) ); // undefined

这里还有一些 MDN 关于 Symbol 的属性介绍,由于感受在平常开发中使用的几率比较低,所以也不赘述一些本身的理解了,有兴趣的朋友能够去看看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbolcode

关于 Symbol 的总结: 对于 Symbol 的使用,实用性最高的我以为是 symbol 值用于属性命名的状况,在一些开发状况下,对一个 Object 对象进行遍历的时候,但愿某一些属性不被 for in 或 Object.keys 遍历出来,避免作 if 或 switch 状况处理,这时候使用 Symbol 定义属性名是个不错的选择。orm

在原有 ES5 的属性命名和赋值过程当中,多人协做开发可能会致使一些属性名重命名而致使值覆盖,对“半私有属性”属性使用 Symbol 命名属性名会是个很好的选择。对象


Iterator - 迭代器

ES6 在原有的数据结构类型( Array、Object )上新增了两种类型( Map、Set ),咱们在使用的时候还能够经过自由组合的形式使用这些结构类型达到本身想要的数据结构,这就须要一种统一的接口机制供咱们调用处理不一样的数据结构 —— Iterator。

ES6中,只要被遍历“对象”存在 可迭代协议 , 即System.iterator 属性,该对象都是被认为是“可遍历的”。

在 ES6 中,有三类数据结构原生具有 Iterator 接口:__数组、某些相似数组的对象(如 字符串、相似数组形式的对象)、Set 和 Map 结构数据__。

迭代器协议 定义了一种标准的方式来产生一个有限或无限序列的值,每次遍历都会首先调用被遍历数据集合对象中的 [Symbol.iterator]() 方法,该方法返回一个 Symbol对象的iterator属性 ,该属性拥有执行迭代对象的 next 方法,并返回一个对象,以下是一段模拟 next 方法的代码

function Iterator(arr){
    let nextIndex = 0;
    return {
        next: function(){
            return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true};
        },
        [Symbol.iterator]: function() { return this }
    }
}
let example = Iterator(['a', 'b']);

example.next()      // {value: "a", done: false}
example.next()      // {value: "b", done: false}
example.next()      // {value: undefined, done: true}       //遍历结束

返回的对象中必须包含以下两个属性

{
    done,       //迭代是否已经执行完毕  迭代完毕时返回true,不然返回false,返回false时会继续执行迭代
    value       //当前成员的值     迭代完毕时返回undefined,不然返回当前成员的值
}

在某些情景下,JS会默认调用 Symbol.iterator (Iterator接口)

  1. 扩展运算符

    扩展运算符(…)会默认调用 iterator 接口。

  2. 解构赋值

    对数组和Set结构进行解构赋值时,会默认调用 Symbol.iterator 方法。

  3. yield*

    yield*后面跟的是一个可遍历的结构,它会调用该结构的 iterator 接口。

  4. for...of

    执行 for...of循环时,会调用 iterator 接口对数据进行处理。

  5. Array.from()

    Array.form() 时,会遍历数据,调用 iterator 接口返回相应数据

  6. 其它状况还有
    Map()Set()WeakMap()WeakSet() (好比new Map([['a',1],['b',2]]))Promise.all()Promise.race()

顺带一提:

在ES6中,具备 System.iterator 属性的对象都可经过 for...of 进行遍历

let arr = ['1','2','3'];
arr.pro = 'test';
for (let i in arr) {
  console.log(arr[i]);          //1  2  3  test
}
for(let i of arr) {
    console.log(arr[i]);        //1  2  3
}

for...of 的相比于 forEachfor...in ,其好处在于: forEach 循环没法经过 breakcontinuereturn 跳出循环,而 for...of 能够; for...in 循环设计的目的是用于遍历包含键值对的对象,对数组并非那么友好,而 for...of 遍历输出的值会在输出数据后默认遍历结束。

关于 Iterator 的总结: Iterator做为一种统一的接口机制供咱们调用处理不一样的数据结构,让能够用相同的方式来遍历集合,而不用去考虑集合的内部实现,若数据的形式发生改变,只要数据内部还存在 System.iterator 属性,那遍历的代码就能够不用作修改直接使用。 同时,其服务于 for...of 循环, for...of 又有效地避免了以往对数据集仅能经过 forEachfor..in 遍历时遇到的一部分问题。

以上。

文章观点内容若有错误欢迎指出交流,相互进步

相关文章
相关标签/搜索