博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通俗易懂理解ES6 - ES6的变量类型及Iterator
阅读量:6246 次
发布时间:2019-06-22

本文共 6507 字,大约阅读时间需要 21 分钟。

引言

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

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

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

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

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

Symbol类型:

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

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

不允许在 Symbol 前使用 new,symbol 类型的值可通过直接调用 Symbol 函数创建

let symbol = Symbol();symbol;         //Symbol()typeof symbol;  //symbollet 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;         //falsesymbol1 === 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 stringlet newNum = Symbol + num;          //Uncaught TypeError: Cannot convert a Symbol value to a numbersymStr;                             // 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      //falsesymbol === symbol2      //falsesymbol1 === 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 的属性介绍,因为感觉在日常开发中使用的概率比较低,因此也不赘述一些自己的理解了,有兴趣的朋友可以去看看

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

在原有 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 遍历时遇到的一部分问题。

以上。

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

转载于:https://juejin.im/post/5ccaa821f265da035e21348c

你可能感兴趣的文章
Java 斜杠 与 反斜杠
查看>>
垂直居中
查看>>
idea下maven项目,样式css、js更新后,页面不显示更新内容
查看>>
bzoj 1001 平面图转对偶图 最短路求图最小割
查看>>
php 记住密码自动登录
查看>>
NSThread创建线程的三种方法
查看>>
Logger.getLogger与LogFactory.getLog
查看>>
HDU4671 Backup Plan(构造序列-多校七)
查看>>
一些难得一见的代码问题
查看>>
Read–eval–print loop
查看>>
如果我是面试官 我要出什么题目(常更新)
查看>>
初识nginx
查看>>
React Native
查看>>
最优化
查看>>
HDU1495 非常可乐
查看>>
CCF NOI1071 Pell数列
查看>>
Studio快捷键
查看>>
75. Sort Colors(按颜色进行排序)(leetcode)
查看>>
4_文件与目录权限
查看>>
SQLServer 2008 R2 清空日志文件
查看>>