ES6 全称 ECMAScript 6.0 ,2015.06 发布。
本教程内容如下:

let 只在代码块内有效,var 在全局范围内有效;
let 只能声明一次,var 可以多次声明;
let 不存在变量提升,var 会变量提升;
xxxxxxxxxx51console.log(a); //ReferenceError: a is not defined2let a = "apple";34console.log(b); //undefined5var b = "banana";let 和 const 都会存在;
xxxxxxxxxx51var PI = "a";2if(true){3 console.log(PI); //ReferenceError: PI is not defined4 const PI = "3.1415926";5}ES6 明确规定,代码块内如果存在 let 或者 const ,代码块会对这些声明命令的变量从 块的开始 就形成一个封闭作用域。在代码块内,在声明变量 PI 之前使用它就会报错。
在解构中,有下面两部分参与:
基本
xxxxxxxxxx41let [a, b, c] = [1, 2, 3];2// a = 13// b = 24// c = 3可嵌套
xxxxxxxxxx41let [a, [[b], c]] = [1, [[2], 3]];2// a = 13// b = 24// c = 3可忽略
xxxxxxxxxx31let [a, , b] = [1, 2, 3];2// a = 13// b = 3不完全解构
xxxxxxxxxx31let [a = 1, b] = [];2// a = 13// b = undefined剩余运算符
xxxxxxxxxx31let [a, b] = [1, 2, 3];2// a = 13// b = [2, 3]字符串等
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据;
xxxxxxxxxx61let [a, b, c, d, e] = 'hello';2// a = 'h'3// b = 'e'4// c = 'l'5// d = 'l'6// e = 'o'解构默认值
xxxxxxxxxx21let [a = 2] = [undefined];2// a = 2当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果;
xxxxxxxxxx61let [a = 3, b = a] = [];2// a = 3, b = 33let [a = 3, b = a] = [1];4// a = 1, b = 15let [a = 3, b = a] = [1, 2]6// a = 1, b = 2基本
xxxxxxxxxx61let { foo, bar } = { foo: 'aaa', bar : 'bbb' };2// foo = 'aaa'3// bar = 'bbb'45let { baz : foo } = { baz : 'ddd'};6// foo = 'ddd'可嵌套可忽略
xxxxxxxxxx81let obj = { p: ['hello', { y: 'world'}] };2let { p: [x, { y }] } = obj;3// x = 'hello'4// y = 'world'56let obj = { p: ['hello', { y : 'world' }] };7let { p: [x, { }] } = obj;8// x = 'hello'不完全解构
xxxxxxxxxx41let obj = { p: [{y: 'world'}] };2let { p: [{ y }, x]} = obj;3// x = undefined4// y = 'world'剩余运算符
xxxxxxxxxx41let {a, b, rest} = {a: 10, b: 20, c: 30, d: 40};2// a = 103// b = 204// rest = {c: 30, d: 40}解构默认值
xxxxxxxxxx41let {a = 10, b = 5} = {a: 3};2// a = 3; b = 53let {a: aa = 10,b: bb = 5} = {a: 3}4// aa = 3; bb = 5Symbol 函数栈不能使用 new 命令,因为 Symbol 是原始数据类型,不是对象。
可以接收一个 字符串 作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分;
字符串相同的 Symbol ,值不相同;
xxxxxxxxxx71let sy = Symbol("kk");2console.log(sy); // Symbol(kk)3typeof(sy); // "symbol"45// 参数相同的 Symbol() 返回的值不相等6let sy1 = Symbol("kk");7console.log(sy === sy1); // false由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名;
xxxxxxxxxx171let sy = Symbol("key1");23// 写法14let syObject = {};5syObject[sy] = "kk";6console.log(syObject); // {Symbol(key1): "kk"}78// 写法29let syObject = {10 [sy]: "kk"11};12console.log(syObject); // {Symbol(key1): "kk"}1314// 写法315let syObject = {};16Object.defineProperty(syObject, sy, {value: "kk"});17console.log(syObject); // {Symbol(key1): "kk"}Symbol 作为对象属性名时不能用 . 运算符,要用方括号。因为 . 运算符后面是字符串,所以取到的是 字符串 值 sy 属性,而不是 Symbol 值 sy 属性;
xxxxxxxxxx51let syObject = {};2syObject[sy] = "kk";34syObject[sy]; // "kk"5syObject.sy; // undefinedSymbol 值作为属性名时,该属性是公有属性,不是私有属性,可以在类的外部访问;
不会出现在 for … in ,for … of 的循环中;
也不会被 Object.keys() ,Object.getOwnPropertyNames() 返回;
如果要读取一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到;
xxxxxxxxxx111let syObject = {};2syObject[sy] = "kk";3console.log(syObject); // {Symbol(key1): "kk"}45for (let i in syObject){6 console.log(i); // 无输出7}89Object.keys(syObject); // []10Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]11Reflect.ownKeys(syObject); // [Symbol(key1)]在 ES5 中用字符串表示常量,但是字符串不能保证是唯一的,这会引起一些问题:
xxxxxxxxxx191const COLOR_RED = "red";2const COLOR_YELLOW = "yellow";3const COLOR_BLUE = "blue";4const MY_BLUE = "blue";56function getConstantName(color) {7 switch (color) {8 case COLOR_RED : 9 return "COLOR_RED";10 case COLOR_YELLOW:11 return "COLOR_YELLOW";12 case COLOR_BLUE:13 return "COLOR_BLUE";14 case MY_BLUE:15 return "MY_BLUE";16 default:17 throw new Exception("Can't find this color");18 }19}如果使用 Symbol 定义常量,就可以保证这一组常量的值都不相等。用 Symbol 来修改上面的例子;
xxxxxxxxxx191const COLOR_RED = Symbol("red");2const COLOR_RED = Symbol("yellow");3const COLOR_BLUE = Symbol("blue");4const MY_BLUE = Symbol("blue");56function getConstantName(color) {7 switch (color) {8 case COLOR_RED : 9 return "COLOR_RED";10 case COLOR_YELLOW:11 return "COLOR_YELLOW";12 case COLOR_BLUE:13 return "COLOR_BLUE";14 case MY_BLUE:15 return "MY_BLUE";16 default:17 throw new Exception("Can't find this color");18 }19}Symbol.for() 类似单例模式;
首先会在全局搜索被 登记 的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值;若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并 登记 在全局环境中供搜索;
xxxxxxxxxx61let yellow = Symbol("Yellow");2let yellow1 = Symbol.for("Yellow");3console.log(yellow === yellow1); // false45let yellow2 = Symbol.for("Yellow");6console.log(yellow1 === yellow2); // trueSymbol.keyFor() 返回一个 已登记 的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被 登记 ;
xxxxxxxxxx51let yellow = Symbol("Yellow");2console.log(Symbol.keyFor(yellow)); // undefined34let yellow1 = Symbol.for("Yellow");5console.log(Symbol.keyFor(yellow1)); // "Yellow"key 是字符串
xxxxxxxxxx81var myMap = new Map();2var keyString = "a string";34myMap.set(keyString, "和键'a string'关联的值");56myMap.get(keyString); // "和键'a string'关联的值"7myMap.get("a string"); // "和键'a string'关联的值"8 // 因为 keyString === 'a string'key 是对象
xxxxxxxxxx71var myMap = new Map();2var keyObj = {};34myMap.set(keyObj, "和键 keyObj 关联的值");56myMap.get(keyObj); // "和键 keyObj 关联的值"7myMap.get({}); // undefined, 因为 keyObj !== {}key 是函数
xxxxxxxxxx71var myMap = new Map();2var keyFunc = function(){};34myMap.set(keyFunc, "和键 keyFunc 关联的值");56myMap.get(keyFunc); // "和键 keyFunc 关联的值"7myMap.get(function() {}); // undefined,因为 keyFunc !== function() {}key 是NaN
xxxxxxxxxx81var myMap = new Map();2myMap.set(NaN, "not a number");34myMap.get(NaN); // "not a number"56var otherNuN = Number("foo");7myMap.get(otherNaN); // "not a number"8 // 虽然 NaN 和任何值甚至它自己都不相等(NaN !== NaN 返回 true),但 NaN 作为 Map 的键来说是没有区别的对 Map 进行遍历,以下两个最高级;
for … of
xxxxxxxxxx241var myMap = new Map();2myMap.set(0, "zero");3myMap.set(1, "one");45// 将会显示两个 log。一个是 "0 = zero",另一个是 "1 = one"6for (var [key, value] of myMap) {7 console.log(key + " = " + value);8}9for (var [key, value] of myMap.entries()) {10 console.log(key + " = " + value);11}12/* 这个 entries() 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组 */1314// 将会显示两个 log。一个是 "0",另一个是 "1"15for (var key of myMap.keys()) {16 console.log(key);17}18/* 这个 keys() 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的键 */1920// 将会显示两个 log。一个是 "zero",另一个是 "one"21for (var value of myMap.values()) {22 console.log(value);23}24/* 这个 values() 方法返回一个 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */forEach()
xxxxxxxxxx81var myMap = new Map();2myMap.set(0, "zero");3myMap.set(1, "one");45// 将会显示两个 log。一个是 "0 = zero",另一个是 "1 = one"6myMap.forEach(function(value, key){7 console.log(key + " = " + value);8}, myMap)Map 与 Array 的转换
xxxxxxxxxx71var kvArray = [["key1", "value1"], ["key2", "value2"]];23// Map 构造函数可以将一个 二维键值对数组 转换成一个 Map 对象4var myMap = new Map(kvArray);56// Array.from 函数可以将一个 Map 对象转换成一个 二维键值对数组7var outArray = Array.from(myMap);Map 的克隆
xxxxxxxxxx51var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);2var myMap2 = new Map(myMap1);34console.log(myMap1 === myMap2);5// false. Map 对象构造函数生成的实例,迭代出新的对象Map 的合并
xxxxxxxxxx61var first = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);2var second = new Map([[1, 'uno'], [2, 'dos']]);34var merged = new Map([first, second]);5// 合并两个 Map 对象时,如果有重复的键值,后面的会覆盖前面的。6// 所以,merged 后的值是 uno, dos, threeSet 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
+0 和 -0 在存储判断唯一性的时候是恒等的,所以不重复;
undefined 与 undefined 是恒等的,所以不重复;
NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,所以不重复;
xxxxxxxxxx131let mySet = new Set();23mySet.add(1); // Set(1) {1}4mySet.add(5); // Set(2) {1, 5}5mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性6mySet.add(-0); // Set(3) {1, 5, 0} 这里体现了值的 FIFO 原则7mySet.add(+0); // Set(3) {1, 5, 0}89mySet.add("some text"); // Set(4) {1, 5, 0, "some text"} 这里体现了类型的多样性1011var o = {a: 1, b: 2};12mySet.add(o); // Set(5) {1, 5, 0, "some text", {...}}13mySet.add({a: 1, b: 2}); // Set(6) {1, 5, 0, "some text", {...}, {...}} 这里体现了对象之间引用不同于不恒等,即使值相同,Set 也能存储Array
xxxxxxxxxx121// Array ==> Set2var mySet = new Set(["value1", "value2", "value3"]);34// Set ==> Array,用 ...操作符5var myArray = [mySet];67// String ==> Set8var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}9// Set ==> String10// 可以 Set ==> Array ==> String (.toString 或 join 都可以)11// .toString 是不能将 Set 转换成 String12数组去重
xxxxxxxxxx21var mySet = new Set([1, 2, 3, 4, 4]);2[mySet]; // [1, 2, 3, 4]并集
xxxxxxxxxx31var a = new Set([1, 2, 3]);2var b = new Set([4, 3, 2]);3var union = new Set([a, b]); // {1, 2, 3, 4}交集
xxxxxxxxxx31var a = new Set([1, 2, 3]);2var b = new Set([4, 3, 2]);3var insersect = new Set([a].filter(x => b.has(x))); // {2, 3}差集
xxxxxxxxxx31var a = new Set([1, 2, 3]);2var b = new Set([4, 3, 2]);3var difference = new Set([a].filter(x => !b.has(x))); // {1}一个 Proxy 对象由两个部分组成:target 和 handler ;
在通过 Proxy 构造函数生成对象实例时,需要提供这两个参数;
target 即目标对象,handler 是一个对象,声明了代理 target 的指定行为;
xxxxxxxxxx411let target = {2 name: 'Tom',3 age: 244}5let handler = {6 get: function(target, key) {7 console.log('getting ' + key);8 return target[key]; // 不是target.key9 },10 set: function(target, key, value) {11 console.log('setting ' + key);12 target[key] = value;13 }14}1516let proxy = new Proxy(target, handler);17proxy.name; // 实际执行 handler.get18proxy.age = 25; // 实际执行 handler.set19// getting name20// setting age21// 252223// target 可以为空对象24let targetEpt = {};25let proxyEpt = new Proxy(targetEpt, handler);26// 调用 get 方法,此时目标对象为空,没有 name 属性27proxyEpt.name;28// 调用 set 方法,向目标对象中添加了 name 属性29proxyEpt.name = 'Tom';30// setting name31// "Tom"32// 再次调用 get ,此时已经存在 name 属性了33proxyEpt.name;34// getting name35// "Tom"3637// handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象38let targetEmpty = {};39let proxyEmpty = new Proxy(targetEmpty, {});40proxyEmpty.name = "Tom";41targetEmpty) // {name: "Tom"}详情参考 ES6 Reflect ;
静态方法
xxxxxxxxxx381// 查找并返回 target 对象的 name 属性2Reflect.get(target, name, receiver);34// 将 target 的 name 属性设置为 value。返回值为 boolean,true 表示修改成功,false 表示修改失败。当 target 为不存在的对象时,会报错5Reflect.set(target, name, value, receiver);67// 是 name in obj 指令的函数化,用于查找 name 属性在 obj 中是否存在。返回值为 boolean。如果 obj 不是对象则会报错 TypeError8Reflect.has(obj, name)910// 是 delete obj[property] 的函数化,用于删除 obj 对象的 property 属性。返回值为 boolean。如果 obj 不是对象则会报错 TypeError11Reflect.deleteProperty(obj, property)1213// 等同于 new target(...args)14Reflect.construct(obj, args);1516// 用于读取 obj 的 _proto_ 属性。在 obj 不是对象时会报错。17Reflect.getPrototypeOf(obj)1819// 用于设置目标对象的 prototype20Reflect.setPrototypeOf(obj, newProto);2122// 等同于 Function.prototype.apply.call(func, thisArg, args).func 表示目标函数;thisArg 表示目标函数绑定的 this 对象;args 表示目标函数调用时传入的参数列表,可以是数组或类似数组的对象。若目标函数无法调用,会抛出 TypeError23Reflect.apply(func, thisArg, args);2425// 用于为目标对象定义属性。如果 target 不是对象,会抛出异常26Reflect.defineProperty(target, propertyKey, attributes)2728// 用于得到 target 对象的 propertyKey 属性的描述。如果 target 不是对象,会抛出错误29Reflect.getOwnPropertyDescriptor(target, propertyKey)3031// 用于判断 target 对象是否可扩展。返回值为 boolean。如果 target 参数不是对象,会抛出异常;32Reflect.isExtensible(target)3334// 用于让 target 对象变为不可扩展。如果 target 参数不是对象,会抛出异常35Reflect.preventExtensions(target)3637// 用于返回 target 对象的所有属性,等同于 Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 之和38Reflect.ownKeys(target)Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作;
xxxxxxxxxx211let exam = {2 name: "Tom",3 age: 244}5let handler = {6 get: function(target, key) {7 console.log("getting" + key);8 return Reflect.get(target, key);9 },10 set: function(target, key, value) {11 console.log("setting" + key + " to " + value);12 Reflect.set(target, key, value);13 }14}1516let proxy = new Proxy(target, handler);17proxy.name = "Jerry";18proxy.name;19// setting name to Jerry20// getting name21// "Jerry"ES6 之前判断字符串是否包含子串,是用 indexOf 方法,ES6 新增了子串的识别方法。
includes() :返回布尔值,判断是否找到参数字符串;
startsWith() :返回布尔值,判断参数字符串是否是在原字符串的头部;
endsWith() :返回布尔值,判断参数字符串是否是在原字符串的尾部;
以上三个方法都可以接受两个参数 - 需要搜索的字符串,和 可选的搜索起始位置索引;
xxxxxxxxxx51let string = "apple,banana,orange";2string.includes("banana"); // true3string.startsWith("apple"); // true4string.endsWith("apple"); // false5string.startsWith("banana",6); // true注意点 :
repeat() :返回新的字符串,表示将字符串重复指定次数返回;
xxxxxxxxxx11console.log("hello,".repeat(2)); // "hello,hello,"如果参数是小数,向下取整;
xxxxxxxxxx11console.log("hello,".repeat(3.2)); // "hello,hello,hello,"如果参数是 0 至 -1 之间的小数,会进行取整运算,等同于 0 ;
xxxxxxxxxx11console.log("hello,".repeat(-0.5)); // ""如果参数是 NaN ,等同于 0 ;
xxxxxxxxxx11console.log("hello,".repeat(NaN)); // ""如果参数是负数或者 Infinity ,会报错;
xxxxxxxxxx21console.log("hello,".repeat(-1)); // RangeError: Invalid count value2console.log("hello,".repeat(Infinity)); // RangeError: Invalid count value如果参数的参数是字符串,会先将字符串转化为数字;
xxxxxxxxxx21console.log("hello,".repeat("hh")); // ""2console.log("hello,".repeat("2")); // "hello,hello,"padStart :返回新的字符串,表示用参数字符串从 头部 补全原字符串;
padEnd :返回新的字符串,表示用参数字符串从 尾部 补全原字符串;
以上两个方法接受两个参数 - 生成字符串的最小长度,和 可选的用来补全的字符串。如果没有第二个参数,默认用空格填充;
xxxxxxxxxx31console.log("h".padStart(5,"0")); // "ooooh"2console.log("h".padEnd(5,"0")); // "hoooo"3console.log("h".padStart(5)); // " h"如果指定的长度小于或等于原字符串的长度,则返回原字符串;
xxxxxxxxxx31console.log("hello".padStart(3,"A")); // "hello"2console.log("hello".padStart(5,"A")); // "hello"3console.log("hello".padStart(10,"A")); // "AAAAAhello"如果原字符串加上补全字符串的长度大于指定长度,则截去超出位数的补全字符串;
xxxxxxxxxx11console.log("hello".padStart(10,",world!")); // "hello,worl"常用于补全位数;
xxxxxxxxxx11console.log("123".padStart(10,"0")); // "0000000123"普通字符串
xxxxxxxxxx41let string = `Hello'\n'world`;2console.log(string);3// "Hello"4// "world"多行字符串
xxxxxxxxxx51let string1 = `Hey,2can you stop angry now?`;3console.log(string1);4// Hey,5// can you stop angry now?字符串插入变量和表达式
变量名写在 ${} 中,${} 中也可以放入 JavaScript 表达式;
xxxxxxxxxx51let name = "Mike";2let age = 27;3let info = `My Name is ${name}, I am ${age+1} years old next year.`;4console.log(info);5// My Name is Mike, I am 28 years old next year.字符串中调用函数
xxxxxxxxxx61function f() {2 return "have fun!";3}4let string2 = `Game start,${f()}`;5console.log(string2);6// Game start,have fun!模板字符串中的换行和空格都是会被保留的
xxxxxxxxxx111let innerHtml = `<ul>2 <li>menu</li>3 <li>mine</li>4</ul>5`;6console.log(innerHtml);7// 输出8<ul>9 <li>menu</li>10 <li>mine</li>11</ul>模板标签,是一个函数的调用,调用的参数是模板字符串;
xxxxxxxxxx31alert`Hello world!`;2// 等价于3alert('Hello world!');当模板字符串中带有变量,会将模板字符串参数处理成多个参数;
xxxxxxxxxx181function f(stringArr,values) {2 let result = "";3 for (let i=0,i<stringArr.length;i++) {4 result += stringArr[i];5 if (values[i]) {6 result += values[i];7 }8 }9 return result;10}11let name = "Mike";12let age = 27;13f`My Name is ${name},I am ${age+1} years old next year.`;14// "My Name is Mike,I am 28 years old next year."1516f`My Name is ${name},I am ${age+1} years old next year.`;17// 等价于18f(['My Name is',',I am',' years old next year.'],'Mike',28);过滤 HTML 字符串,防止用于输入恶意内容
xxxxxxxxxx141function f(stringArr,values) {2 var result = "";3 for (let i=0,i<stringArr.length;i++) {4 result += stringArr[i];5 if (values[i]) {6 result += String(values[i]).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");7 }8 }9 return result;10}1112var name = '<Amy&Mike>';13f`<p>Hi, ${name}. I would like send you some message.</p>`;14// <p>Hi, <Amy&Mike>. I would like send you some message.</p>国际化处理(转化多国语言)
xxxxxxxxxx21i18n`Hello ${name}, you are visitor number ${visitorNumber}`;2// 你好**,你是第**位访问者二进制表示法新写法:前缀 0b 或 0B ;
xxxxxxxxxx21console.log(0b11 === 3); // true2console.log(0B11 === 3); // true八进制表示法新写法:前缀 0o 或 0O ;
xxxxxxxxxx21console.log(0o11 === 9); // true2console.log(0O11 === 9); // trueNumber.EPSILON ;
表示 1 与 大于 1 的最小浮点数之间的差;
它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。
可用于测试数值是否在误差范围内
xxxxxxxxxx310.1 + 0.2 === 0.3; // false2// 在误差范围内 即视为相等3equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true属性特性
xxxxxxxxxx31writeable: false2enumerable: false3configurable: false安全整数
安全整数表示在 JavaScript 中能够精确表示的整数。安全整数的范围在 2 的 -53 次方到 2 的 53 次方之间(不包括两个端点)。超过这个范围的整数无法精确表示;
最大安全整数
安全整数范围的上限,即 2 的 53 次方减 1;
xxxxxxxxxx41Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1; // true2Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // true3Number.MAX_SAFE_INTEGER === Number.MAX_SAFE_INTEGER + 1; // false4Number.MAX_SAFE_INTEGER -1 === Number.MAX_SAFE_INTEGER - 2; // false最小安全整数
安全整数范围的下限,即 2 的 53 次方减 1 的负数
xxxxxxxxxx41Number.MIN_SAFE_INTEGER === -(Math.pow(2, 53) - 1); // true2Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2; // true3Number.MIN_SAFE_INTEGER === Number.MIN_SAFE_INTEGER - 1; // false4Number.MIN_SAFE_INTEGER + 1 === Number.MIN_SAFE_INTEGER + 2; // false属性特性
xxxxxxxxxx31wirteable: false2enumerable: false3configurable: falseNumber 对象新方法
xxxxxxxxxx151// 用于检查一个数值是否为有限的(finite),即不是 Infinity2Number.isFinite();34console.log(Number.isFinite(1)); // true5console.log(Number.isFinite(0.1)); // true67// NaN 不是有限的8console.log(Number.isFinite(NaN)); // false910console.log(Number.isFinite(Infinity)); // false11console.log(Number.isFinite(-Infinity));// false12// Number.isFinite 没有隐式的 Number() 类型转换,所有非数值都返回 false13console.log(Number.isFinite('foo')); // false14console.log(Number.isFinite('15')); // false15console.log(Number.isFinite(true)); // falsexxxxxxxxxx121// 用于检测一个值是否为 NaN2Number.isNaN();34console.log(Number.isNaN(NaN)); // true5console.log(Number.isNaN('true'/0)); // true67// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换8// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false9Number.isNaN("NaN"); // false10Number.isNaN(undefined); // false11Number.isNaN({}); // false12Number.isNaN("true"); // false从全局移植到 Number 对象的方法
xxxxxxxxxx121// 用于将给定的字符串转化为指定进制的整数2Number.parseInt();34// 不指定默认为 10 进制5Number.parseInt('12.34'); // 126Number.parseInt(12.34); // 1278// 指定进制9Number.parseInt('0011', 2); // 31011// 与全局的 parseInt() 函数是同一个函数12Number.parseInt === parseInt; // truexxxxxxxxxx111// 用于把一个字符串解析成浮点数2Number.parseFloat();34Number.parseFloat('123.45'); // 123.455Number.parseFloat('123.45abc'); // 123.4567// 无法被解析成浮点数时,则返回 NaN8Number.parseFloat('abc'); // NaN910// 与全局的 parseFloat() 方法是同一个方法11Number.parseFloat === parseFloat; // truexxxxxxxxxx271// 用于判断给定的参数是否是整数2Number.isInteger(value);34Number.isInteger(0); // true5// JavaScript 内部,整数和浮点数采用的是相同的存储方法,因此 1 与 1.0 被视为相同的值6Number.isInteger(1); // true7Number.isInteger(1.0); // true89Number.isInteger(1.1); // false10Number.isInteger(Math.PI); // false1112// NaN 和正负 Infinity 不是整数13Number.isInteger(NaN); // false14Number.isInteger(Infinity); // false15Number.isInteger(-Infinity); // false1617Number.isInteger("10"); // false18Number.isInteger(true); // false19Number.isInteger(false); // false20Number.isInteger([1]); // false2122// 数值的进度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判23Number.isInteger(1.0000000000000001); // true2425// 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0,也会产生误判26Number.isInteger(5E-324); // false27Number.isInteger(5E-325); // truexxxxxxxxxx51// 用于判读整数数值是否在安全范围内2Number.isSafeInteger();34Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1); // false5Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // falseES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用;
详情参考 ES6 Math 对象的扩展 ;
xxxxxxxxxx611// 计算一个数的 立方根2Math.cbrt(num);34// 计算两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位带符号整数5Math.imul(num1, num2);67// 用于计算所有参数的 平方和的平方根8Math.hypot(num1, num2, );9Math.hypot(3, 4); // 51011// 用于返回数字的 32 位无符号整数形式的前导0的个数12Math.clz32(num);1314// 用于返回数字的整数部分15Math.trunc(num);1617// 用于获取数字的 32 位单精度浮点数形式18Math.fround(num);1920// 判断数字的符号(正、负、0)21Math.sign(num);2223// 用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x)-124Math.expm1(num);2526// 用于计算 1+x 的自然对数,即 Math.log(1+x)27Math.log1p(num);2829// 用于计算以 10 为底的 x 的对数30Math.log10(num);3132// 用于计算以 2 为底的 x 的对数33Math.log2(num);3435// 双曲函数方法36// 正弦37Math.sinh(x);3839// 余弦40Math.cosh(x);4142// 正切43Math.tanh(x);4445// 反正弦46Math.asinh(x);4748// 反余弦49Math.acosh(x);5051// 反正切52Math.atanh(x);5354// 指数运算符55// 右结合,从右往左计算56**571 ** 2; // 2582 ** 2 ** 3; // 2565960let exam = 2;61exam ** = 2; // 4属性的简洁表示法
ES6 允许对象的属性值直接写变量。这时候属性名时变量名,属性值是变量值;
xxxxxxxxxx61const age = 12;;2const name = "Amy";3const person = {age, name};4person // {age: 12, name: "Amy"}5// 等同于6const person = {age: age, name: name};方法名也可以简写
xxxxxxxxxx131const person = {2 sayHi(){3 console.log("Hi");4 }5}6person.sayHi(); // "Hi"7// 等同于8const person = {9 sayHi:function(){10 console.log("Hi");11 }12}13person.sayHi(); // "Hi"如果是 Generator 函数,则要在前面加一个星号;
xxxxxxxxxx111const obj = {2 * myGenerator(){3 yield 'hello world';4 }5};6// 等同于7const obj = {8 myGenerator: function* (){9 yield 'hello world';10 }11};属性名表达式
ES6 允许用表达式作为属性名,但是一定要将表达式放在方括号内;
xxxxxxxxxx61const obj = {2 ["he"+"llo"](){3 return "Hi";4 }5}6obj.hello(); // "Hi"注意点: 属性的简洁表示法和属性名表达式 不能 同时使用,会报错;
xxxxxxxxxx111const hello = "Hello";2const obj = {3 [hello]4};5obj // SyntaxError: Unexpected token }67const hello = "Hello";8const obj = {9 [hello+"2"]:"world"10};11obj // {Hello2: 'world'}扩展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象;
基本用法
xxxxxxxxxx31let person = {name: "Amy", age: 15};2let someone = { person };3someone; // {name: "Amy", age: 15}可用于合并两个对象
xxxxxxxxxx41let age = {age: 15};2let name = {name: "Amy"};3let person = {age, name};4person; // {age: 15, name: "Amy"}注意点
自定义属性和拓展运算符对象里面属性相同的时候:后面的属性会把前面的属性覆盖掉
xxxxxxxxxx31let person = {name: "Amy", age: 15};2let someone = { person, name: "Mike", age: 17};3someone; // {name: "Mike", age: 17}xxxxxxxxxx31let person = {name: "Amy", age: 15};2let someone = {name: "Mike", age: 17, person};3someone; // {name: "Amy", age: 15}拓展运算符后面是空对象,没有任何效果也不会报错
xxxxxxxxxx21let a = {{}, a: 1, b: 2};2a; // {a: 1, b: 2}拓展运算符后面是 null 或者 undefined,没有任何效果也不会报错
xxxxxxxxxx21let b = {null, undefined, a: 1, b: 2};2b; // {a: 1, b: 2}Object.assing(target,source_1,…)
用于将源对象的所有可枚举属性复制到目标对象中;
基本用法
xxxxxxxxxx211let target = {a: 1};2let object2 = {b: 2};3let object3 = {c: 3};4Object.assign(target,object2,object3);5// 第一个参数是目标对象,后面的参数是源对象6target; // {a: 1, b: 2, c: 3}78// 如果目标独享和源对象有同名属性,或者多个源对象有同名属性,后面的属性会覆盖前面的属性9// 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,先将该参数转为对象然后返回;10Object.assign(3); // Number {3}11typeof Object.assign(3); // "object"1213// 因为 null 和 undefined 不能转化为对象,所以会报错14Object.assign(null); // TypeError: Cannot convert undefined or null to object15Object.assign(undefined); // TypeError: Cannot convert undefined or null to object1617// 当 null 和 undefined 为源对象的参数时,会跳过,不报错18Object.assign(1, undefined); // Number {1}19Object.assign({a: 1}, null); // {a: 1}2021Object.assign(undefined, {a: 1}); // TypeError: Cannot convert undefined or null to object注意点
assing 的属性拷贝是浅拷贝
xxxxxxxxxx51let sourceObj = {a: {b: 1}};2let targetObj = {c: 3};3Object.assign(targetObj, sourceObj);4targetObj.a.b = 2;5sourceObj.a.b; // 2同名属性替换
xxxxxxxxxx51let targetObj = {a: {b: 1, c: 2}};2let sourceObj = {a: {b: "hh"}};3Object.assign(targetObj, sourceObj);4targetObj; // {a: {b: "hh"}}5// 会直接把 整个a 属性替换掉,而不是替换 a.b属性数组的替换
xxxxxxxxxx21Object.assign([2,3], [5]); // [5,3]2// 会将数组处理成对象,所以先将 [2,3] 转化为 {0: 2, 1: 3},然后在进行属性复制。所以源对象的 0 号属性覆盖了目标对象的0 号属性Object.is(value1,value2)
用来比较两个值是否严格相等,与(===)基本类似。
基本用法
xxxxxxxxxx41Object.is("q","q"); // true2Object.is(1,1); // true3Object.is([1],[1]); // false4Object.is({q:1},{q:1}); // false与(===)的区别
xxxxxxxxxx71// 一是 +0不等于-02Object.is(+0,-0); // false3+0 === -0 // true45// 二是 NaN等于本身6Object.is(NaN,NaN); // true7NaN === NaN // falseArray.of()
将参数中所有值作为元素形成数组
xxxxxxxxxx71console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]23// 参数值可为不同类型4console.log(Array.of(1, '2', true)); // [1, '2', true]56// 参数为空时返回空数组7console.log(Array.of()); // []Array.from()
将 类数组对象 或 可迭代对象 转化为数组
xxxxxxxxxx51// 参数为数组,返回和原数组一样的数组2console.log(Array.from([1, 2])); // [1, 2]34// 参数含空位5console.log(Array.from(1, , 3)); // [1, undefined, 3]参数
xxxxxxxxxx11Array.from(arrayLike[, mapFn[, thisArg]])返回值为转换后的数组
arrayLike
想要转化的 类数组对象 或 可迭代对象
xxxxxxxxxx11console.log(Array.from([1, 2, 3])); // [1, 2, 3]mapFn
可选,map 函数,用于对每个元素进行处理,放入数组的是 处理后 的元素
xxxxxxxxxx11console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]thisArg
可选,用于指定 map 函数执行时的 this 对象
xxxxxxxxxx91let map = {2 do: function(n) {3 return n * 2;4 }5}6let arrayLike = [1, 2, 3];7console.log(Array.from(arrayLike, function (n) {8 return this.do(n);9}, map)); // [2, 4, 6]类数组对象
一个类数组对象必须含有 length 属性,且元素属性名必须是数值或者可转为数值的字符
xxxxxxxxxx231let arr = Array.from({2 0: '1',3 1: '2',4 2: 3,5 length: 36});7console.log(arr); // ['1', '2', 3]89// 没有 length 属性,则返回空数组10let array = Array.from({11 0: '1',12 1: '2',13 2: 314});15console.log(array); // []1617// 元素属性名不为数值 且无法转换为数值,返回长度为 length ,元素值为 undefined 的数组18let array1 = Array.from({19 a: 1,20 b: 2,21 length: 222});23console.log(array1); // [undefined, undefined]转换可迭代对象
转换 map
xxxxxxxxxx41let map = new Map();2map.set('key0', 'value0');3map.set('key1', 'value1');4console.log(Array.from(map)); // [['key0', 'value0'], ['key1', 'value1']]转换 set
xxxxxxxxxx31let arr = [1, 2, 3];2let set = new Set(arr);3console.log(Array.from(set)); // [1, 2, 3]转换 字符串
xxxxxxxxxx21let str = 'abc';2console.log(Array.from(str)); // ["a", "b", "c"]查找
find()
查找数组中符合条件的元素,返回第一个元素
xxxxxxxxxx51let arr = Array.of(1, 2, 3, 4);2console.log(arr.find(item => item > 2)); // 334// 数组空位 处理为 undefined5console.log([, 1].find(n => true)); // undefinedfindIndex()
查找数组中符合条件的元素索引,返回第一个元素索引
xxxxxxxxxx41let arr = Array.of(1, 2, 3, 4);2// 参数1: 回调函数3// 参数2(可选): 指定回调函数中的 this 值(可参考Array.from 的 thisArg参数)4console.log(arr.findIndex(item => item === 3)); // 2填充
fill()
将一定范围索引的数组元素内容 填充 为单个指定的值
xxxxxxxxxx91let arr = Array.of(1, 2, 3, 4);2// 参数1: 用来填充的值3// 参数2: 被填充的起始索引4// 参数3(可选): 被填充的结束索引,默认为数组末尾5// 参数2 <= 填充值索引 < 参数36console.log(arr.fill(0, 1, 1)); // [1, 2, 3, 4]7console.log(arr.fill(0, 1, 2)); // [1, 0, 3, 4]8console.log(arr.fill(0, 1, 3)); // [1, 0, 0, 4]9console.log(arr.fill(0, 1)); // [1, 0, 0, 0]copyWithIn()
将一定范围索引的数组元素 修改 为此数组另一指定范围索引的元素
xxxxxxxxxx101// 参数1: 被修改的起始索引2// 参数2: 被用来覆盖的数据的起始索引3// 参数3(可选): 被用来覆盖的数组的结束索引,默认为数组末尾4// 参数2 <= 覆盖值索引 < 参数35console.log([1, 2, 3, 4].copyWithIn(0, 2, 4)); // [3, 4, 3, 4]67// 参数1为负数,表示倒数。最后一个数为 -18console.log([1, 2, 3, 4].copyWithIn(-2, 0)); // [1, 2, 1, 2]910console.log([1, 2, , 4].copyWithIn(0, 2, 4)); // [, 4, , 4]遍历
entries()
遍历键值对
xxxxxxxxxx131for (let [key, value] of ['a', 'b'].entries()) {2 console.log(key, value);3}4// 0 "a"5// 1 "b"67// 不使用 for...of 循环8let entries = ['a', 'b'].entries();9console.log(entries.next().value); // [0, "a"]10console.log(entries.next().value); // [1, "b"]1112// 数组含空位13consoel.log([[, 'a'].entries()]); // [[0, undefined], [1, "a"]]keys()
遍历键名
xxxxxxxxxx81for (let key of ['a', 'b'].keys()) {2 console.log(key);3}4// 05// 167// 数组含空位8console.log([[, 'a'].keys()]); // [0, 1]values()
遍历键的值
xxxxxxxxxx81for (let value of ['a', 'b'].values()) {2 console.log(value);3}4// "a"5// "b"67// 包含空数组8console.log([[, 'a'].values()]); // [undefined, "a"]包含
includes()
数组是否包含指定值
注意: 与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名
xxxxxxxxxx51// 参数1: 包含的指定值2// 参数2(可选): 搜索的起始索引,默认为03[1, 2, 3].includes(1); // true4[1, 2, 3].includes(1, 2); // false5[1, NaN, 3].includes(NaN); // true嵌套数组转一维数组
flat()
默认转换一层,即去掉一个中括号
xxxxxxxxxx101console.log([1, [2, 3]].flat()); // [1, 2, 3]23// 指定转换的嵌套层数4console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]56// 不管嵌套多少层7console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]89// 自动跳过空位10console.log([1, [2, , 3]].flat()); // [1, 2, 3]flatMap()
先对数组中的每个元素进行了处理,再对数组执行 flat() 方法
xxxxxxxxxx31// 参数1: 遍历函数,该遍历函数可接受3个参数:当前元素,当前元素索引,原数组2// 参数2: 指定遍历函数中 this 的指向3console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]复制数组
xxxxxxxxxx81let arr = [1, 2];2let arr1 = [arr];3console.log(arr1); // [1, 2]45// 数组含空位6let arr2 = [1, , 3];7let arr3 = [arr2];8console.log(arr3); // [1, undefined, 3]合并数组
xxxxxxxxxx11console.log([[1, 2], [3, 4]]); // [1, 2, 3, 4]基本用法
xxxxxxxxxx61function fn(name, age = 17) {2 console.log(name + "," + age);3}4fn("Amy",18); // Amy,185fn("Amy",""); // Amy,6fn("Amy"); // Amh,17注意点:使用函数默认参数时,不允许有同名参数
xxxxxxxxxx101// 不报错2function fn(name, name) {3 console.log(name);4}56// 报错7// SyntaxError: Duplicate parameter name not allowed in this context8function fn(name, name, age=17) {9 console.log(name+","+age);10}只有在未传递参数,或者参数为 nudefined 时,才会使用默认参数。null 值被认为是有效的值传递
xxxxxxxxxx41function fn(name, age=17) {2 console.log(name+","+age);3}4fn("Amy",null); // Amy,null函数默认参数存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值
xxxxxxxxxx91function f(x, y=x) {2 console.log(x,y);3}4f(1); // 1 156function f(x=y) {7 console.log(x);8}9f(); // ReferenceError: y is not defined不定参数用来表示不确定参数个数,形如,…变量名。由...加上一个具名参数标识符组成
具名参数只能放在参数组的最后,并且有且只有一个不定参数
xxxxxxxxxx61function f(values) {2 console.log(values.length);3}45f(1,2); // 26f(1,2,3,4); // 4箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
xxxxxxxxxx11参数 => 函数体基本语法
xxxxxxxxxx61var f = v => v;2// 等价于3var f = function(a) {4 return a;5}6f(1); // 1当箭头函数没有参数或者有多个参数,要用 () 括起来
xxxxxxxxxx21var f = (a,b) => a+b;2f(6,2); // 8当箭头函数函数体有多行语句,要用 {} 括起来,表示代码块。当只有一行语句,并且需要返回结果时,可以省略 {} ,结果会自动返回
xxxxxxxxxx51var f = (a,b) => {2 let result = a+b;3 return result;4}5f(6,2); // 8当箭头函数要返回对象的时候,为了区分与代码块,要用 () 将对象包裹起来
xxxxxxxxxx71// 报错2var f = (id,name) => {id: id, name: name};3f(6,2); // SyntaxError: Unexpected token :45// 不报错6var f = (id,name) => ({id: id, name: name});7f(6,2); // {id: 6, name: 2}注意点:没有 this、super、arguments 和 new.target 绑定
xxxxxxxxxx111var func = () => {2 // 箭头函数里没有 this 对象3 // 此时的 this 是外层的 this 对象,即 Window4 console.log(this);5}6func(55); // Window78var func = () => {9 console.log(arguments);10} 11func(55); // ReferenceError: arguments is not defined箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象
xxxxxxxxxx91function fn(){2 setTimeout(()=>{3 // 定义时,this 绑定的是 fn 中的 this 对象4 console.log(this.a);5 },0)6}7var a = 20;8// fn 的 this 对象为 {a: 18}9fn.call({a: 18}); // 18不可以作为构造函数,也就是不能使用 new 命令,会报错
ES6 之前, JavaScript 的 this 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中。有了箭头函数,就不需要这样了;
回调函数 中的 普通函数 的 this ,指 使用函数时的对象
回调函数 中的 箭头函数 的 this ,指 定义函数时的对象
或者说,要 维护一个 this 的上下文 的时候,使用 箭头函数
xxxxxxxxxx221// 回调函数2var Person = {3 'age': 18,4 'sayHello': function (){5 setTimeout(function(){6 console.log(this.age);7 },0);8 }9};10var age = 20;11Person.sayHello(); // 201213var Person1 = {14 'age': 18,15 'sayHello': function(){16 setTimeout(()=>{17 console.log(this.age);18 },0);19 }20}21var age = 20;22Person1.sayHello(); // 18定义函数的方法,且该方法中包含 this
一般函数 中的 普通函数 的 this , 指 定义函数时的对象
一般函数 中的 箭头函数 的 this , 指 外层的 this对象,最外层为全局对象 Window
xxxxxxxxxx191var Person = {2 'age': 18,3 'sayHello': function(){4 console.log(this.age);5 }6}7var age = 20;8Person.sayHello(); // 189 // 此时的 this 指 Person 对象1011var Person1 = {12 'age': 18,13 'sayHello': ()=>{14 console.log(this.age);15 }16}17var age = 20;18Person1.sayHello(); // 2019 // 此时的 this 指 外层的 Window 全局对象需要动态 this 的时候
xxxxxxxxxx61var button = document.getElementById('userClick');2button.addEventListener('click',()=>{3 this.classList.toggle('on');4});5// button 的监听函数是箭头函数,所以监听函数里的 this 指向的是定义的时候外层的 this 对象,即 Window。6// 导致无法操作到被点击的按钮对象可迭代的数据结构
Array
String
Map
Set
Dom元素(正在进行中)
普通对象(由 object 创建)不可迭代
常规数据类型
Array
String
Map
Set
可迭代的数据类型
类数组对象
let、const 和 var
类定义
类声明
注意要点
类的主体
属性
静态属性
公共属性
实例属性
name 属性
方法
constructor 方法
返回对象
静态方法
原型方法
实例方法
类的实例化
new
实例化对象
类修饰
一个参数
多个参数——嵌套实现
方法修饰
三个参数
基本用法
as 的用法
import 命令的特点
只读属性
单例模式
export default 命令