本文共 12921 字,大约阅读时间需要 43 分钟。
ES6 提供了新的数据结构Set(集合)。类似于数组,但Set 中元素值都是唯一的,Set
对象允许你存储任何类型的唯一值,无论是或者是对象引用。
集合实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
size 返回集合的元素个数
add 增加一个新元素,返回当前集合
delete 删除元素,返回boolean 值
has 检测集合中是否包含某个元素,返回boolean 值
clear 清空集合,返回undefined
Set相关属性和方法的使用
let mySet = new Set();mySet.add(1); // Set [ 1 ]mySet.add(5); // Set [ 1, 5 ]mySet.add(5); // 自动去重:Set [ 1, 5 ]mySet.add("some text"); // Set [ 1, 5, "some text" ]let o = { a: 1, b: 2};mySet.add(o);mySet.add({ a: 1, b: 2}); // o 指向的是不同的对象,所以没问题mySet.size; // 5mySet.has(1); // truemySet.has(3); // falsemySet.has(5); // truemySet.has(Math.sqrt(25)); // truemySet.has("Some Text".toLowerCase()); // truemySet.has(o); // truemySet.delete(5); // true, 从set中移除5mySet.has(5); // false, 5已经被移除mySet.size; // 4, 刚刚移除一个值console.log(mySet);// logs Set(4) [ 1, "some text", {…}, {…} ] in Firefox// logs Set(4) { 1, "some text", {…}, {…} } in Chrome
迭代Set
// 迭代整个set// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}for (let item of mySet) console.log(item);// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}for (let item of mySet.keys()) console.log(item);// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}for (let item of mySet.values()) console.log(item);// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}//(键与值相等)for (let [key, value] of mySet.entries()) console.log(key);// 用forEach迭代mySet.forEach(function(value) { console.log(value);});
Set 和 Array互换
let myArray = ["value1", "value2", "value3"];// 用Set构造器将Array转换为Setlet mySet = new Set(myArray);mySet.has("value1"); // returns true// 用...(展开操作符)操作符将Set转换为Array[...mySet]; // 与myArray完全一致// 使用 Array.from 转换Set为Arrayvar myArr = Array.from(mySet); //数组去重const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]console.log([...new Set(numbers)])// [2, 3, 4, 5, 6, 7, 32]
字符串相关
let text = 'India';let mySet = new Set(text); // Set {'I', 'n', 'd', 'i', 'a'}mySet.size; // 5// 大小写敏感 & duplicate ommisionnew Set("Firefox") // Set(7) [ "F", "i", "r", "e", "f", "o", "x" ]new Set("firefox") // Set(6) [ "f", "i", "r", "e", "o", "x" ]
ES6 提供了Map 数据结构。它类似于对象,也是键值对的集合,并且能够记住键的原始插入顺序。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
1. Objects 和 maps 的比较
和 Maps
类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此过去我们一直都把对象当成 Maps
使用。不过 Maps
和 Objects
有一些重要的区别,在下列情况里使用 Map
会是更好的选择
。。。。 | Map | Object |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 | 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。注意: 虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 | 一个Object 的键必须是一个 或是。 |
键的顺序 | Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 一个 Object 的键是无序的注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。 |
Size | Map 的键值对个数可以轻易地通过 属性获取 | Object 的键值对个数只能手动计算 |
迭代 | Map 是 的,所以可以直接被迭代。 | 迭代一个Object 需要以某种方式获取它的键然后才能迭代。 |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
2. Map 的属性和方法
size 返回Map 的元素个数
set 增加一个新元素,返回当前Map
get 返回键名对象的键值
delete 如果 Map
对象中存在该元素,则移除它并返回 true
;否则如果该元素不存在则返回 false
。
has 检测Map 中是否包含某个元素,返回boolean 值
clear 清空集合,返回undefined
使用Map对象
let myMap = new Map();let keyObj = { };let keyFunc = function() { };let keyString = 'a string';// 添加键myMap.set(keyString, "和键'a string'关联的值");myMap.set(keyObj, "和键keyObj关联的值");myMap.set(keyFunc, "和键keyFunc关联的值");myMap.size; // 3// 读取值myMap.get(keyString); // "和键'a string'关联的值"myMap.get(keyObj); // "和键keyObj关联的值"myMap.get(keyFunc); // "和键keyFunc关联的值"myMap.get('a string'); // "和键'a string'关联的值" // 因为keyString === 'a string'myMap.get({ }); // undefined, 因为keyObj !== {}myMap.get(function() { }); // undefined, 因为keyFunc !== function () {}
迭代Map
// 使用for..of循环来实现迭代:let myMap = new Map();myMap.set(0, "zero");myMap.set(1, "one");for (let [key, value] of myMap) { console.log(key + " = " + value); // 将会显示两个log。一个是"0 = zero"另一个是"1 = one"}for (let key of myMap.keys()) { console.log(key); // 将会显示两个log。 一个是 "0" 另一个是 "1"}for (let value of myMap.values()) { console.log(value); // 将会显示两个log。 一个是 "zero" 另一个是 "one"}//通过forEach()方法迭代:myMap.forEach(function(value, key) { console.log(key + " = " + value);})// 将会显示两个logs。 一个是 "0 = zero" 另一个是 "1 = one"
复制或合并Maps
//Map 能像数组一样被复制:let original = new Map([ [1, 'one']]);let clone = new Map(original);console.log(clone.get(1)); // oneconsole.log(original === clone); // false. 浅比较 不为同一个对象的引用//Map对象间可以进行合并,但是会保持键的唯一性。let first = new Map([ [1, 'one'], [2, 'two'], [3, 'three'],]);let second = new Map([ [1, 'uno'], [2, 'dos']]);// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。// 展开运算符本质上是将Map对象转换成数组。let merged = new Map([...first, ...second]);console.log(merged.get(1)); // unoconsole.log(merged.get(2)); // dosconsole.log(merged.get(3)); // three
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。class 的本质是 function。类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
// 匿名类let Example = class { constructor(a) { this.a = a; }}// 命名类let Example = class Example { constructor(a) { this.a = a; }}//类的声明class Example { constructor(a) { this.a = a; }}
注意要点
不可重复声明。
类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
类中方法不需要 function 关键字。
方法间不能加分号。
1. prototype:ES6 中,prototype 仍旧存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。 覆盖方法 / 初始化时添加方法
Example.prototype={ //methods}//添加方法Object.assign(Example.prototype,{ //methods})
2. 静态属性:class 本身的属性,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。
class Example { // 新提案 static a = 2;}// 目前可行写法Example.b = 2;
3. 公共属性
class Example{ }Example.prototype.a = 2;
4. 实例属性:定义在实例对象( this )上的属性。
//实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。class Example { a = 2; constructor () { this._count = 0; console.log(this.a); }}//上面代码中,a 就是Example的实例属性。在Example的实例上,可以读取这个属性。
5. name 属性:返回跟在 class 后的类名(存在时)。
let Example = class Exam { constructor(a) { this.a = a; }}console.log(Example.name); // Exam let Example=class { constructor(a) { this.a = a; }}console.log(Example.name); // Example
类的所有方法都定义在类的prototype
属性上面。
在类的实例上面调用方法,其实就是调用原型上的方法。b
是B
类的实例,它的constructor
方法就是B
类原型的constructor
方法。
class B { }let b = new B();b.constructor === B.prototype.constructor // true
类的所有实例共享一个原型对象。p1
和p2
都是Point
的实例,它们的原型都是Point.prototype
,所以__proto__
属性是相等的。
var p1 = new Point(2,3);var p2 = new Point(3,2); p1.__proto__ === p2.__proto__ //true
1. constructor 方法:constructor 方法是类的默认方法,创建类的实例化对象时被调用。
class Example{ constructor(){ console.log('我是constructor'); }}new Example(); // 我是constructor
2. 静态方法:该方法不会实例被继承,而是直接通过类来调用
静态方法只能在当前类上调用,不能被该类的实例对象调用。父类的静态方法可以被子类继承。
因此静态方法被调用的方式一共有三种(三种调用方式都在下面一段代码中使用到了,请耐心阅读):
class Foo { static classMethod() { return 'hello'; }}//父类直接调用Foo.classMethod(); //hello//子类继承父类后调用class Bar extends Foo { }Bar.classMethod(); //hell//子类通过super对象调用class Cla extends Foo { return super.classMethod(); //hello}
3. 原型方法
class Example { sum(a, b) { console.log(a + b); }}let exam = new Example();exam.sum(1, 2); // 3
4. 实例方法
class Example { constructor() { this.sum = (a, b) => { console.log(a + b); } }}
new:class 的实例化必须通过 new 关键字。
lass Example { constructor(a, b) { this.a = a; this.b = b; console.log('Example'); } sum() { return this.a + this.b; }}//共享原型对象let exam1 = new Example(2, 1);let exam2 = new Example(3, 1);console.log(exam1._proto_ == exam2._proto_); // true exam1._proto_.sub = function() { return this.a - this.b;}console.log(exam1.sub()); // 1console.log(exam2.sub()); // 2
通过 extends 实现类的继承。
class Calculator { constructor(a,b){ this.a = a; this.b = b; } add(a,b){ return ("相加結果:"+ (a+b)) }}class Son extends Calculator{ add(a,b){ console.log(super.add(a,b)) //super.add(a,b)就是调用父类中的普通函数 }}var rt = new Son()rt.add(3, 4)
子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
class Father { constructor() { }}class Child extends Father { constructor() { } // or // constructor(a) { // this.a = a; // super(); // }}let test = new Child(); // Uncaught ReferenceError: Must call super // constructor in derived class before accessing 'this' or returning // from derived constructor
调用父类构造函数,只能出现在子类的构造函数。
class Father { test(){ return 0; } static test1(){ return 1; }}class Child extends Father { constructor(){ super(); }}class Child1 extends Father { test2() { super(); // Uncaught SyntaxError: 'super' keyword unexpected // here }}
调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类
class Child2 extends Father { constructor(){ super(); // 调用父类普通方法 console.log(super.test()); // 0 } static test3(){ // 调用父类静态方法 return super.test1+2; }}Child2.test3(); // 3
总结:
ES5中:利用借用构造函数实现实例属性和方法的继承 ; 利用原型链或者寄生式继承实现 共享的原型属性和方法的继承 。
ES6中:
利用class定义类,extends实现类的继承;
子类constructor里调用super()(父类构造函数)实现 实例属性和方法的继承;
子类原型继承父类原型,实现原型对象上方法的继承。
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。
模块功能主要由两个命令构成:export 和 import。
ES6 的模块自动开启严格模式,不管你有没有在模块头部加上 use strict;。
模块中可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,类等。
每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。
每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,直接从内存中读取。
模块导入导出各种类型的变量,如字符串,数值,函数,类。
/*-----export [test.js]-----*/let myName = "Tom";let myAge = 20;let myfn = function(){ return "My name is" + myName + "! I'm '" + myAge + "years old."}let myClass = class myClass { static a = "yeah!";}export { myName, myAge, myfn, myClass } /*-----import [xxx.js]-----*/import { myName, myAge, myfn, myClass } from "./test.js";console.log(myfn());// My name is Tom! I'm 20 years old.console.log(myAge);// 20console.log(myName);// Tomconsole.log(myClass.a );// yeah!
建议使用大括号指定所要输出的一组变量写在文档尾部,明确导出的接口。
函数与类都需要有对应的名称,导出文档尾部也避免了无对应名称。
只读属性:不允许在加载模块的脚本里面,改写接口的引用指向,即可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
import { a} from "./xxx.js"a = { }; // error import { a} from "./xxx.js"a.foo = "hello"; // a = { foo : 'hello' }
单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 。
import { a } "./xxx.js";import { a } "./xxx.js";// 相当于 import { a } "./xxx.js"; import { a } from "./xxx.js";import { b } from "./xxx.js";// 相当于 import { a, b } from "./xxx.js";
静态执行特性:import 是静态执行,所以不能使用表达式和变量。
import { "f" + "oo" } from "methods";// errorlet module = "methods";import { foo } from module;// errorif (true) { import { foo } from "method1";} else { import { foo } from "method2";}// error
export 命令导出的接口名称,须和模块内部的变量有一一对应关系。
导入的变量名,须和导出的接口名称相同,即顺序可以不一致。
使用 as 重新定义导出的接口名称,隐藏模块内部的变量/*-----export [test.js]-----*/let myName = "Tom";export { myName as exportName } /*-----import [xxx.js]-----*/import { exportName } from "./test.js";console.log(exportName);// Tom//不同模块导出接口名称命名重复, 使用 as 重新定义变量名。/*-----export [test1.js]-----*/let myName = "Tom";export { myName }/*-----export [test2.js]-----*/let myName = "Jerry";export { myName }/*-----import [xxx.js]-----*/import { myName as name1 } from "./test1.js";import { myName as name2 } from "./test2.js";console.log(name1);// Tomconsole.log(name2);// Jerry
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b 和0o 表示。
console.log(0b1111);//15console.log(0o10);//16
Number.isFinite() 用来检查一个数值是否为有限的
console.log(Number.isFinite(15));//trueconsole.log(Number.isFinite(NaN));//falseconsole.log(Number.isFinite(10/0));//false
Number.isNaN() 用来检查一个值是否为NaN
console.log(Number.isNaN(NaN));//trueconsole.log(Number.isNaN(5));//true
Number.isInteger() 判断一个数是不是整数
console.log(Number.isInteger(25));//trueconsole.log(Number.isInteger(25.0));//trueconsole.log(Number.isInteger(25.1));//false
Number.parseInt 转为整数 Number.parseFloat 转为带小数的(字符串转为数字)
console.log(Number.parseInt('5211314love')); //5211314console.log(Number.parseFloat('3.1415926hh')); //3.1415926
**Math.trunc()**用于去除一个数的小数部分,返回整数部分。
console.log(Math.trunc(4.1));//4console.log(Math.trunc(-4.1));//-4
Math.sign 判断一个数是正数还是负数还是0
console.log(Math.sign(100)); //1console.log(Math.sign(0)); // 0console.log(Math.sign(-100)); //-1
ES6 新增了一些Object 对象的方法
Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与NaN)
Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
proto、setPrototypeOf、setPrototypeOf 可以直接设置对象的原型
转载地址:http://bkqgn.baihongyu.com/