ES6P2(Symbol、Set和Map、class、迭代器、生成器)


一、Symbol

为什么要引入一个新的数据类型?
ES5 的对象属性名都是字符串,这容易造成属性名的冲突,如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。
而Symbol就是ES6新提出的一种基本数据类型,表示独一无二的值;

  • Symbol的声明
    注意:生成的 Symbol 是一个原始类型的值,不是对象,不能使用new命令来调用
    let s1 = Symbol();
    let s2 = Symbol();
    console.log(s1, s2, typeof s1, typeof s2);//Symbol() Symbol() 'symbol' 'symbol'
    console.log(s1 === s2);//false
    //Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述
    let s3 = Symbol('s1');
    let s4 = Symbol('s2');
    console.log(s3, s4, typeof s3, typeof s4);//Symbol(s1) Symbol(s2) 'symbol' 'symbol'

Symbol()函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的

  • Symbol 值不能与其他类型的值进行运算,会报错。
let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
  • Symbol.prototype.description
    let s = Symbol("spylent");
    console.log(s.description);//spylent
  • Symbol用作对象的属性名
    Symbol 值作为对象属性名时,不能用点运算符
    let s = Symbol('s');
    let a = Symbol('a');
    let obj = {
      name: "spylent",
      [Symbol('h')]: "嘻嘻",
      [s]: function () {
        console.log("我是symbol类型");
      }
    }
    obj[a] = "疯兔";
    console.log(Reflect.ownKeys(obj));//["name",Symbol(h),Symbol(s),Symbol(a)]
  • Object.getOwnPropertySymbols()遍历对象中的symbol属性名
const obj = {};
let a = Symbol('a');
let b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

const objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols
// [Symbol(a), Symbol(b)]
  • Symbol.for()
    接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值
    let s1 = Symbol.for('s');
    let s2 = Symbol.for('s');
    console.log(s1 === s2, s1, s2, typeof s1, typeof s2);//true Symbol(s) Symbol(s) 'symbol' 'symbol'
    console.log(Symbol.keyFor(s1));//s

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。


二、Set和Map

Set

Set是什么?

Set是类似于数组的一种数据结构,不同于数组的是,Set中的元素都不会重复;

Set的声明

Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化

    let s = new Set(['spylent', 'AK', 'taoie', 'taoie']);
    console.log(s);//Set(3) { 'spylent', 'AK', 'taoie' }
    for (const v of s) {
      console.log(v);//spylent AK taoie
    }

Set实例的操作方法

  • size():返回Set实例的成员总数
  • add():添加元素,返回 Set 结构本身
  • has():返回一个布尔值,表示该值是否为Set的成员
  • delete():删除对应的元素,返回一个布尔值,表示删除是否成功
  • clear():清空元素,没有返回值
    let s = new Set();
    let s2 = new Set(['spylent', 'AK', 'taoie', 'taoie']);
    s2.add(3); //添加
    s2.delete('AK'); //删除对应的值
    //s2.clear(); 清空
    console.log(s2.has('spylent')); //true
    console.log(s2);//Set(3) { 'spylent', 'AK', 'taoie' }

    for (const v of s2) {
      console.log(v);//spylent AK taoie
    }

注意:向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

Set实例的遍历方法

  • keys()
  • values()
  • entries()
    三中方法返回的都是遍历器对象
    由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

Set的应用

  • 数组去重
const arr = [1, 2, 2, 2, 3, 3, 4, 5, 6, 9, 7, 6, 5, 4];
console.log([...new Set(arr)])//[1, 2, 3, 4,5, 6, 9, 7 ]
  • 交集
let result = [...new Set(arr)].filter(item => newArr1.has(item));
  • 并集
let union = [...new Set([...arr, ...arr1])];
  • 差集
let result1 = [...new Set(arr)].filter(item => !(newArr1.has(item)));

Map

Map是什么?

Map是类似于对象的一种数据结构,不同于对象的是,对象中的属性名只能是字符串而map中的属性名可以是任意的数据类型

Map的声明

Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构,都可以当作Map构造函数的参数

    let m = new Map([["name", "spylent"], [1, 2], [() => { }, "函数"]]);
    console.log(m);//Map(3) { 'name' => 'spylent', 1 => 2, [Function (anonymous)] => '函数' }

如果对同一个键多次赋值,后面的值将覆盖前面的值。

const map = new Map();

map
.set(1, 'aaa')
.set(1, 'bbb');

map.get(1) // "bbb"

Map实例的操作方法

  • size():返回 Map 结构的成员总数。
  • set():设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
  • get():读取key对应的键值,如果找不到key,返回undefined。
  • has():返回一个布尔值,表示某个键是否在当前 Map 对象之中.
  • delete():删除某个键,返回true。如果删除失败,返回false。
  • clear():清除所有成员,没有返回值。
	let mbj = new Map();
    mbj.set("abc", 3);
    let obj = {
      name: "曹颖梅"
    }
    mbj.set(obj, [1, 2, 3]);
    console.log(mbj);//Map(2) { 'abc' => 3, { name: '曹颖梅' } => [ 1, 2, 3 ] }
    console.log(mbj.size);//2
    console.log(mbj.get(obj));//[ 1, 2, 3 ]
    console.log(mbj.has("abc"));//true
    for (const v of mbj) {
      console.log(v);
    }
    //[ 'abc', 3 ]
    //[ { name: '曹颖梅' }, [ 1, 2, 3 ] ]

Map实例的遍历方法

  • keys()
  • values()
  • entries()
    三中方法返回的都是遍历器对象
const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

//map.keys():[Map Iterator] { 'F', 'T' }
for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

//map.values():[Map Iterator] { 'no', 'yes' }
for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

//[Map Entries] { [ 'F', no ], [ 'T', "yes" ] }
for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

Map和对象的区别?

  • 对象的键只能是字符串,Map的键可以是任意数据类型的值
  • 对象没有迭代器接口,不能被for of遍历,Map有迭代器对象,可以被for of遍历
  • 对象的键是无序的,而Map的键是有序的
  • Map的键值对个数可以轻易通过size获取,而对象只能手动计算

三、class(类)

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

  • ES5写法:
	function Phone(brand, price){
		this.brand = brand;
        this.price = price;
	}
	Phone.prototype.call = function(){
		console.log("我可以打电话!");
	}
	Phone.prototype.network= function(){
		console.log("我可以上网!!");
	}
    let huawei = new Phone("华为", 6999);
    console.log(huawei);
    huawei.call();
    huawei.network();
  • ES6写法:
    class Phone {
      //构造方法,固定
      constructor(brand, price) {
        this.brand = brand;
        this.price = price;
      }
      //添加方法必须使用该语法
      call () {
        console.log("我可以打电话!");
      }
      network () {
        console.log("我可以上网!!");
      }
    }
    let huawei = new Phone("华为", 6999);
    console.log(huawei);
    huawei.call();
    huawei.network();

一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加

class Point {
}

// 等同于
class Point {
  constructor() {}
}
  • 静态属性和静态方法
    类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
    class People {
      static name = "cym";
      static change () {
        console.log("我要改变世界");
      }
    }
    let p = new People();
    console.log(p.name);//undefined
    // p.change()//p.change is not a function
    console.log(People.name)//cym
    People.change()//我要改变世界

类的继承

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法

    class Phone {
      constructor(brand, price) {
        this.brand = brand;
        this.price = price;
      }
      telephone () {
        console.log("我可以打电话");
      }
    }
    class SmartPhone extends Phone {
      constructor(brand, price, color, size) {
      //ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错
        super(brand, price);//super在这里表示父类的构造函数,用来新建一个父类的实例对象
        this.color = color;
        this.size = size;
      }
      sing () {
        console.log("我可以唱歌");
      }
      dance () {
        console.log("我可以跳舞");
      }
      telephone () {//子类对父类方法的重写
        console.log("我也可以打电话!");
      }
    }
    let s = new SmartPhone("huawei", 3456, "黑色", 6.5);
    s.telephone();

四、interator迭代器

什么是interator?

遍历器(Iterator)抽象化来说就是一种机制。它是一种接口,为各种不同的数据结构提
供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
通俗来说遍历器其实就是数据结构原型对象上的一个属性,通过[Symbol.interator]属性名得到一个函数,该函数返回一个指针对象,指向当前数据结构的起始位置;
Iterator 接口主要供 for…of 消费

原理

  • 创建一个指针对象,指向当前数据结构的起始位置
  • 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  • 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  • 每调用 next 方法返回一个包含 value 和 done 属性的对象
let arr = ['spylent', '疯兔', '嘿嘿'];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());//{ value: 'spylent', done: false }
console.log(iterator.next());//{ value: '疯兔', done: false }
console.log(iterator.next());//{ value: '嘿嘿', done: false }
console.log(iterator.next());//{ value: undefined, done: true }

原生具备 Iterator 接口的数据结构

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

五、generator生成器

生成器函数是 ES6 提供的一种异步编程解决方案;用来解决以前异步编程用纯回调函数编写造成的回调地狱的问题;
形式上,Generator 函数是一个普通函数,但是有两个特征。
是,function关键字与函数名之间有一个星号;
是,函数体内部使用yield表达式,定义不同的内部状态

//* 的位置没有限制
function* gen () {
  console.log("aaa");
}
// gen();//直接调用没有反应
let generator = gen();//生成器函数返回的结果是迭代器对象
// console.log(generator);
generator.next();//aaa
    function user () {
      setTimeout(() => {
        let data = "用户数据";
        generator.next(data);//第二次调用next()参数会作为第一个yield的结果返回;
      }, 1000)
    }
    function order () {
      setTimeout(() => {
        let data = "订单数据";
        generator.next(data);//第三次调用next()参数会作为第二个yield的结果返回;
      }, 1000)
    }
    function good () {
      setTimeout(() => {
        let data = "商品数据";
        generator.next(data);第四次调用next()参数会作为第三个yield的结果返回;
      }, 1000)
    }
    function* gen () {
      let user = yield user();
      console.log(user);//用户数据
      let order = yield order();
      console.log(order);//订单数据
      let good = yield good();
      console.log(good);//商品数据
    }
    let generator = gen();
    generator.next();//第一次调用next()
  1. * 的位置没有限制
  2. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到
    yield 语句后的值
  3. yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next
    方法,执行一段代码
  4. next 方法可以传递实参,作为 yield 语句的返回值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值