文章目录
ES6:2015年推出的
ECMAScript
的语法规范
let/const声明变量
- ES6中新增了声明变量的关键字
let/const
let/const
和var
的区别let/const
声明的变量必须在声明后使用var
声明的变量会被预解析,可以在声明前使用let/const
声明的变量因为有暂时性死区的存在,不能在声明前使用
console.log(x); // undefined var x = 1; console.log(y); // 报错:'Cannot access 'y' before initialization' let y = 2; console.log(z); // 报错:'Cannot access 'z' before initialization' const z = 3;
let/const
不能重复声明同名变量var
可以重复声明同名变量
var x = 0; var x = 1; console.log(x); // 1 let y = 2; let y = 3; console.log(y); // 报错:'Identifier 'y' has already been declared' const z = 4; const z = 5; console.log(z); // 报错:'Identifier 'z' has already been declared'
- 任何一个
{}
都会限制let/const
声明的变量的使用范围- 对于
let/const
声明的变量来说,任何一个{}
都是一个作用域 var
声明的变量只有函数的{}
才会限制使用范围
- 对于
if (true) { var a = 1; let b = 2; const c = 3; console.log(a, b, c); // 1 2 3 } console.log(a); // 1 console.log(b); // 报错:'b is not defined' console.log(c); // 报错:'c is not defined' for (var i = 0; i <= 3; i++) { console.log(i); // 0 1 2 3 setTimeout(function() { // 此代码执行时,for循环已经结束,全局变量i的值为4 console.log(i); // 4 }, 500); } console.log(i); // 4 for (let i = 0; i <= 3; i++) { // 对于变量i来说,for循环的{}就是一个块级作用域 console.log(i); // 0 1 2 3 setTimeout(function() { console.log(i); // 0 1 2 3 }, 500); } console.log(i); // 报错:'i is not defined'
let/const
声明的变量不会挂载到window
中- 在全局中通过
var
声明的变量,本质上相当于是给window
添加的属性- 也就是说,在全局中通过
var
声明的变量都会自动挂载到window
中
- 也就是说,在全局中通过
- 然而在全局中通过
let/const
声明的变量,不会挂载到window
中
- 在全局中通过
var a = 100; let b = 200; const c = 300; console.log(window.a, window.b, window.c); // 100 undefined undefined
let
和const
的区别let
声明变量的时候可以不赋值const
声明变量的时候必须赋值
let x; console.log(x); // undefined const y; // 报错:'Missing initializer in const declaration'
let
声明变量后可以修改变量的值const
声明变量后不可修改变量的值- 因此
const
声明的变量也可叫做常量
let n = 100; n = 200; console.log(n); // 200 // 情况1 const m = 100; m = 200; // 报错:'Assignment to constant variable' // 情况2 // 变量obj中的值是对象的地址 const obj = { a: 1 } // 根据obj中的地址,找到存储对象并修改其中的数据 obj.a = 666; // 没有修改obj中的值,值还是那个地址 console.log(obj); // { a: 666 } // 此操作会将obj中的地址替换为别的地址 obj = { a: 666 } // 报错:'Assignment to constant variable'
模板字符串
- 反引号包裹的字符串
- 特点:
- 可以换行书写
- 可以识别解析变量
${变量}
let n = 10;
let m = 20;
let str = `n的值为${n},m的值为${m}`;
console.log(str); // n的值为10,m的值为20
- 扩展:使用模板字符串调用函数
function fn(arg, n1, n2) {
// 第一个参数是一个数组,是由模板字符串通过${}分隔后组成的数组
console.log(arg, n1, n2);
}
let x = 666;
let y = 999;
// 使用模板字符串调用
fn`I${x}LOVE${y}U`; // ['I', 'LOVE', 'U', raw: Array(3)] 666 999
fn`I LOVE U`; // ['I LOVE U', raw: Array(1)] undefined undefined
函数形参默认值
- 在定义函数的时候
()
中直接给形参赋的值,就是形参的默认值 - 形参默认值的作用
- 函数调用的时候,如果没有给形参赋值实参,则函数内该形参使用默认值
- 如果给形参传递了实参,则函数内该形参使用传递的实参值
function fn(n = 10, m = 20, x) {
console.log(n, m, x);
}
fn(); // 10 20 undefined
fn(666, 777, 888); // 666 777 888
对象简写
- 当对象的属性名和属性值变量同名的时候,可以简写
- 比如:
let n = 100; let obj = { n: n }
- 属性名为
n
,属性值是变量n
的值
- 属性名为
- 简写:
let n = 100; let obj = { n }
- 比如:
- 当对象的属性值是匿名函数的时候,可以简写
- 比如:
let obj = { fn: function() {} }
- 属性值为匿名函数
- 简写:
let obj = { fn() {} }
- 属性名就是fn,值为匿名函数
- 比如:
箭头函数
- 是函数表达式(赋值式函数)中匿名函数的简写
- 语法:
let 变量 = () => {}
()
书写函数形参的位置=>
箭头函数的标志(不能省略){}
书写函数代码的位置
- 语法:
let ff1 = function(n, m) { console.log(n + m); }
let ff2 = (n, m) => { console.log(n + m); }
ff1(10, 20); // 30
ff2(10, 20); // 30
- 箭头函数的特点
- 如果函数只有一个形参则可以省略
()
- 注意:如果形参有默认值则不能省略
()
- 注意:如果形参有默认值则不能省略
let fn1 = n => {} // 只有一个形参可以省略() let fn2 = (n = 1) => {} // 形参有默认值不能省略()
- 如果函数只有一行代码则可以省略
{}
- 并且会自动将这行代码的结果作为函数返回值返回,所以不用写
return
- 并且会自动将这行代码的结果作为函数返回值返回,所以不用写
let fn = (n, m) => n + m; console.log(fn(10, 20)); // 30 // 在数组方法中经常使用 let r1 = [1, 2, 3, 4].map(v => v * 10); console.log(r1); // [10, 20, 30, 40] let r2 = [1, 2, 3].every(v => v > 0); console.log(r2); // true let r3 = [1, 2, 3].reduce((prev, v) => prev + v, 0); console.log(r3); // 6
- 箭头函数内没有
arguments
function fn1() { console.log(arguments); // [10, 20, 30, callee: ƒ...] } fn1(10, 20, 30); let fn2 = () => { console.log(arguments); // 报错:'arguments is not defined' } fn2(10, 20, 30);
- 箭头函数内的
this
指向问题(没有自己的this
)- 箭头函数内的
this
与函数的定义(书写)位置有关,与函数的调用方式无关 - 箭头函数内的
this
与其所在的作用域中的this
指向一样 - 注意:
call()/apply()/bind()
等方法不能改变箭头函数内的this
指向
- 箭头函数内的
let fn = () => { console.log(this); } fn(); // window let obj = { a: 1 }; obj.f = fn; obj.f(); // window document.onclick = fn; // window fn.call({ a: 666 }); // window document.onclick = function() { console.log(this); // document let f = () => { console.log(this); // document } f(); }
- 箭头函数不能作为构造函数使用
- 箭头函数内没有
prototype
原型
- 如果函数只有一个形参则可以省略
扩展运算符
- 扩展运算符具有两个作用
- 作为展开运算符使用
- 作用:展开大部分数据
- 比如:对象、字符串、数组、伪数组等
- 语法:
...数据
- 注意:展开后的数据必须使用在合适的地方
- 作用:展开大部分数据
// 1. 展开对象(只能用于对象{}中) let obj1 = { a: 1, b: 2 } let o1 = { c: 3, ...obj1 } console.log(o1); // { c: 3, a: 1, b: 2 } let obj2 = { a: 1, b: 2 } let obj3 = { c: 3, d: 4 } let o2 = { ...obj2, ...obj3 } console.log(o2); // { a: 1, b: 2, c: 3, d: 4 }
// 2. 展开字符串 let str = 'qwertyu'; let arr = [...str]; console.log(arr); // ['q', 'w', 'r', 't', 'y', 'u'] function fn() { console.log(arguments); // Arguments(6) } // 将str展开,作为fn函数调用的实参使用 fn(...str); // 等价于 fn('q','w','e','r','t','y')
// 3. 展开数组 let arr1 = ['q', 'w', 'r', 't', 'y']; let newArr = [1, 2, 3, ...arr1]; console.log(newArr); // [1, 2, 3, 'q', 'w', 'r', 't', 'y'] let arr2 = ['q', 'w', 'r']; let arr3 = [1, 2, 3, 4]; // 使用concat console.log(arr2.concat(arr3)); // ['q', 'w', 'r', 1, 2, 3, 4] // 使用展开运算符 console.log([...arr2, ...arr3]); // ['q', 'w', 'r', 1, 2, 3, 4] let arr4 = [1, 2, 3, 4, 5]; function fn() { console.log(arguments); // Arguments(5) } // 将arr4展开,作为fn函数调用的实参使用 fn(...arr4); // 等价于:fn(1, 2, 3, 4, 5) console.log(Math.max(...arr4)); // 5
// 4. 展开伪数组 let btns = document.querySelectorAll('button'); let arr5 = Array.from(btns); console.log(arr5); // [button, button, button] let arr6 = [...btns]; console.log(arr6); // [button, button, button]
// 扩展 let obj = { 0: 'a', 1: 'b', length: 2 } console.log(Array.from(obj)); // ['a', 'b'] console.log([...obj]); // 报错:'obj is not iterable'
- 作为合并运算符使用
- 只有当函数定义的时候在函数形参位置使用才是合并运算符
- 作用:将接收到的实参合并为一个数组
- 比如:
function fn(...arg) {}
- 在本次函数执行中,函数内的
arg
就是接收的实参组成的数组
- 在本次函数执行中,函数内的
- 比如:
- 注意:合并运算符只能作用于最后一个形参
function ff(...arg) { console.log(arg); } ff(1, 2, 3); // [1, 2, 3] ff(); // [] function ff(a, b, ...arg) { console.log(a, b); // 1 2 console.log(arg); // [3, 4, 5] } ff(1, 2, 3, 4, 5); function ff(a, b, ...arg, c, d) {} // 报错:'Rest parameter must be last formal parameter' // ...合并运算符可以缓解箭头函数内没有arguments的问题 let ff = (...arg) => { console.log(arg); // [10, 20, 30, 40, 50] } ff(10, 20, 30, 40, 50);
- 作为展开运算符使用
解构赋值
- 解析数据结构并赋值
- 作用:快速获取数据
- 数组解构
- 语法:
let [变量名, ..., 变量名] = 数组
- 等号
=
左边的[]
是解构的含义
- 等号
- 作用:将数组中的数据依次赋值给左边的变量
- 注意:等号
=
右边的只要是可迭代数据即可
- 语法:
let arr1 = ['a', 'b', 'c', 'd'];
let [a1, a2] = arr1; // 等价于 let a1 = arr1[0] let a2 = arr1[1]
console.log(a1, a2); // 'a' 'b'
let [b1, b2, b3, b4, b5] = arr1;
console.log(b1, b2, b3, b4, b5); // 'a' 'b' 'c' 'd' undefined
let [n1, n2, ...ar] = arr1; // 将剩余的数据合并到ar数组中
console.log(n1, n2, ar); // 'a' 'b' ['c', 'd']
// 使用数组解构交换两个变量的值
let c1 = 100;
let c2 = 200;
[c2, c1] = [c1, c2]; // 等号右边是数组,左边是解构赋值
console.log(c1, c2); // 200 100
let [d] = 1234; // 报错:'1234 is not iterable'
// 解构多维数组
let arr2 = [10, [20, [30, [40, 50]]]];
let [e1, e2] = arr2;
console.log(e1); // 10
console.log(e2); // [20, [30, [40, 50]]]
let [f1, [f2, [f3, [f4, f5]]]] = arr2;
console.log(f1, f2, f3, f4, f5) // 10 20 30 40 50
- 对象解构
- 语法1:
let { 属性名, ..., 属性名 } = 对象
- 等号
=
左边的{}
是解构的含义
- 等号
- 作用:将对象中的属性名对应的属性值,赋值给左边的属性名同名变量
- 比如:
let { age } = 对象
等价于let age = 对象.age
- 比如:
- 语法2:
let { 属性名: 变量, ..., 属性名: 变量 } = 对象
- 等号
=
左边的{}
是解构的含义
- 等号
- 作用:将对象中的属性名对应的属性值,赋值给左边的属性名同名变量
- 比如:
let { age: userAge } = 对象
等价于let userAge = 对象.age
- 比如:
- 注意:如果等号
=
左边没有声明变量的关键字则需要将整个表达式用()
包裹- 比如:
let age; ({ age } = 对象);
- 比如:
- 语法1:
let obj = { name: 'zs', age: 17 }
let { name, age } = obj; // 等价于 let name = obj.name let age = obj.age
console.log(name, age); // 'zs' 17
let { gender } = obj; // 等价于 let gender = obj.gender
console.log(gender); // undefined
let { age: userAge } = obj; // 等价于 let userAge = obj.age
console.log(userAge); // 17
console.log(age); // 报错:'age is not defined'
// 解构多级对象
let user = {
userName: 'zs',
info: {
age: 18,
gender: '男'
}
}
let { userName, info } = user;
console.log(userName, info); // 'zs' { age: 18, gender: '男' }
let { userName, info: { age, gender } } = user;
console.log(userName, age, gender); // 'zs' 18 '男'
console.log(info); // 报错:'info is not defined'
// 如果解构对象的时候,没有使用声明变量的关键字,则需要将整个解构赋值用()包裹
let user = { age: 17, userName: 'zs' }
let age, userName;
({ age, userName } = user);
console.log(age, userName); // 17 'zs'
// 利用解构赋值获取事件对象身上的属性
document.onclick = function(e) {
let { target, type, offsetX } = e;
console.log(target, type, offsetX);
}
document.onclick = function({ target }) {
console.log(target);
}
- 扩展:在js中
{}
的含义- 表示对象,存储键值对
- 表示解构赋值
- 块级代码作用域
严格模式
- 是一种js代码开始模式
- 早期js语法有较多的不合理、不严谨之处
- 为了更好的版本迭代(为了给后续的新语法做铺垫),于是就推出了严格模式
- 如何开启严格模式:
- 在代码开始位置书写:
'use strict';
- 在代码开始位置书写:
- 在严格模式下:
- 函数普通调用时(直接
函数名()
)- 函数内的
this
不再指向window
,而是指向undefined
- 函数内的
function fn() { console.log(this); // undefined } fn();
- 函数形参名不能重复
// 严格模式下报错 function fn(a, a) {}
- 函数中的
arguments.callee
不能使用
// 严格模式下报错 function fn() { console.log(arguments.callee); } fn();
- 不能使用隐式全局变量(变量必须先通过关键字声明才可以使用)
// 严格模式下报错 n = 100;
- 不能使用0开头的八进制数
// 严格模式下报错 let n1 = 0o12; let n2 = 012; console.log(n1, n2);
- 不能设置只读属性
// 严格模式下报错 let str = 'qwerty'; str.length = 666;
- 函数普通调用时(直接
Symbol数据类型
- 是ES6中新增的一种基本数据类型
- 特点:
Symbol
数据是唯一的
- 创建
Symbol
数据- 语法1:
Symbol()
let s1 = Symbol(); let s2 = Symbol(); console.log(s1, s2); // Symbol() Symbol() console.log(s1 == s2); // false
- 语法2:
Symbol(描述)
let s1 = Symbol('index'); let s2 = Symbol('index'); console.log(s1, s2); // Symbol(index) Symbol(index) console.log(s1 == s2); // false
- 语法3:
Symbol.for(描述)
- 在全局
Symbol
注册表中查找拥有相同描述的Symbol
数据,如果有则返回- 如果没有则创建并返回
- 在全局
let s1 = Symbol.for('index'); console.log(s1); // Symbol(index) let s2 = Symbol.for('index'); console.log(s2); // Symbol(index) console.log(s1 == s2); // true
- 语法1:
- 检测
Symbol
数据类型- 直接使用
typeof
检测
let s = Symbol('index'); console.log(typeof s); // 'symbol'
- 直接使用
Symbol
数据无法隐式转换,必须使用方法强制转换let s = Symbol('index'); console.log('abc' + String(s)); // 'abcSymbol(index)' console.log('abc' + s); // 报错:'Cannot convert a Symbol value to a string'
- 获取
Symbol
数据的描述- 语法:
Symbol数据.description
- 返回值:
Symbol
数据的描述
let s = Symbol('index'); console.log(s.description); // 'index'
- 语法:
- 使用
Symbol
数据作为对象的属性名- 注意:
- 使用
Symbol
数据作为对象的属性名,则需要使用[]
包裹 Symbol
作为对象的属性名不能被for-in
遍历
- 使用
let va = Symbol('index'); let obj = { num: 17, [va]: 'aaa', [Symbol('index')]: 'bbb' } console.log(obj); // { num: 17, Symbol(index): 'aaa', Symbol(index): 'bbb' } for (const key in obj) { console.log(key); // 'num' }
- 注意:
set数据类型
- 是ES6中新增的一种复杂数据类型
set
是数据集合,与数组相似set
集合中的数据是无序的set
集合中的数据是唯一的、不重复的
- 注意:
set
集合中NaN
和NaN
是一样的数据
- 创建
set
集合- 语法:
new Set(可迭代数据)
let s1 = new Set(); console.log(s1); // 空set集合 let s2 = new Set('abcde'); console.log(s2); // { 'a', 'b', 'c', 'd', 'e' } let s3 = new Set([1, 2, 3, 4]); console.log(s3); // { 1, 2, 3, 4 } let s4 = new Set([1, 2, 3, 3, 3, 3, 4, NaN, NaN]); console.log(s4); // { 1, 2, 3, 4, NaN }
- 语法:
set
集合的数据操作API方法- 语法:
set集合.size
- 作用:返回
set
集合中的数据个数
- 作用:返回
let s = new Set([10, 20, 30]); console.log(s.size); // 3
- 语法:
set集合.add(数据)
- 作用:向
set
集合中添加数据 - 返回值:添加数据后的
set
集合
- 作用:向
let s = new Set([10, 20, 30]); console.log(s.add(30)); // { 10, 20, 30 } console.log(s.add('30')); // { 10, 20, 30, '30' } console.log(s.add(NaN)); // { 10, 20, 30, '30', NaN } console.log(s); // { 10, 20, 30, '30', NaN }
- 语法:
set集合.has(数据)
- 作用:判断
set
集合中是否存在传入的数据 - 返回值:布尔值,存在返回
true
,不存在返回false
- 作用:判断
let s = new Set([10, 20, 30, { a: 1 }]); console.log(s.has(10)); // true console.log(s.has(30)); // true console.log(s.has('30')); // false console.log(s.has({ a: 1 })); // false
- 语法:
set集合.delete(数据)
- 作用:删除传入数据对应的
set
集合中的数据 - 返回值:布尔值,成功删除返回
true
,没有删除返回false
- 作用:删除传入数据对应的
let s = new Set([10, 20, 30, { a: 1 }]); console.log(s.delete(10)); // true console.log(s.delete(20)); // true console.log(s.delete({ a: 1 })); // false console.log(s); // { 30, {…} }
- 语法:
set集合.clear()
- 作用:清空
set
集合中的所有数据
- 作用:清空
let s = new Set([10, 20, 30, { a: 1 }]); console.log(s); // { 10, 20, 30, {…} } s.clear(); console.log(s); // 空set集合
- 语法:
set集合.forEach(函数)
- 作用:遍历
set
集合中的所有数据set
集合中有多少个数据则执行多少次传入的函数
- 作用:遍历
let s = new Set([10, 20, 30, { a: 1 }]); s.forEach(function(v, i, s) { // v, i 表示的都是set集合中的数据(值相同) // s 表示调用forEach方法的set集合 console.log(v, i, s); });
- 语法:
set
集合和数组的转换- 数组转换为
set
集合
let arr = [10, 20, 30, 40, 20, 30]; let s = new Set(arr); console.log(s); // {10, 20, 30, 40}
set
集合转换为数组
console.log(Array.from(s)); // [10, 20, 30, 40] console.log([...s]); // [10, 20, 30, 40]
- 数组转换为
set
集合的使用- 需求:三个按钮都点击后才可以进行后续操作
<button>1</button> <button>2</button> <button>3</button>
let s = new Set(); let btns = document.querySelectorAll('button'); btns.forEach(ele => ele.onclick = () => { s.add(ele); if (s.size == btns.length) { console.log('三个按钮都点击了,可以继续后续操作'); } });
map数据类型
- 是ES6中新增的一种复杂数据类型
map
是数据集合,与对象相似map
集合中数据也是键值对存储map
集合与对象的区别- 对象的键名(属性名)只能是字符串或
Symbol
类型数据map
集合中的键名(属性名)可以是任意类型数据
- 对象通过点语法或数组关联法进行数据操作
map
集合通过自带的API方法进行数据操作
- 对象的键名(属性名)只能是字符串或
- 注意:
map
集合中NaN
和NaN
是一样的键名
- 创建
map
集合- 语法:
new Map(可迭代数据)
- 一般创建
map
集合传入的参数是一个二维数组 - 在每个小数组中:
- 第一个数据作为
map
集合中的键名(属性名) - 第二个数据作为
map
集合中对应的键值(属性值)
- 第一个数据作为
- 一般创建
let m1 = new Map(); console.log(m1); // 空map集合 let m2 = new Map([[10, 20], ['10', 20], [{ a: 10 }, 20]]); console.log(m2); // { 10 => 20, '10' => 20, {…} => 20 }
- 语法:
map
集合的数据操作API方法- 语法:
map集合.size
- 作用:返回
set
集合中的数据个数
- 作用:返回
let m = new Map([[10, 20], ['10', 20], [{ a: 10 }, 20]]); console.log(m.size); // 3
- 语法:
map集合.set(键名, 键值)
- 作用:设置
map
集合中的键值对 - 返回值:操作后的
map
集合
- 作用:设置
let m = new Map([[10, 20], [{ a: 10 }, 20]]); m.set(10, '666'); m.set({ a: 10 }, 999); m.set(NaN, 123); m.set(NaN, 456); console.log(m); // { 10 => '666', {…} => 20, {…} => 999, NaN => 456 } console.log(m.set(true, '789')); // { 10 => '666', {…} => 20, {…} => 999, NaN => 456, true => '789' }
- 语法:
map集合.get(键名)
- 作用:获取键名在
map
集合中的键值 - 返回值:键名对应的键值,如果不存在则返回
undefined
- 作用:获取键名在
let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]); console.log(m.get(10)); // 20 console.log(m.get('10')); // 80 console.log(m.get({ a: 10 })); // undefined console.log(m.get(100)); // undefined
- 语法:
map集合.has(键名)
- 作用:判断键名在
map
集合中是否存在 - 返回值:布尔值,存在返回
true
,不存在返回false
- 作用:判断键名在
let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]); console.log(m.has(10)); // true console.log(m.has('10')); // true console.log(m.has({ a: 10 })); // false console.log(m.has(100)); // false
- 语法:
map集合.delete(键名)
- 作用:根据键名删除
map
集合中的数据 - 返回值:布尔值,成功删除返回
true
,没有删除返回false
- 作用:根据键名删除
let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]); console.log(m.delete(10)); // true console.log(m.delete(100)); // false console.log(m.delete({a:10})); // false console.log(m); // { '10' => 80, {…} => 20 }
- 语法:
map集合.clear(键名)
- 作用:清空
map
集合中的所有数据
- 作用:清空
let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]); console.log(m); // { 10 => 20, '10' => 80, {…} => 20 } m.clear(); console.log(m); // 空map集合
- 语法:
map集合.forEach(函数)
- 作用:遍历
map
集合中的所有数据map
集合中有多少个键值对则执行多少次传入的函数
- 作用:遍历
let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 100]]); s.forEach(function(v, k, m) { // v 表示map集合中的数据(键值) // k 表示map集合中该数据对应的键名 // m 表示调用forEach方法的map集合 console.log(v, k, m); });
- 语法:
WeakSet与WeakMap
WeakSet
与WeakMap
对应set
与map
,数据类型、作用以及使用都是一模一样- 区别:
WeakSet
与WeakMap
只能存储复杂类型数据WeakMap
的键名只能存储复杂类型数据,键值不受限制
let ws = new WeakSet(); ws.add(10); // 报错:'Invalid value used in weak set' let wm = new WeakMap(); wm.set(10); // 报错:'Invalid value used as weak map key'
WeakSet
与WeakMap
中的数据是弱引用,而set
与map
中的数据是强引用- 数组、对象、
set
、map
等集合中存储的复杂类型数据是强引用
- 数组、对象、
let o1 = { a: 1 }; o1 = null; // 此时{ a: 1 }没有任何内容对其引用,会被js垃圾回收机制销毁(回收) let arr = []; let o2 = { a: 2 }; arr[0] = o2; o2 = null; // 此时虽然o2不再引用{ a: 1 },但是arr[0]依旧引用了{ a: 1 } // 所以{ a: 1 }不会被js垃圾回收机制销毁(回收)
let arr = []; let ws = new WeakSet(); let o1 = { a: 1 }; let o2 = { a: 2 }; arr[0] = o1; ws.add(o2); o1 = null; o2 = null; // 此时a[0]引用了{ a: 1 },是强引用,所以不会被js垃圾回收机制销毁(回收) // 此时ws引用了{ a: 2 },但是是弱引用,所以会被js垃圾回收机制销毁(回收)
for-of语句
- 是ES6中新增的遍历可迭代数据的语句
- 语法:
for (变量 of 可迭代数据) { 循环获取可迭代数据中的数据 }
- 常见的可迭代数据:
- 数组、伪数组、字符串、
set
集合、map
集合
- 数组、伪数组、字符串、
- 判断数据结构是否可迭代的小技巧
- 如果数据结构中具有
Symbol.iterator
属性,则一般都是可迭代数据
- 如果数据结构中具有
// 遍历数组
let arr = [10, 20, 30];
for (const v of arr) {
console.log(v); // 10 20 30
}
// 遍历字符串
let str = 'qwert';
for (const v of str) {
console.log(v); // 'q' 'w' 'e' 'r' 't'
}
// 遍历set集合
let s = new Set([10, 20, 30, 40, 40, 40]);
for (const v of s) {
console.log(v); // 10 20 30 40
}
// 遍历map集合
let m = new Map([[10, 20], [30, 40], [40, 40]]);
m.set(true, 666);
m.set({ a: 1 }, 999);
for (const v of m) {
console.log(v);
// [10, 20] [30, 40] [40, 40] [true, 666] [{…}, 999]
}
for-of
与for-in
的区别for-of
适用于可迭代数据for-in
适用于可枚举属性
let arr = [10, 20, 30];
arr.abc = '666';
for (const v of arr) {
console.log(v); // 10 20 30
}
for (const k in arr) {
console.log(k); // '0' '1' '2' 'abc'
console.log(arr[k]); // 10 20 30 '666'
}
console.log(arr); // [10, 20, 30, abc: '666']
模块化语法
- 将逻辑复杂的代码,分为多个模块单独书写
- 使用的时候,导入对应的模块实现功能
- 模块化的优点
- 方便多人协同开发
- 便于代码复用以及后续维护
- 对于代码封装更友好,使得代码的变量不会被污染
- 使用模块化的前提条件
- 使用模块化语法的
script
标签必须要有type
属性,属性值为module
- 使用模块化语法的页面必须通过服务器访问
- 使用模块化语法的
- 模块化语法
- 在js模块化中,一个js文件就是一个模块,每个模块之间都是相互独立的
- 每个独立的模块都是一个模块作用域,各自模块中的数据只能在当前模块中使用
- 如果想在当前模块中使用其他模块中的数据,则需要用到模块的导入导出语法
- 模块导出语法
- 作用:向外部暴露当前模块中的数据
- 使其他模块在导入当前模块的时候可以使用暴露的数据
- 语法1:默认导出
export default 数据
- ”默认导出“一个模块中只能导出一次
- 语法2:常规导出
export let 变量 = 数据
export { 变量 }
- 变量必须是当前模块中的变量,可以导出多次
- 作用:向外部暴露当前模块中的数据
let num = 100; let fn = () => { console.log(num) }; // 默认导出 export default { n: num }; // 常规导出 export let age = num; export { num, fn };
- 模块导入语法
- 作用:接收导入模块中导出的数据
- 导入模块中的代码会立即执行
- 语法1:
import 变量 from 模块
- 变量接收的是导入模块中”默认导出“的数据
- 语法2:
import { 变量, ..., 变量 } from 模块
- 变量名必须是导入模块中”常规导出“的变量名
- 语法3:
import * as 变量 from 模块
- 变量接收导入模块中所有导出的数据
- 变量是一个对象,对象中的
default
属性的值就是”默认导出“的数据
- 变量是一个对象,对象中的
- 变量接收导入模块中所有导出的数据
- 语法4:
import 模块
- 仅仅为了执行导入模块中的代码,不接收导入模块中导出的数据
<script src="模块" type="module"></script>
- 可以使用html中的
script
标签导入模块
- 可以使用html中的
- 作用:接收导入模块中导出的数据
// 语法1 import moduleA1 from './modules/a.js'; console.log(moduleA1); // { n: 100 } // 语法2 import { age, num, fn } from './modules/a.js'; console.log(age, num); // 100 100 fn(); // 100 // 语法1 + 语法2 import moduleA2, { age, num, fn } from './modules/a.js'; console.log(moduleA2, age, num); // { n: 100 } 100 100 fn(); // 100 // 语法3 import * as moduleA3 from './modules/a.js'; console.log(moduleA3.default); // { n: 100 } console.log(moduleA3.age); // 100 console.log(moduleA3.num); // 100 console.log(moduleA3.fn()); // 100 // 语法4 import './modules/a.js';
<!-- 使用script标签导入模块 --> <script src="./modules/a.js" type="module"></script>
- 在js模块化中,一个js文件就是一个模块,每个模块之间都是相互独立的
- 注意:
- 模块化语法中默认开启严格模式
- ES6的模块化是静态导入的
类语法(class)
class
的作用与构造函数一样- 都是用于实例化对象(创建新对象)的
class
的类型本质上也是函数- 但是
class
只能和new
一起使用来创建对象,不能单独调用
- 但是
class
创建语法
class 类名{
// 构造器方法,在实例化类的时候会执行,并且该方法函数中的this指向实例对象
constructor(参数) {
// 参数接收实例化对象时传递的实参
// 一般都会在此方法中给实例对象添加属性
}
// 此处书写的方法是给prototype原型添加的方法
方法名() {}
// 静态属性方法 => 给类对象添加的属性(注意不是实例对象)
static 属性名 = 值;
}
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
this.ff = () => this;
}
say() {
console.log('hi');
}
static eat = function() { console.log('food'); }
}
// 给Person.prototype原型添加属性方法
Person.prototype.like = function() { console.log('ctrl'); }
let p = new Person('zs', 17);
console.log(p); // Person { name: 'zs', age: 17, ff: ƒ }
console.log(p.ff() === p); // true
console.log(p.say()); // 'hi'
console.log(p.like()); // 'ctrl'
console.log(Person.eat()); // 'food'
console.log(Person.prototype); // { like: ƒ, constructor: ƒ, say: ƒ }
// say方法和constructor一样不可枚举
for (const key in p) {
console.log(key); // name age ff like
}
- 注意:
class
内部代码默认开启严格模式