ES6(二):Symbol、class初识、迭代器和生成器工作原理

ES6(二)

Symbol的基本使用

基本概念

ES6引入了一种新的原始数据类型Symbol(因为不是对象,所以不能添加属性),表示独一无二的值。他是JS语言的第七种数据类型,是一种类似于字符串的数据类型。

ES5的对象属性名都是字符串,这容易造成属性名的冲突,所以我们新增Symbol类型,保证每个属性的名字独一无二,可以从根本上防止属性名的冲突

  1. Symbol 的值是唯一的,用来解决命明冲突的问题
  2. Symbol 值不能与其他数据类型进行运算,会报错。(但可以进行类型转化,利用Boolean()转化为布尔值,利用String()转化为字符串。
  3. Symbol 定义的对象不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
//创建Symbol
let s = Symbol();//通过函数调用,返回一个Symbol类型的值
console.log(s, typeof s);//类型是“symbol”

let s2 = Symbol('Joseph');//括号里的字符串相当于对这个s2的描述
let s3 = Symbol('Joseph');
s2.toSring();//得到"Symbol(Joseph)";
let name = s2.description;//可以取出描述值,这个description是在Symbol.prototype上的
console.log(s2 === s3);
//得到false 

作为属性名时候的Symbol

  • 注意:不能用点运算符,只能用方括号
let s1 = Symbol();

//第一种写法
let a = {};
a[s1] = "Joseph"

//第二种
let a = {
    [s1] : "Joseph"
}

//第三种
let a = {};
Object.defineProperty(a,s1,{value:'Hello'});

console.log(a[s1]);
//都可以得到"Joseph"
  • 使用点运算符新增的属性,属性名的类型都是字符串类型,利用[]查找时,里面应该是"属性名"
const s1 = Symbol();
const a = {}
a.s1 = "Joseph";
//这里实质上新增了一个属性,他的属性名是字符串类型的,与代码第一行无关
console.log(a['s1']);//可以有结果
console.log(a[s1]);//结果未被定义
  • 因此在对象内部定义的时候,Symbol的值(即属性名)必须放在方括号中
let s1 = Symbol();

let obj = {
	[s1] : function (arg){}
}

obj[s1]();
//这里时是调用方法

属性名遍历中的Symbol

  • Symbol作为属性名时,遍历对象for(var prop in 对象){}时候,不会检索到它,也不会被一些对象的方法Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,即得到的里面没有Symbol类型的属性名
  • 但是它可以利用Object.getOwnPropertySymbols()方法,获取指定对象中的所有Symbol属性名。该方法返回的是数组
const obj = {};
const s1 = Symbol('s1');

obj[s1] = 'joseph';

for(let i in obj){
	console.log(i);
	//这里得不到输出
}
Object.getOwnPropertySymbols(obj);//可以得到[Symbol(foo)];
Object.getOwnPropertyNames(obj);//得到空数组[]

Reflect.ownKeys()方法则可以返回所有类型的键名

let obj = {
	[Symbol('s1')] : 1,
	enum : 2
}
Reflect.ownKeys(obj);
//得到[ Symbol(s1),"enum"];

class初识

  • 类似于JS自定义构造函数,可以定义并生成新对象(联系JS笔记三创建对象的方式)
  • 函数声明和类声明的最大区别在于函数声明会出现声明提前
//声明一个类
class Rectangle {
	constructor(height,width){
		this.height = height;
		this.width = width;
	}
	//get 方法即调用将会返回的值(只读)
	get area(){
		return this.calcArea();
	}
	calcArea{
	return this.height * this.width
	}
	//static 为一个静态方法,new构造出来的对象无法使用该方法
	//一般用法 Rectangle.heightlength(s1);
	static heightlength(obj){
        return obj.height;
        
    }
}

//下面是使用
const s1 = new Rectangle(100,100);
console.log(s1.area);

Symbol.for()

  • 利用for创造的话,传进去相同的描述值,赋出来的Symbol是能够做到一样的
var s1 = Symbol.for('Joseph');
var s2 = Symbol.for('Joseph');
s1 === s2 //得到true,一般我们利用这个来得到同一个Symbol值
//同样不能运算
  • 区别:Symbol.for和Symbol都是生成新的Symbol。区别在于前者会被登记在全局环境中供搜索,后者不会。即Symbol()没有登记机制

Symbol.keyFor()

  • 得到Symbol值的key名字(但是这里要注意只能返回有登记机制的Symbol,即用Symbol.for创建的)
var s1 = Symbol.for('Joseph');
var s2 = Symbol('koseph');
console.log(Symbol.keyFor(s1));
//得到'Joseph'
console.log(Symbol.keyFor(s2));
//得到undefined

往对象里添加Symbol类型属性

  • 一般对象自身可能就有一些方法,我们不想因为新加的属性名将其覆盖,所以采用添加Symbol类型的属性

方式一:

let game = {
	//up和down是对象本身自带的,我们不想将其覆盖
	
    //单独设置一个对象存Symbol
	let methods = {
	up : Symbol(),
	dowm : Symbol()
	}
	
	game[methods.up] = function(){
	....
	}
	game[methods.down] = function(){
	
	}
}

方式二:

let game = {
	name : 'Mario',
	[Symbol('jump')] : ()=>{
	...
	}
	[Symbol('eat')] : function(){
	..
	}
}

Symbol内置的值(指向内部使用的方法)

Symbol.hasInstance(构造函数的)

  • instanceof 的本质,比如 foo instance of Foo ,实质是Foo[Symbol.hasInstance] (foo)
class Person {
	[Symbol.hasInstance](param){
	console.log(param);
	console.log('instanceof Person的实质就是调用Person里面的这个内置的Symbol值');
	return true;
	}
}
let obj = {}
console.log(obj instanceof Person);
//得到{} 代码第四行 和 true

Symbol.isConcatSpreadable(数组的)

  • 一般我们调用数组的concat方法,连接数组里面的元素,就是我们会把传进去的数组进行扩展,再一个个push,但我们利用这个内置的值可以令他不扩展连接
let arr1 = ['a','b'];
let arr2 = ['c','d'];
//下面是concat默认情况
console.log(arr1.concat(arr2));
//得到['a','b','c','d'];
console.log(arr1[Symbol.isConcatSpreadable]);
//得到undefined




arr1[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2));
//得到['a','b',['c','d']];

所以当Symbol.isConcatSpreadable是true和undefined时都是默认展开

还有一系列关于字符串的,参考阮一峰的ES6

迭代器

  • 迭代器(iterator)是一种接口,他是对象中的 Symbol.iterator这个属性,指向该对象的默认遍历器方法 ,为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署了iterator接口,就可以完成遍历操作。
  • ES6创造的一种新的遍历命令for…of,使用for…of循环时,会调用Symbol.iterator方法
  • 原生具备iterator的数据结构
    1. Array
    2. Arguments
    3. Set
    4. Map
    5. String
    6. TypedArray
    7. NodeList
const arr1 = ['a','b','c','d'];

//获取这个迭代器的指针对象,因为返回的才是指针对象,所以要加括号执行这个方法
let iterator = arr1[Symbol.iterator]();

console.log(iterator.next());
//得到{valun:'a',done:false}
console.log(iterator.next());
//得到{valun:'b',done:false}
console.log(iterator.next());
//得到{valun:'c',done:false}
console.log(iterator.next());
//得到{valun:'d',done:false}
console.log(iterator.next());
//得到{valun:undefined,done:true}
//当done为true,会自动停止循环

迭代器工作原理

当我们想自定义遍历数据,要想到迭代器

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用next方法,指针一直往后移动,直至指向最后一个成员
  4. 每次调用next方法返回一个包含value和done属性的对象
//声明一个对象,对象一般遍历用for...in
//现在我们想遍历对象中的某个特定数组,用for..of实现
//对象本身没有[Symbol.iterator],我们要自定义

var obj = {
        name: "Joseph",
        capacity: ["fly", "attack", "defend", "jump"],

        [Symbol.iterator]() {
          //创建索引变量
          var index = 0;

          //返回指针对象
          return {
              //指针对象中的next方法
            next: () => {
                //我们这里利用箭头函数,让this指向next声明时的作用域
                //next方法同样要有返回结果,里面有value和dobe
              if (index < this.capacity.length) {
                const result = { value: this.capacity[index], done: false };
                index++;
                  //下标自增
                return result;
                  //返回结果
              } else return { value: undefined, done: true };
            },
          };
        },
      };
      for (let c of obj) {
        console.log(c);
      }

生成器

  • 生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
//一个解决异步编程的特殊函数
//之前一般都是利用回调函数的方式实现异步

function * gen(){
    console.log('Hello generator');
}
let iterator = gen();
console.log(iterator);
//没有得到第5行代码的内容
//得到的是一个迭代器对象,类似返回了一个指针对象,里面有next方法

iterator.next();
//这里能得到第五行代码的内容

  • 函数代码的分隔符 yield(后面跟表达式或者自变量)
//三个分隔符产生四块代码,由next一块块执行
function * Sn(){
    console.log(1);
    yield 'The first';
        console.log(2);
    yield 'Second';
        console.log(3);
    yield 'Third';
        console.log(4);
  //这里是第四段结尾
}
let interator = Sn();
interator.next();//输出1
interator.next();//输出2
interator.next();//输出3
interator.next();//输出4


console.log(interator.next());
//这里next()返回的会是一个含valun和done的对象,前提是我们要先把上面13-16行代码给注掉
console.log(interator.next());
//2
//{value:"Second",done:false}
console.log(interator.next());
//3
//{value:"Third",done:false}
console.log(interator.next());
//4
//{valun:undefined,done:true}

//因为是一个指针对象,所以我们可以用for...of来遍历
//同样首先需要把上面有关next的语句先注释掉
for(let v of interator){
        console.log(v);
      }
//得到yield里面的值,一共得到三条语句

关于生成器的函数参数

function * Sn(arg){
    console.log(arg);
    let one = yield 'The first';
    console.log(one);
    yield 'Second';
    yield 'Third';

}

let interator = Sn("AAA");
 //获取迭代器对象,并传入实参

console.log(iterator.next());
//得到'AAA' 和 {value:'The first',done:false}

//next方法也可以传入实参,实参会赋值给yield语句的返回结果
//第二次调用next传入实参会成为第一次yield的返回结果
console.log(iterator.next('BBB'));
//自上面14行代码,得到'BBB' , {value:'Second',done:false}

生成器函数实例

//异步编译  	--比如文件操作,网络操作(ajax,request), 数据库操作
//1s 后在控制台输出111, 2s后输出222, 3s后输出 333
//回调地狱
setTimeout(()=>{
    console.log(111);
    setTimeout(()=>{
        console.log(222);
    	setTimeout(()=>{
    		console.log(333);
		},3000)
	},2000)
},1000)

//现在我们利用生成器函数,把三个函数的调用把在yield里面

function one(){
    setTimeout(()=>{console.log(111)},1000);
    iterator.next();
}
function two(){
    setTimeout(()=>{console.log(222)},2000);
     iterator.next();
}
function three(){
    setTimeout(()=>{console.log(333)},3000);
     iterator.next();
}

function * behave(){
    yield one();
    yield two();
    yield three();
}
//获取这个生成器函数
let iterator = behave();
iterator.next();
//重点注意18,22,26行
  • 模拟获取(先获取用户数据,再订单数据,商品数据)
function getUsers() {
    setTimeout(() => {
        let data = "用户数据";
        interator.next(data);
    }, 1000);
    //调用next方法,并且将数据传入实参

}
function getOrders() {
    setTimeout(() => {
        let data = "订单";
        interator.next(data);
    }, 1000);

}
function getGoods() {
    setTimeout(() => {
        let data = "商品数据";
        interator.next(data);
    }, 1000);

}

function* query() {
    let users = yield getUsers();
    console.log(users);
    let orders = yield getOrders();
    console.log(orders);
    let goods = yield getGoods();
    console.log(goods);
}
let interator = query();
interator.next();

//这里结果是每间隔1s出现数据,因为interator.next();是在定时器里面的

文献参考:阮一峰的ES6,尚硅谷ES6教学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值