ES6-类-Symbol
1.类
在javascript语言中,生成实例对象使用构造函数;ES6提供了类Class这个概念,作为对象的模板。定义一个类通过class关键字,ES6的类可以看成是构造函数的另一种写法。
ES5 如何继承 实例使用属性和方法 1.从实例对象本身查找属性或者方法 2.如果实例没有,从构造函数的原型对象中找 3.如果还没有,从父构造函数的原型对象中找
function Person(){} Person.prototype={}; var p1=new Person(); p1.sayName=function(){}; p1.sayName(); //p1去调用sayName,自身有访问自身,自身没有访问构造函数原型对象,构造函数原型对象没有去找父构造函数
1.经典继承 function Animal(type,age,weight,length){ this.type=type; this.age=age; this.weight=weight; this.length=length } Animal.prototype={ constructor:Animal, sayType:function(){ console.log(this.type) } } function Dog(type,age,weight,length,name,color){ // 经典继承又称为构造函数继承 Animal.call(this,type,age,weight,length); this.name=name; this.color=color; } 处理完构造函数处理原型对象 2.//原型链继承 Dog.prototype=new Animal(); Dog.prototype.constructor=Dog; Dog.prototype.sayColor=function(){ console.log(this.color) } var d1=new Dog('狗',1,'10kg','40cm','可乐','白色'); console.log(d1); d1.sayType(); d1.sayColor();
ES6 1.如何定义一个类 class Person{ constructor(name,age){ this.name=name; this.age=age } sayName(){ //--类似于存在类的原型对象中 console.log(this.name) } } 2.constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。通过static关键字来定义静态属性和静态方法。 class Person{ } => class Person{ constructor(){} } 3.定义在类体的方法称为实例方法,其实是存在于Person.prototype中,可供所有的实例调用。 class Person{ constructor(name,age){ this.name=name; his.age=age } sayName(){ //--- 其实可以理解为存在于Person.prototype中 console.log(this.name) } } 4.静态方法 通过static关键字来定义静态属性和静态方法。也可以在外侧添加静态属性;静态属性和静态方法是定义在类【构造函数】上的,所以可以通过类【构造函数】直接访问。在静态方法中,this指向当前类【构造函数】 class Person{ //静态属性是构造函数私有属性 并不是公共属性 static test=['hello'];//可以是引用数据类型 static test1='hello';//可以是基本数据类型 test3=[];---实例私有属性 //静态方法 static sayName(){ return this.test3 } }
class Person{ //构造器 默认有一个,可以显式提供 constructor(name,age){ // 实例的私有属性 this.name=name; this.age=age; } test=['hello'] // 实例的方法 原型对象中的方法 sayName(){ console.log(this.name) } // 静态方法 static sayType(p){ return p instanceof Person } //静态属性 static gende='男' }; let p=new Person('zhangsan',12); let p2=new Person({}); p.test.push('tom') console.log(p,p2);//维护的是私有属性 创建的是不同子类构造函数对象 p.sayName(); console.log(Person.sayType(p));//静态方法由类去调用 console.log(p.test===p2.test);//私有属性不相同 console.log(p.sayName===p2.sayName)//存放在原型对象中的是同一个方法 实例方法和实例属性写在哪 实例可以调用的方法和属性 静态方法和静态属性写在哪 类本身调用的方法和属性
2.继承
class可以通过extends关键字实现继承,子类可以没有构造函数,系统会默认分配。子类提供了构造函数则必须要显式调用super。super函数类似于借用构造函数。类似于Animal.call()
1.子类对象指向父类对象
2.子类原型对象继承父类原型对象
class Animal{ // 静态属性 static animalAttr='Animal的静态属性'; constructor(name,age,weight){ this.name=name; this.age=age; this.weight=weight; } // 实例方法 sayName(){ console.log('实例方法') } // 静态方法 static animalmethod(){ console.log('Animal静态方法') } } // 要实现继承 class Dog extends Animal{ constructor(name,age,weight,color){ super(name,age,weight); this.color=color; console.log('Dog的构造器') } } let dog=new Dog('豆豆',1,10,'golden'); // 继承 子类的原型对象继承父类的原型对象 dog.sayName(); // 继承 子类的对象通过__proto__指针指向父类的对象 Dog.animalmethod(); console.log(Dog.animalAttr); console.log(Dog.__proto__===Animal); console.log(Dog.prototype.__proto__===Animal.prototype)
3.Symbol
ES6引入的一种新的原始数据类型Symbol,表示独一无二的值。Symbol函数可以接受参数,表示对于这个唯一值的描述。属于基本数据类型,Symbol()函数会返回symbol类型的值
// 创建symbol值 let sy1=Symbol('hello'); let sy2=Symbol(); console.log(sy1==sy2); ###1. 为了解决冲突 let obj={ name:'zhangsan', age:12 } // 新增属性 修改属性 let sy3=Symbol('name'); let obj1={ ...obj, // 属性名是变量名时使用中括号 [sy3]:"myname" } console.log(obj1) 案例: let sy1=Symbol('age'); let sy2=Symbol('name'); let obj={ name:"zhangsan", age:12, [sy1]:'myname', [sy2]:'myage', [Symbol('email')]:'kangjie@briup.com' } for(let key in obj){ console.log(key); } let ss=Object.getOwnPropertySymbols(obj); console.log(obj[ss[2]]);
### 2.消除魔术字符串 魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。
function test(shape,options){ let area=0; switch(shape){ case Shape.SJX: area=.5 * options.width*options.height break; case Shape.ZFX: area=options.width*options.height break; case Shape.CIRCLE: area=Math.PI*Math.pow(options.r,2) break; default: area=-1 } return area } let Shape={ SJX:Symbol('sjx'), ZFX:Symbol('zfx'), CIRCLE:Symbol('circle') } let res=test(Shape.SJX,{width:100,height:100,r:100}); console.log(res);
### 3.全局注册表 Symbol() 不同的是,用 Symbol.for() 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
// 将symbol放到全局注册表中 let sy1=Symbol.for('hello'); // 从全局注册表中找到该key对应的value let sy2=Symbol.for('hello'); console.log(sy1===sy2);//true // 每次都会创建一个不同symbol值 虽然描述符一样 但是Symbol value值不一样 let sy3=Symbol('hello'); let sy4=Symbol('hello'); console.log(sy3==sy4);//false Symbol.keyFor() // Symbol.keyFor()可以检测symbol值是否在全局注册表中注册过。 返回对于symbol的描述或者undefined let sy1=Symbol.for('hello'); console.log(Symbol.keyFor(sy1)); let sy2=Symbol('world'); console.log(Symbol.keyFor(sy2))
1.迭代器
迭代器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
Iterator 的遍历过程是这样的。
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
- 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
- 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
1.迭代器实现了Iterator接口,只要有实现了Iterator就可以使用for-of遍历 let arr=[1,2,3,4,5]; console.log(arr.keys()); console.log(arr.values()); console.log(arr.entries()); // keys values entries 当前变量是迭代器对象 // 迭代器对象实现了Iterator接口,只要实现了迭代接口就可以使用for-of 遍历 // let [a,b]=10; 报错10 is not iterable let str='hello'; //实现了迭代器接口,可以遍历 // console.log(a,b); for(let key of str){ console.log(key) } // 以前遍历字符串 let [...a]=str; console.log(a) let result=str.split("") console.log(result); for(i=0;i<str.length;i++){ console.log(str.charAt(i)) } 遍历迭代器对象 let keys=arr.keys() for(let key of keys){ console.log(key) } let values=arr.values(); for(let value of values){ console.log(value,'--------') } let entries=arr.entries(); for(let entry of entries){ console.log(entry) } while(!(result=keys.next()).done){ console.log(result) } for-of实现原理就是调用迭代器的next()方法,第一次调用将指针指向数据结构的第一个成员,依次调用依次指向,直到没有成员可以指向,done为true 迭代过程:创建一个指针对象,指向当前的数据结构起始位置; 第一次调用指针对象的next方法,指向数据结构的第一个成员; 第二次调用指针对象的next方法,指向数据结构的第二个成员; 直到done为true,指向数据结构的结束位置; let keys=arr.keys(); let values=arr.values(); let entries=arr.entries() console.log(keys.next()) console.log(keys.next()) console.log(keys.next()) console.log(keys.next()) console.log(keys.next()) console.log(keys.next()) console.log(entries.next())
原生具备 Iterator 接口的数据结构如下 Array、Map、Set、String、TypedArray、arguments、NodeList
2.Set
Set类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。Set 构造函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
Set API var set=new Set() //创建Set集合 成员是唯一的,key-value是一样的 //添加元素 set.add('hello'); set.add('world'); set.add('world'); console.log(set); // 删除元素 set.delete('hello'); console.log(set); // 遍历 let keys=set.keys() let values=set.values() let entries=set.entries(); console.log(keys,values,entries); set.forEach((value)=>{ console.log(value) }) //判断有没有某个成员 set.has('hello');返回布尔值true或者false //清空set set.clear(); //返回set成员个数 set.size ------------------------------------------------ set应用 set构造函数可以接受数组或者其他可遍历的数据结构 let set=new Set([1,2,3,4,1,2,3,4]); console.log(set); //数组去重 let arr=[1,2,3,5,3,2]; let result=new Set(arr); console.log(result); //将字符串转换为数组 let [...arr1]='hello'; //将set集合转换为数组 let [...arr2]=result; console.log(arr2); console.log([...new Set(arr)]) let s = new Set(); s.add([1]); s.add([1]); console.log(s); console.log(s.size);
3.Map集合
Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。Map 可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
let obj={ name:"zhangsan", age:12 } //遍历键值对组成的数组 let arr=Object.entries(obj); console.log(arr); //将数组作为参数放到Map中 let map=new Map(arr) console.log(map); // 添加元素 map.set('1','1'); console.log(map); // 删除元素 map.delete('name'); console.log(map); // 获取元素 console.log(map.get('age')); // 遍历 let keys=map.keys() let values=map.values() let entries=map.entries(); console.log(keys,values,entries); map.forEach((value,key)=>{ console.log(value,key) })
Map |
Object | |
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 | 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。 |
键的类型 | 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。 | 一个Object 的键必须是一个 String 或是Symbol。 |
键的顺序 | Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 一个 Object 的键是无序的。注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。 |
Size | Map 的键值对个数可以轻易地通过size 属性获取 | Object 的键值对个数只能手动计算 |
迭代 | Map 是 iterable 的,所以可以直接被迭代。 | 迭代一个Object需要以某种方式获取它的键然后才能迭代。 |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
4.数值扩展
Number.isFinite(), Number.isNaN()
与isFinite、isNaN不同,这两个新方法只对数值有效,Number.isFinite()
对于非数值一律返回false
, Number.isNaN()
只有对于NaN
才返回true
,非NaN
一律返回false
。
Number.isFinite(0.8); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Number.isNaN(NaN) // true Number.isNaN(15) // false
Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()
和parseFloat()
,移植到Number
对象上面,行为完全保持不变。
Number.parseInt('12.34') // 12 Number.parseFloat('123.45#') // 123.45
Number.isInteger()
Number.isInteger()
用来判断一个数值是否为整数。
Number.isInteger(25) // true Number.isInteger(25.1) // false
async异步函数
async函数是使用async关键字声明的函数。 async函数是AsyncFunction构造函数的实例, 并且其中允许使用await关键字。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
await关键字只在async函数内有效。如果你在async函数体之外使用它,就会抛出语法错误 SyntaxError 。async/await的目的为了简化使用基于promise的API时所需的语法。async/await的行为就好像搭配使用了生成器和promise。async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
简单来说 是一个函数,是一个异步编程解决方案,内部封装了generator函数,是一个语法糖,内部自带执行器,与await配合使用;异步编程,同步处理;
async function loadArticle() { try { let res = await axios.get('http://122.199.0.35:8888/index/category/findAll') console.log(res.data.data); } catch(error) { throw new Error('请求失败'); } finally { console.log('最终执行'); } } loadArticle()
forEach、for in、for of三者区别 forEach更多的用来遍历数组 for in 一般常用来遍历对象或数组 for of遍历实现了迭代器接口的对象,遍历对象需要通过Object.keys()