文章目录
一、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()
*
的位置没有限制- 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到
yield 语句后的值 - yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next
方法,执行一段代码 - next 方法可以传递实参,作为 yield 语句的返回值