ES6 全称 ECMAScript 6.0 ,2015.06 发布。
本教程内容如下:
let 只在代码块内有效,var 在全局范围内有效;
let 只能声明一次,var 可以多次声明;
let 不存在变量提升,var 会变量提升;
xxxxxxxxxx
51console.log(a); //ReferenceError: a is not defined
2let a = "apple";
3
4console.log(b); //undefined
5var b = "banana";
let 和 const 都会存在;
xxxxxxxxxx
51var PI = "a";
2if(true){
3 console.log(PI); //ReferenceError: PI is not defined
4 const PI = "3.1415926";
5}
ES6 明确规定,代码块内如果存在 let 或者 const ,代码块会对这些声明命令的变量从 块的开始 就形成一个封闭作用域。在代码块内,在声明变量 PI 之前使用它就会报错。
在解构中,有下面两部分参与:
基本
xxxxxxxxxx
41let [a, b, c] = [1, 2, 3];
2// a = 1
3// b = 2
4// c = 3
可嵌套
xxxxxxxxxx
41let [a, [[b], c]] = [1, [[2], 3]];
2// a = 1
3// b = 2
4// c = 3
可忽略
xxxxxxxxxx
31let [a, , b] = [1, 2, 3];
2// a = 1
3// b = 3
不完全解构
xxxxxxxxxx
31let [a = 1, b] = [];
2// a = 1
3// b = undefined
剩余运算符
xxxxxxxxxx
31let [a, b] = [1, 2, 3];
2// a = 1
3// b = [2, 3]
字符串等
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据;
xxxxxxxxxx
61let [a, b, c, d, e] = 'hello';
2// a = 'h'
3// b = 'e'
4// c = 'l'
5// d = 'l'
6// e = 'o'
解构默认值
xxxxxxxxxx
21let [a = 2] = [undefined];
2// a = 2
当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果;
xxxxxxxxxx
61let [a = 3, b = a] = [];
2// a = 3, b = 3
3let [a = 3, b = a] = [1];
4// a = 1, b = 1
5let [a = 3, b = a] = [1, 2]
6// a = 1, b = 2
基本
xxxxxxxxxx
61let { foo, bar } = { foo: 'aaa', bar : 'bbb' };
2// foo = 'aaa'
3// bar = 'bbb'
4
5let { baz : foo } = { baz : 'ddd'};
6// foo = 'ddd'
可嵌套可忽略
xxxxxxxxxx
81let obj = { p: ['hello', { y: 'world'}] };
2let { p: [x, { y }] } = obj;
3// x = 'hello'
4// y = 'world'
5
6let obj = { p: ['hello', { y : 'world' }] };
7let { p: [x, { }] } = obj;
8// x = 'hello'
不完全解构
xxxxxxxxxx
41let obj = { p: [{y: 'world'}] };
2let { p: [{ y }, x]} = obj;
3// x = undefined
4// y = 'world'
剩余运算符
xxxxxxxxxx
41let {a, b, rest} = {a: 10, b: 20, c: 30, d: 40};
2// a = 10
3// b = 20
4// rest = {c: 30, d: 40}
解构默认值
xxxxxxxxxx
41let {a = 10, b = 5} = {a: 3};
2// a = 3; b = 5
3let {a: aa = 10,b: bb = 5} = {a: 3}
4// aa = 3; bb = 5
Symbol 函数栈不能使用 new 命令,因为 Symbol 是原始数据类型,不是对象。
可以接收一个 字符串 作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分;
字符串相同的 Symbol ,值不相同;
xxxxxxxxxx
71let sy = Symbol("kk");
2console.log(sy); // Symbol(kk)
3typeof(sy); // "symbol"
4
5// 参数相同的 Symbol() 返回的值不相等
6let sy1 = Symbol("kk");
7console.log(sy === sy1); // false
由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名;
xxxxxxxxxx
171let sy = Symbol("key1");
2
3// 写法1
4let syObject = {};
5syObject[sy] = "kk";
6console.log(syObject); // {Symbol(key1): "kk"}
7
8// 写法2
9let syObject = {
10 [sy]: "kk"
11};
12console.log(syObject); // {Symbol(key1): "kk"}
13
14// 写法3
15let syObject = {};
16Object.defineProperty(syObject, sy, {value: "kk"});
17console.log(syObject); // {Symbol(key1): "kk"}
Symbol 作为对象属性名时不能用 . 运算符,要用方括号。因为 . 运算符后面是字符串,所以取到的是 字符串 值 sy 属性,而不是 Symbol 值 sy 属性;
xxxxxxxxxx
51let syObject = {};
2syObject[sy] = "kk";
3
4syObject[sy]; // "kk"
5syObject.sy; // undefined
Symbol 值作为属性名时,该属性是公有属性,不是私有属性,可以在类的外部访问;
不会出现在 for … in ,for … of 的循环中;
也不会被 Object.keys() ,Object.getOwnPropertyNames() 返回;
如果要读取一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到;
xxxxxxxxxx
111let syObject = {};
2syObject[sy] = "kk";
3console.log(syObject); // {Symbol(key1): "kk"}
4
5for (let i in syObject){
6 console.log(i); // 无输出
7}
8
9Object.keys(syObject); // []
10Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]
11Reflect.ownKeys(syObject); // [Symbol(key1)]
在 ES5 中用字符串表示常量,但是字符串不能保证是唯一的,这会引起一些问题:
xxxxxxxxxx
191const COLOR_RED = "red";
2const COLOR_YELLOW = "yellow";
3const COLOR_BLUE = "blue";
4const MY_BLUE = "blue";
5
6function 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 来修改上面的例子;
xxxxxxxxxx
191const COLOR_RED = Symbol("red");
2const COLOR_RED = Symbol("yellow");
3const COLOR_BLUE = Symbol("blue");
4const MY_BLUE = Symbol("blue");
5
6function 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 值,并 登记 在全局环境中供搜索;
xxxxxxxxxx
61let yellow = Symbol("Yellow");
2let yellow1 = Symbol.for("Yellow");
3console.log(yellow === yellow1); // false
4
5let yellow2 = Symbol.for("Yellow");
6console.log(yellow1 === yellow2); // true
Symbol.keyFor() 返回一个 已登记 的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被 登记 ;
xxxxxxxxxx
51let yellow = Symbol("Yellow");
2console.log(Symbol.keyFor(yellow)); // undefined
3
4let yellow1 = Symbol.for("Yellow");
5console.log(Symbol.keyFor(yellow1)); // "Yellow"
key 是字符串
xxxxxxxxxx
81var myMap = new Map();
2var keyString = "a string";
3
4myMap.set(keyString, "和键'a string'关联的值");
5
6myMap.get(keyString); // "和键'a string'关联的值"
7myMap.get("a string"); // "和键'a string'关联的值"
8 // 因为 keyString === 'a string'
key 是对象
xxxxxxxxxx
71var myMap = new Map();
2var keyObj = {};
3
4myMap.set(keyObj, "和键 keyObj 关联的值");
5
6myMap.get(keyObj); // "和键 keyObj 关联的值"
7myMap.get({}); // undefined, 因为 keyObj !== {}
key 是函数
xxxxxxxxxx
71var myMap = new Map();
2var keyFunc = function(){};
3
4myMap.set(keyFunc, "和键 keyFunc 关联的值");
5
6myMap.get(keyFunc); // "和键 keyFunc 关联的值"
7myMap.get(function() {}); // undefined,因为 keyFunc !== function() {}
key 是NaN
xxxxxxxxxx
81var myMap = new Map();
2myMap.set(NaN, "not a number");
3
4myMap.get(NaN); // "not a number"
5
6var otherNuN = Number("foo");
7myMap.get(otherNaN); // "not a number"
8 // 虽然 NaN 和任何值甚至它自己都不相等(NaN !== NaN 返回 true),但 NaN 作为 Map 的键来说是没有区别的
对 Map 进行遍历,以下两个最高级;
for … of
xxxxxxxxxx
241var myMap = new Map();
2myMap.set(0, "zero");
3myMap.set(1, "one");
4
5// 将会显示两个 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] 数组 */
13
14// 将会显示两个 log。一个是 "0",另一个是 "1"
15for (var key of myMap.keys()) {
16 console.log(key);
17}
18/* 这个 keys() 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的键 */
19
20// 将会显示两个 log。一个是 "zero",另一个是 "one"
21for (var value of myMap.values()) {
22 console.log(value);
23}
24/* 这个 values() 方法返回一个 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */
forEach()
xxxxxxxxxx
81var myMap = new Map();
2myMap.set(0, "zero");
3myMap.set(1, "one");
4
5// 将会显示两个 log。一个是 "0 = zero",另一个是 "1 = one"
6myMap.forEach(function(value, key){
7 console.log(key + " = " + value);
8}, myMap)
Map 与 Array 的转换
xxxxxxxxxx
71var kvArray = [["key1", "value1"], ["key2", "value2"]];
2
3// Map 构造函数可以将一个 二维键值对数组 转换成一个 Map 对象
4var myMap = new Map(kvArray);
5
6// Array.from 函数可以将一个 Map 对象转换成一个 二维键值对数组
7var outArray = Array.from(myMap);
Map 的克隆
xxxxxxxxxx
51var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
2var myMap2 = new Map(myMap1);
3
4console.log(myMap1 === myMap2);
5// false. Map 对象构造函数生成的实例,迭代出新的对象
Map 的合并
xxxxxxxxxx
61var first = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
2var second = new Map([[1, 'uno'], [2, 'dos']]);
3
4var merged = new Map([first, second]);
5// 合并两个 Map 对象时,如果有重复的键值,后面的会覆盖前面的。
6// 所以,merged 后的值是 uno, dos, three
Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
+0 和 -0 在存储判断唯一性的时候是恒等的,所以不重复;
undefined 与 undefined 是恒等的,所以不重复;
NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,所以不重复;
xxxxxxxxxx
131let mySet = new Set();
2
3mySet.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}
8
9mySet.add("some text"); // Set(4) {1, 5, 0, "some text"} 这里体现了类型的多样性
10
11var 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
xxxxxxxxxx
121// Array ==> Set
2var mySet = new Set(["value1", "value2", "value3"]);
3
4// Set ==> Array,用 ...操作符
5var myArray = [mySet];
6
7// String ==> Set
8var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}
9// Set ==> String
10// 可以 Set ==> Array ==> String (.toString 或 join 都可以)
11// .toString 是不能将 Set 转换成 String
12
数组去重
xxxxxxxxxx
21var mySet = new Set([1, 2, 3, 4, 4]);
2[mySet]; // [1, 2, 3, 4]
并集
xxxxxxxxxx
31var a = new Set([1, 2, 3]);
2var b = new Set([4, 3, 2]);
3var union = new Set([a, b]); // {1, 2, 3, 4}
交集
xxxxxxxxxx
31var 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}
差集
xxxxxxxxxx
31var 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 的指定行为;
xxxxxxxxxx
411let target = {
2 name: 'Tom',
3 age: 24
4}
5let handler = {
6 get: function(target, key) {
7 console.log('getting ' + key);
8 return target[key]; // 不是target.key
9 },
10 set: function(target, key, value) {
11 console.log('setting ' + key);
12 target[key] = value;
13 }
14}
15
16let proxy = new Proxy(target, handler);
17proxy.name; // 实际执行 handler.get
18proxy.age = 25; // 实际执行 handler.set
19// getting name
20// setting age
21// 25
22
23// target 可以为空对象
24let targetEpt = {};
25let proxyEpt = new Proxy(targetEpt, handler);
26// 调用 get 方法,此时目标对象为空,没有 name 属性
27proxyEpt.name;
28// 调用 set 方法,向目标对象中添加了 name 属性
29proxyEpt.name = 'Tom';
30// setting name
31// "Tom"
32// 再次调用 get ,此时已经存在 name 属性了
33proxyEpt.name;
34// getting name
35// "Tom"
36
37// handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象
38let targetEmpty = {};
39let proxyEmpty = new Proxy(targetEmpty, {});
40proxyEmpty.name = "Tom";
41targetEmpty) // {name: "Tom"}
详情参考 ES6 Reflect ;
静态方法
xxxxxxxxxx
381// 查找并返回 target 对象的 name 属性
2Reflect.get(target, name, receiver);
3
4// 将 target 的 name 属性设置为 value。返回值为 boolean,true 表示修改成功,false 表示修改失败。当 target 为不存在的对象时,会报错
5Reflect.set(target, name, value, receiver);
6
7// 是 name in obj 指令的函数化,用于查找 name 属性在 obj 中是否存在。返回值为 boolean。如果 obj 不是对象则会报错 TypeError
8Reflect.has(obj, name)
9
10// 是 delete obj[property] 的函数化,用于删除 obj 对象的 property 属性。返回值为 boolean。如果 obj 不是对象则会报错 TypeError
11Reflect.deleteProperty(obj, property)
12
13// 等同于 new target(...args)
14Reflect.construct(obj, args);
15
16// 用于读取 obj 的 _proto_ 属性。在 obj 不是对象时会报错。
17Reflect.getPrototypeOf(obj)
18
19// 用于设置目标对象的 prototype
20Reflect.setPrototypeOf(obj, newProto);
21
22// 等同于 Function.prototype.apply.call(func, thisArg, args).func 表示目标函数;thisArg 表示目标函数绑定的 this 对象;args 表示目标函数调用时传入的参数列表,可以是数组或类似数组的对象。若目标函数无法调用,会抛出 TypeError
23Reflect.apply(func, thisArg, args);
24
25// 用于为目标对象定义属性。如果 target 不是对象,会抛出异常
26Reflect.defineProperty(target, propertyKey, attributes)
27
28// 用于得到 target 对象的 propertyKey 属性的描述。如果 target 不是对象,会抛出错误
29Reflect.getOwnPropertyDescriptor(target, propertyKey)
30
31// 用于判断 target 对象是否可扩展。返回值为 boolean。如果 target 参数不是对象,会抛出异常;
32Reflect.isExtensible(target)
33
34// 用于让 target 对象变为不可扩展。如果 target 参数不是对象,会抛出异常
35Reflect.preventExtensions(target)
36
37// 用于返回 target 对象的所有属性,等同于 Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 之和
38Reflect.ownKeys(target)
Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作;
xxxxxxxxxx
211let exam = {
2 name: "Tom",
3 age: 24
4}
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}
15
16let proxy = new Proxy(target, handler);
17proxy.name = "Jerry";
18proxy.name;
19// setting name to Jerry
20// getting name
21// "Jerry"
ES6 之前判断字符串是否包含子串,是用 indexOf 方法,ES6 新增了子串的识别方法。
includes() :返回布尔值,判断是否找到参数字符串;
startsWith() :返回布尔值,判断参数字符串是否是在原字符串的头部;
endsWith() :返回布尔值,判断参数字符串是否是在原字符串的尾部;
以上三个方法都可以接受两个参数 - 需要搜索的字符串,和 可选的搜索起始位置索引;
xxxxxxxxxx
51let string = "apple,banana,orange";
2string.includes("banana"); // true
3string.startsWith("apple"); // true
4string.endsWith("apple"); // false
5string.startsWith("banana",6); // true
注意点 :
repeat() :返回新的字符串,表示将字符串重复指定次数返回;
xxxxxxxxxx
11console.log("hello,".repeat(2)); // "hello,hello,"
如果参数是小数,向下取整;
xxxxxxxxxx
11console.log("hello,".repeat(3.2)); // "hello,hello,hello,"
如果参数是 0 至 -1 之间的小数,会进行取整运算,等同于 0 ;
xxxxxxxxxx
11console.log("hello,".repeat(-0.5)); // ""
如果参数是 NaN ,等同于 0 ;
xxxxxxxxxx
11console.log("hello,".repeat(NaN)); // ""
如果参数是负数或者 Infinity ,会报错;
xxxxxxxxxx
21console.log("hello,".repeat(-1)); // RangeError: Invalid count value
2console.log("hello,".repeat(Infinity)); // RangeError: Invalid count value
如果参数的参数是字符串,会先将字符串转化为数字;
xxxxxxxxxx
21console.log("hello,".repeat("hh")); // ""
2console.log("hello,".repeat("2")); // "hello,hello,"
padStart :返回新的字符串,表示用参数字符串从 头部 补全原字符串;
padEnd :返回新的字符串,表示用参数字符串从 尾部 补全原字符串;
以上两个方法接受两个参数 - 生成字符串的最小长度,和 可选的用来补全的字符串。如果没有第二个参数,默认用空格填充;
xxxxxxxxxx
31console.log("h".padStart(5,"0")); // "ooooh"
2console.log("h".padEnd(5,"0")); // "hoooo"
3console.log("h".padStart(5)); // " h"
如果指定的长度小于或等于原字符串的长度,则返回原字符串;
xxxxxxxxxx
31console.log("hello".padStart(3,"A")); // "hello"
2console.log("hello".padStart(5,"A")); // "hello"
3console.log("hello".padStart(10,"A")); // "AAAAAhello"
如果原字符串加上补全字符串的长度大于指定长度,则截去超出位数的补全字符串;
xxxxxxxxxx
11console.log("hello".padStart(10,",world!")); // "hello,worl"
常用于补全位数;
xxxxxxxxxx
11console.log("123".padStart(10,"0")); // "0000000123"
普通字符串
xxxxxxxxxx
41let string = `Hello'\n'world`;
2console.log(string);
3// "Hello"
4// "world"
多行字符串
xxxxxxxxxx
51let string1 = `Hey,
2can you stop angry now?`;
3console.log(string1);
4// Hey,
5// can you stop angry now?
字符串插入变量和表达式
变量名写在 ${} 中,${} 中也可以放入 JavaScript 表达式;
xxxxxxxxxx
51let 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.
字符串中调用函数
xxxxxxxxxx
61function f() {
2 return "have fun!";
3}
4let string2 = `Game start,${f()}`;
5console.log(string2);
6// Game start,have fun!
模板字符串中的换行和空格都是会被保留的
xxxxxxxxxx
111let 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>
模板标签,是一个函数的调用,调用的参数是模板字符串;
xxxxxxxxxx
31alert`Hello world!`;
2// 等价于
3alert('Hello world!');
当模板字符串中带有变量,会将模板字符串参数处理成多个参数;
xxxxxxxxxx
181function 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."
15
16f`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 字符串,防止用于输入恶意内容
xxxxxxxxxx
141function 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}
11
12var 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>
国际化处理(转化多国语言)
xxxxxxxxxx
21i18n`Hello ${name}, you are visitor number ${visitorNumber}`;
2// 你好**,你是第**位访问者
二进制表示法新写法:前缀 0b 或 0B ;
xxxxxxxxxx
21console.log(0b11 === 3); // true
2console.log(0B11 === 3); // true
八进制表示法新写法:前缀 0o 或 0O ;
xxxxxxxxxx
21console.log(0o11 === 9); // true
2console.log(0O11 === 9); // true
Number.EPSILON ;
表示 1 与 大于 1 的最小浮点数之间的差;
它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。
可用于测试数值是否在误差范围内
xxxxxxxxxx
310.1 + 0.2 === 0.3; // false
2// 在误差范围内 即视为相等
3equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true
属性特性
xxxxxxxxxx
31writeable: false
2enumerable: false
3configurable: false
安全整数
安全整数表示在 JavaScript 中能够精确表示的整数。安全整数的范围在 2 的 -53 次方到 2 的 53 次方之间(不包括两个端点)。超过这个范围的整数无法精确表示;
最大安全整数
安全整数范围的上限,即 2 的 53 次方减 1;
xxxxxxxxxx
41Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1; // true
2Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // true
3Number.MAX_SAFE_INTEGER === Number.MAX_SAFE_INTEGER + 1; // false
4Number.MAX_SAFE_INTEGER -1 === Number.MAX_SAFE_INTEGER - 2; // false
最小安全整数
安全整数范围的下限,即 2 的 53 次方减 1 的负数
xxxxxxxxxx
41Number.MIN_SAFE_INTEGER === -(Math.pow(2, 53) - 1); // true
2Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2; // true
3Number.MIN_SAFE_INTEGER === Number.MIN_SAFE_INTEGER - 1; // false
4Number.MIN_SAFE_INTEGER + 1 === Number.MIN_SAFE_INTEGER + 2; // false
属性特性
xxxxxxxxxx
31wirteable: false
2enumerable: false
3configurable: false
Number 对象新方法
xxxxxxxxxx
151// 用于检查一个数值是否为有限的(finite),即不是 Infinity
2Number.isFinite();
3
4console.log(Number.isFinite(1)); // true
5console.log(Number.isFinite(0.1)); // true
6
7// NaN 不是有限的
8console.log(Number.isFinite(NaN)); // false
9
10console.log(Number.isFinite(Infinity)); // false
11console.log(Number.isFinite(-Infinity));// false
12// Number.isFinite 没有隐式的 Number() 类型转换,所有非数值都返回 false
13console.log(Number.isFinite('foo')); // false
14console.log(Number.isFinite('15')); // false
15console.log(Number.isFinite(true)); // false
xxxxxxxxxx
121// 用于检测一个值是否为 NaN
2Number.isNaN();
3
4console.log(Number.isNaN(NaN)); // true
5console.log(Number.isNaN('true'/0)); // true
6
7// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
8// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
9Number.isNaN("NaN"); // false
10Number.isNaN(undefined); // false
11Number.isNaN({}); // false
12Number.isNaN("true"); // false
从全局移植到 Number 对象的方法
xxxxxxxxxx
121// 用于将给定的字符串转化为指定进制的整数
2Number.parseInt();
3
4// 不指定默认为 10 进制
5Number.parseInt('12.34'); // 12
6Number.parseInt(12.34); // 12
7
8// 指定进制
9Number.parseInt('0011', 2); // 3
10
11// 与全局的 parseInt() 函数是同一个函数
12Number.parseInt === parseInt; // true
xxxxxxxxxx
111// 用于把一个字符串解析成浮点数
2Number.parseFloat();
3
4Number.parseFloat('123.45'); // 123.45
5Number.parseFloat('123.45abc'); // 123.45
6
7// 无法被解析成浮点数时,则返回 NaN
8Number.parseFloat('abc'); // NaN
9
10// 与全局的 parseFloat() 方法是同一个方法
11Number.parseFloat === parseFloat; // true
xxxxxxxxxx
271// 用于判断给定的参数是否是整数
2Number.isInteger(value);
3
4Number.isInteger(0); // true
5// JavaScript 内部,整数和浮点数采用的是相同的存储方法,因此 1 与 1.0 被视为相同的值
6Number.isInteger(1); // true
7Number.isInteger(1.0); // true
8
9Number.isInteger(1.1); // false
10Number.isInteger(Math.PI); // false
11
12// NaN 和正负 Infinity 不是整数
13Number.isInteger(NaN); // false
14Number.isInteger(Infinity); // false
15Number.isInteger(-Infinity); // false
16
17Number.isInteger("10"); // false
18Number.isInteger(true); // false
19Number.isInteger(false); // false
20Number.isInteger([1]); // false
21
22// 数值的进度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
23Number.isInteger(1.0000000000000001); // true
24
25// 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0,也会产生误判
26Number.isInteger(5E-324); // false
27Number.isInteger(5E-325); // true
xxxxxxxxxx
51// 用于判读整数数值是否在安全范围内
2Number.isSafeInteger();
3
4Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1); // false
5Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // false
ES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用;
详情参考 ES6 Math 对象的扩展 ;
xxxxxxxxxx
611// 计算一个数的 立方根
2Math.cbrt(num);
3
4// 计算两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位带符号整数
5Math.imul(num1, num2);
6
7// 用于计算所有参数的 平方和的平方根
8Math.hypot(num1, num2, );
9Math.hypot(3, 4); // 5
10
11// 用于返回数字的 32 位无符号整数形式的前导0的个数
12Math.clz32(num);
13
14// 用于返回数字的整数部分
15Math.trunc(num);
16
17// 用于获取数字的 32 位单精度浮点数形式
18Math.fround(num);
19
20// 判断数字的符号(正、负、0)
21Math.sign(num);
22
23// 用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x)-1
24Math.expm1(num);
25
26// 用于计算 1+x 的自然对数,即 Math.log(1+x)
27Math.log1p(num);
28
29// 用于计算以 10 为底的 x 的对数
30Math.log10(num);
31
32// 用于计算以 2 为底的 x 的对数
33Math.log2(num);
34
35// 双曲函数方法
36// 正弦
37Math.sinh(x);
38
39// 余弦
40Math.cosh(x);
41
42// 正切
43Math.tanh(x);
44
45// 反正弦
46Math.asinh(x);
47
48// 反余弦
49Math.acosh(x);
50
51// 反正切
52Math.atanh(x);
53
54// 指数运算符
55// 右结合,从右往左计算
56**
571 ** 2; // 2
582 ** 2 ** 3; // 256
59
60let exam = 2;
61exam ** = 2; // 4
属性的简洁表示法
ES6 允许对象的属性值直接写变量。这时候属性名时变量名,属性值是变量值;
xxxxxxxxxx
61const age = 12;;
2const name = "Amy";
3const person = {age, name};
4person // {age: 12, name: "Amy"}
5// 等同于
6const person = {age: age, name: name};
方法名也可以简写
xxxxxxxxxx
131const 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 函数,则要在前面加一个星号;
xxxxxxxxxx
111const obj = {
2 * myGenerator(){
3 yield 'hello world';
4 }
5};
6// 等同于
7const obj = {
8 myGenerator: function* (){
9 yield 'hello world';
10 }
11};
属性名表达式
ES6 允许用表达式作为属性名,但是一定要将表达式放在方括号内;
xxxxxxxxxx
61const obj = {
2 ["he"+"llo"](){
3 return "Hi";
4 }
5}
6obj.hello(); // "Hi"
注意点: 属性的简洁表示法和属性名表达式 不能 同时使用,会报错;
xxxxxxxxxx
111const hello = "Hello";
2const obj = {
3 [hello]
4};
5obj // SyntaxError: Unexpected token }
6
7const hello = "Hello";
8const obj = {
9 [hello+"2"]:"world"
10};
11obj // {Hello2: 'world'}
扩展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象;
基本用法
xxxxxxxxxx
31let person = {name: "Amy", age: 15};
2let someone = { person };
3someone; // {name: "Amy", age: 15}
可用于合并两个对象
xxxxxxxxxx
41let age = {age: 15};
2let name = {name: "Amy"};
3let person = {age, name};
4person; // {age: 15, name: "Amy"}
注意点
自定义属性和拓展运算符对象里面属性相同的时候:后面的属性会把前面的属性覆盖掉
xxxxxxxxxx
31let person = {name: "Amy", age: 15};
2let someone = { person, name: "Mike", age: 17};
3someone; // {name: "Mike", age: 17}
xxxxxxxxxx
31let person = {name: "Amy", age: 15};
2let someone = {name: "Mike", age: 17, person};
3someone; // {name: "Amy", age: 15}
拓展运算符后面是空对象,没有任何效果也不会报错
xxxxxxxxxx
21let a = { {}, a: 1, b: 2};
2a; // {a: 1, b: 2}
拓展运算符后面是 null 或者 undefined,没有任何效果也不会报错
xxxxxxxxxx
21let b = {null, undefined, a: 1, b: 2};
2b; // {a: 1, b: 2}
Object.assing(target,source_1,…)
用于将源对象的所有可枚举属性复制到目标对象中;
基本用法
xxxxxxxxxx
211let target = {a: 1};
2let object2 = {b: 2};
3let object3 = {c: 3};
4Object.assign(target,object2,object3);
5// 第一个参数是目标对象,后面的参数是源对象
6target; // {a: 1, b: 2, c: 3}
7
8// 如果目标独享和源对象有同名属性,或者多个源对象有同名属性,后面的属性会覆盖前面的属性
9// 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,先将该参数转为对象然后返回;
10Object.assign(3); // Number {3}
11typeof Object.assign(3); // "object"
12
13// 因为 null 和 undefined 不能转化为对象,所以会报错
14Object.assign(null); // TypeError: Cannot convert undefined or null to object
15Object.assign(undefined); // TypeError: Cannot convert undefined or null to object
16
17// 当 null 和 undefined 为源对象的参数时,会跳过,不报错
18Object.assign(1, undefined); // Number {1}
19Object.assign({a: 1}, null); // {a: 1}
20
21Object.assign(undefined, {a: 1}); // TypeError: Cannot convert undefined or null to object
注意点
assing 的属性拷贝是浅拷贝
xxxxxxxxxx
51let sourceObj = {a: {b: 1}};
2let targetObj = {c: 3};
3Object.assign(targetObj, sourceObj);
4targetObj.a.b = 2;
5sourceObj.a.b; // 2
同名属性替换
xxxxxxxxxx
51let targetObj = {a: {b: 1, c: 2}};
2let sourceObj = {a: {b: "hh"}};
3Object.assign(targetObj, sourceObj);
4targetObj; // {a: {b: "hh"}}
5// 会直接把 整个a 属性替换掉,而不是替换 a.b属性
数组的替换
xxxxxxxxxx
21Object.assign([2,3], [5]); // [5,3]
2// 会将数组处理成对象,所以先将 [2,3] 转化为 {0: 2, 1: 3},然后在进行属性复制。所以源对象的 0 号属性覆盖了目标对象的0 号属性
Object.is(value1,value2)
用来比较两个值是否严格相等,与(===)基本类似。
基本用法
xxxxxxxxxx
41Object.is("q","q"); // true
2Object.is(1,1); // true
3Object.is([1],[1]); // false
4Object.is({q:1},{q:1}); // false
与(===)的区别
xxxxxxxxxx
71// 一是 +0不等于-0
2Object.is(+0,-0); // false
3+0 === -0 // true
4
5// 二是 NaN等于本身
6Object.is(NaN,NaN); // true
7NaN === NaN // false
Array.of()
将参数中所有值作为元素形成数组
xxxxxxxxxx
71console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
2
3// 参数值可为不同类型
4console.log(Array.of(1, '2', true)); // [1, '2', true]
5
6// 参数为空时返回空数组
7console.log(Array.of()); // []
Array.from()
将 类数组对象 或 可迭代对象 转化为数组
xxxxxxxxxx
51// 参数为数组,返回和原数组一样的数组
2console.log(Array.from([1, 2])); // [1, 2]
3
4// 参数含空位
5console.log(Array.from(1, , 3)); // [1, undefined, 3]
参数
xxxxxxxxxx
11Array.from(arrayLike[, mapFn[, thisArg]])
返回值为转换后的数组
arrayLike
想要转化的 类数组对象 或 可迭代对象
xxxxxxxxxx
11console.log(Array.from([1, 2, 3])); // [1, 2, 3]
mapFn
可选,map 函数,用于对每个元素进行处理,放入数组的是 处理后 的元素
xxxxxxxxxx
11console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]
thisArg
可选,用于指定 map 函数执行时的 this 对象
xxxxxxxxxx
91let 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 属性,且元素属性名必须是数值或者可转为数值的字符
xxxxxxxxxx
231let arr = Array.from({
2 0: '1',
3 1: '2',
4 2: 3,
5 length: 3
6});
7console.log(arr); // ['1', '2', 3]
8
9// 没有 length 属性,则返回空数组
10let array = Array.from({
11 0: '1',
12 1: '2',
13 2: 3
14});
15console.log(array); // []
16
17// 元素属性名不为数值 且无法转换为数值,返回长度为 length ,元素值为 undefined 的数组
18let array1 = Array.from({
19 a: 1,
20 b: 2,
21 length: 2
22});
23console.log(array1); // [undefined, undefined]
转换可迭代对象
转换 map
xxxxxxxxxx
41let map = new Map();
2map.set('key0', 'value0');
3map.set('key1', 'value1');
4console.log(Array.from(map)); // [['key0', 'value0'], ['key1', 'value1']]
转换 set
xxxxxxxxxx
31let arr = [1, 2, 3];
2let set = new Set(arr);
3console.log(Array.from(set)); // [1, 2, 3]
转换 字符串
xxxxxxxxxx
21let str = 'abc';
2console.log(Array.from(str)); // ["a", "b", "c"]
查找
find()
查找数组中符合条件的元素,返回第一个元素
xxxxxxxxxx
51let arr = Array.of(1, 2, 3, 4);
2console.log(arr.find(item => item > 2)); // 3
3
4// 数组空位 处理为 undefined
5console.log([, 1].find(n => true)); // undefined
findIndex()
查找数组中符合条件的元素索引,返回第一个元素索引
xxxxxxxxxx
41let arr = Array.of(1, 2, 3, 4);
2// 参数1: 回调函数
3// 参数2(可选): 指定回调函数中的 this 值(可参考Array.from 的 thisArg参数)
4console.log(arr.findIndex(item => item === 3)); // 2
填充
fill()
将一定范围索引的数组元素内容 填充 为单个指定的值
xxxxxxxxxx
91let arr = Array.of(1, 2, 3, 4);
2// 参数1: 用来填充的值
3// 参数2: 被填充的起始索引
4// 参数3(可选): 被填充的结束索引,默认为数组末尾
5// 参数2 <= 填充值索引 < 参数3
6console.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()
将一定范围索引的数组元素 修改 为此数组另一指定范围索引的元素
xxxxxxxxxx
101// 参数1: 被修改的起始索引
2// 参数2: 被用来覆盖的数据的起始索引
3// 参数3(可选): 被用来覆盖的数组的结束索引,默认为数组末尾
4// 参数2 <= 覆盖值索引 < 参数3
5console.log([1, 2, 3, 4].copyWithIn(0, 2, 4)); // [3, 4, 3, 4]
6
7// 参数1为负数,表示倒数。最后一个数为 -1
8console.log([1, 2, 3, 4].copyWithIn(-2, 0)); // [1, 2, 1, 2]
9
10console.log([1, 2, , 4].copyWithIn(0, 2, 4)); // [, 4, , 4]
遍历
entries()
遍历键值对
xxxxxxxxxx
131for (let [key, value] of ['a', 'b'].entries()) {
2 console.log(key, value);
3}
4// 0 "a"
5// 1 "b"
6
7// 不使用 for...of 循环
8let entries = ['a', 'b'].entries();
9console.log(entries.next().value); // [0, "a"]
10console.log(entries.next().value); // [1, "b"]
11
12// 数组含空位
13consoel.log([ [, 'a'].entries()]); // [[0, undefined], [1, "a"]]
keys()
遍历键名
xxxxxxxxxx
81for (let key of ['a', 'b'].keys()) {
2 console.log(key);
3}
4// 0
5// 1
6
7// 数组含空位
8console.log([ [, 'a'].keys()]); // [0, 1]
values()
遍历键的值
xxxxxxxxxx
81for (let value of ['a', 'b'].values()) {
2 console.log(value);
3}
4// "a"
5// "b"
6
7// 包含空数组
8console.log([ [, 'a'].values()]); // [undefined, "a"]
包含
includes()
数组是否包含指定值
注意: 与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名
xxxxxxxxxx
51// 参数1: 包含的指定值
2// 参数2(可选): 搜索的起始索引,默认为0
3[1, 2, 3].includes(1); // true
4[1, 2, 3].includes(1, 2); // false
5[1, NaN, 3].includes(NaN); // true
嵌套数组转一维数组
flat()
默认转换一层,即去掉一个中括号
xxxxxxxxxx
101console.log([1, [2, 3]].flat()); // [1, 2, 3]
2
3// 指定转换的嵌套层数
4console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
5
6// 不管嵌套多少层
7console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]
8
9// 自动跳过空位
10console.log([1, [2, , 3]].flat()); // [1, 2, 3]
flatMap()
先对数组中的每个元素进行了处理,再对数组执行 flat() 方法
xxxxxxxxxx
31// 参数1: 遍历函数,该遍历函数可接受3个参数:当前元素,当前元素索引,原数组
2// 参数2: 指定遍历函数中 this 的指向
3console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]
复制数组
xxxxxxxxxx
81let arr = [1, 2];
2let arr1 = [arr];
3console.log(arr1); // [1, 2]
4
5// 数组含空位
6let arr2 = [1, , 3];
7let arr3 = [arr2];
8console.log(arr3); // [1, undefined, 3]
合并数组
xxxxxxxxxx
11console.log([ [1, 2], [3, 4]]); // [1, 2, 3, 4]
基本用法
xxxxxxxxxx
61function fn(name, age = 17) {
2 console.log(name + "," + age);
3}
4fn("Amy",18); // Amy,18
5fn("Amy",""); // Amy,
6fn("Amy"); // Amh,17
注意点:使用函数默认参数时,不允许有同名参数
xxxxxxxxxx
101// 不报错
2function fn(name, name) {
3 console.log(name);
4}
5
6// 报错
7// SyntaxError: Duplicate parameter name not allowed in this context
8function fn(name, name, age=17) {
9 console.log(name+","+age);
10}
只有在未传递参数,或者参数为 nudefined 时,才会使用默认参数。null 值被认为是有效的值传递
xxxxxxxxxx
41function fn(name, age=17) {
2 console.log(name+","+age);
3}
4fn("Amy",null); // Amy,null
函数默认参数存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值
xxxxxxxxxx
91function f(x, y=x) {
2 console.log(x,y);
3}
4f(1); // 1 1
5
6function f(x=y) {
7 console.log(x);
8}
9f(); // ReferenceError: y is not defined
不定参数用来表示不确定参数个数,形如,…变量名。由...加上一个具名参数标识符组成
具名参数只能放在参数组的最后,并且有且只有一个不定参数
xxxxxxxxxx
61function f(values) {
2 console.log(values.length);
3}
4
5f(1,2); // 2
6f(1,2,3,4); // 4
箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
xxxxxxxxxx
11参数 => 函数体
基本语法
xxxxxxxxxx
61var f = v => v;
2// 等价于
3var f = function(a) {
4 return a;
5}
6f(1); // 1
当箭头函数没有参数或者有多个参数,要用 () 括起来
xxxxxxxxxx
21var f = (a,b) => a+b;
2f(6,2); // 8
当箭头函数函数体有多行语句,要用 {} 括起来,表示代码块。当只有一行语句,并且需要返回结果时,可以省略 {} ,结果会自动返回
xxxxxxxxxx
51var f = (a,b) => {
2 let result = a+b;
3 return result;
4}
5f(6,2); // 8
当箭头函数要返回对象的时候,为了区分与代码块,要用 () 将对象包裹起来
xxxxxxxxxx
71// 报错
2var f = (id,name) => {id: id, name: name};
3f(6,2); // SyntaxError: Unexpected token :
4
5// 不报错
6var f = (id,name) => ({id: id, name: name});
7f(6,2); // {id: 6, name: 2}
注意点:没有 this、super、arguments 和 new.target 绑定
xxxxxxxxxx
111var func = () => {
2 // 箭头函数里没有 this 对象
3 // 此时的 this 是外层的 this 对象,即 Window
4 console.log(this);
5}
6func(55); // Window
7
8var func = () => {
9 console.log(arguments);
10}
11func(55); // ReferenceError: arguments is not defined
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象
xxxxxxxxxx
91function 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 的上下文 的时候,使用 箭头函数
xxxxxxxxxx
221// 回调函数
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(); // 20
12
13var 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
xxxxxxxxxx
191var Person = {
2 'age': 18,
3 'sayHello': function(){
4 console.log(this.age);
5 }
6}
7var age = 20;
8Person.sayHello(); // 18
9 // 此时的 this 指 Person 对象
10
11var Person1 = {
12 'age': 18,
13 'sayHello': ()=>{
14 console.log(this.age);
15 }
16}
17var age = 20;
18Person1.sayHello(); // 20
19 // 此时的 this 指 外层的 Window 全局对象
需要动态 this 的时候
xxxxxxxxxx
61var 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 命令