let/const命令
let命令
- 用来声明变量;
- 用法类似于var;
- let实际上为Javascript新增了块级作用域
区别于var:
- let命令所声明的变量只在它所在的代码块内有效
- 适用场景:for循环的计数器,计数器i只在for循环体内有效(let循环有奇效)
- 不存在变量提升,即变量需在声明后才能使用
- 不允许在相同作用域内,重复生命同一个变量
cosnt命令
- 用来声明一个只读的常量;
- 一旦声明就必须立即初始化,不能留到以后赋值
- 作用域与let命令相同
注意:将对象声明为常量时,const只能保证指向对象的地址,对象本身仍是可写的
拓展:为保证对象完全冻结,常用freeze()冻结方法:const foo = Object.freeze()
新增数据类型
Symbol
记住两个apiSymbol([description])
每次都创建新的唯一属性Symbol.for(key)
使用给定的 key 搜索现有的 symbol,如果找到则返回该 symbol。否则将使用给定的 key 在全局 symbol 注册表中创建一个新的 symbol
用来做特定场景下 怕重名引起key覆盖的场景,特别在底层框架当中,为了避免key是通过动态计算而被覆盖而使用Symbol
Bigint
表示任意大的整数;它提供了一种方法来表示大于 2^53 - 1 的整数BigInt(1) === 1n
常用于金融业务
- 由于IE的支持不太好,并且当使用 BigInt 时,带小数的运算会被取整,所以实际开发中建议大家使用第三方库来处理
- 官方网站:GitHub - GoogleChromeLabs/jsbi: JSBI is a pure-JavaScript implementation of the official ECMAScript BigInt proposal.
解构赋值
数组解构赋值
let [a, b, c] = [1, 2, 3];
//可以从数组中提取值,按照一 一对应位置,对变量赋值
本质上属于“模式匹配”:只要等号两边的模式相同,左边的变量就会被赋予右边对应模式的值
let [foo] = [];
let [bar, foo] = [1];
//解构不成功,变量值为undefined
应用场景:
当拿到的一系列数据存储在csv文件中时,如const info = ‘npc,12,3333’
需要将这些纯文本字符串转换为数组,const lastInfo = info.split(‘,’)
然后再从该数组中拿出出所需的值
or
排序时互换顺序
数组默认值的设置
前提:
ES6内部使用严格相等运算符===,当一个数组成员严格等于undefined,默认值才会生效(null不严格等于undefined)
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError: y is not defined
对象的解构赋值(常用)
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
与数组解构赋值的重要区别:
数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
适用场景:方便地将现有对象的方法,赋值到某个变量上
取别名
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
//foo是匹配的模式,baz才是变量。
真正被赋值的是变量baz,
而不是模式foo。
对象默认值的设置
前提:
ES6内部使用严格相等运算符===,当一个对象属性严格等于undefined,默认值才会生效(null不严格等于undefined)
应用场景:当后端传递过来的对象中没有xxx属性,即const {xxx} = {…}会报错,因为等式右边对象不存在xxx属性
此时我们可以将xxx在undefined的时候取默认属性值
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
函数参数的解构赋值(实用)
适用场景:函数参数默认值的设置,减少if-else的使用
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
//函数move的参数是一个对象,
通过对这个对象进行解构,得到变量x和y的值。
如果解构失败,x和y等于默认值
字符串-函数-数组-对象的扩展
字符串扩展
用反引号(`)标识
//1.多行字符串
let str = `
hi,
This is ha.com.
You are very good
`
//2.字符串模板嵌入JS变量、表达式
let website = 'ha'
let who ='you'
let str =`
this is ${website}.
${who} are very good
`
函数参数默认值的设置
前置:ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
缺点:如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
ES6允许为函数参数设置默认值,即直接写在参数定义的后面
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
箭头函数及其this
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
注意:
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
箭头函数没有自己的this对象
数组的扩展运算符
扩展运算符…
将一个数组转为用逗号分隔的参数序列
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
用于函数的调用
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
复制数组(深拷贝)
前置:ES5传统复制:变通式concat()
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
扩展运算符… (深拷贝)
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
合并数组(浅拷贝)
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];
a3[0] === a1[0] // true
a4[0] === a1[0] // true
注意:
上面代码中,a3和a4是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。
ES6模块化
Module模块功能主要由两个命令构成:export和import;export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。
export命令
可输出:变量、函数或类(class)
法一:直接声明后export
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
法二:统一声明后尾部export
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
//优先考虑使用这种写法。
因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量
export default 命令
适用场景:给用户import导入提供方便,不需要了解模块有哪些属性和方法名称,即可自定义export导出的对象、函数名称等
注意:
- import命令后面不使用大括号
- 一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令 ```javascript 导出: // export-default.js export default function foo() { console.log(‘foo’); } // 或者写成 function foo() { console.log(‘foo’); } export default foo;
导入: // import-default.js import customName from ‘./export-default’; customName(); // ‘foo’
<a name="iV1WD"></a>
## import命令
注意:<br />import命令具有提升效果,会提升到整个模块的头部,首先执行,即import命令是编译阶段执行的,在代码运行之前
import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
```javascript
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
如果输入的是一个对象,改写对象的属性是允许的
import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性。
Class的基本语法及其继承
class可以看作只是一个语法糖,
class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法
前置:
传统ES5方法:通过构造函数和原型
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
ES6的class:
注意:方法与方法之间不需要逗号分隔,加了会报错
class Point {
constructor(x, y) { //初始化:使用 new 操作符实例化 Person 类的时候
传入的参数会被 constructor 接收并使用
this.x = x;
this.y = y;
}
static name = 'npc' //挂在class类上----类属性(相当于对象的原型上)
toString() {
return '(' + this.x + ', ' + this.y + ')'; ////挂在class类上----类方法(相当于对象的原型上)
}
}
Class的继承
class ColorPoint extends Point { //默认实现propotype上静态方法的继承
constructor(x, y, color) {
super(x, y); //实现私有属性的继承;注意必须先super再写自身属性如下句
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 可调用父类的toString()
}
}
注意:
- super关键字表示父类的构造函数,用来新建父类的this对象
- 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象
Set、Map、WeakSet、WeakMap
强引用和弱引用
Map和WeakMap的区别就在于WeakMap是弱引用,key置空后,value的内存会释放
var wm = new Map();
var b = new Object();
wm.set(b, new Array(5*1024*1024)); // 开辟内存
b = null; //key置空
Proxy代理对象
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚
举、函数调用等)const p = new Proxy(target, handler)