let/const命令

let命令

  • 用来声明变量;
  • 用法类似于var;
  • let实际上为Javascript新增了块级作用域

区别于var:

  1. let命令所声明的变量只在它所在的代码块内有效
  2. 适用场景:for循环的计数器,计数器i只在for循环体内有效(let循环有奇效)
  3. 不存在变量提升,即变量需在声明后才能使用
  4. 不允许在相同作用域内,重复生命同一个变量

cosnt命令

  • 用来声明一个只读的常量;
  • 一旦声明就必须立即初始化,不能留到以后赋值
  • 作用域与let命令相同

注意:将对象声明为常量时,const只能保证指向对象的地址,对象本身仍是可写的
拓展:为保证对象完全冻结,常用freeze()冻结方法:const foo = Object.freeze()

新增数据类型

Symbol

记住两个api
Symbol([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.

    解构赋值

数组解构赋值

  1. let [a, b, c] = [1, 2, 3];
  2. //可以从数组中提取值,按照一 一对应位置,对变量赋值

本质上属于“模式匹配”:只要等号两边的模式相同,左边的变量就会被赋予右边对应模式的值

  1. let [foo] = [];
  2. let [bar, foo] = [1];
  3. //解构不成功,变量值为undefined

应用场景:
当拿到的一系列数据存储在csv文件中时,如const info = ‘npc,12,3333’
需要将这些纯文本字符串转换为数组,const lastInfo = info.split(‘,’)
然后再从该数组中拿出出所需的值
or
排序时互换顺序

数组默认值的设置

前提:
ES6内部使用严格相等运算符===,当一个数组成员严格等于undefined,默认值才会生效(null不严格等于undefined)

  1. let [x = 1, y = x] = []; // x=1; y=1
  2. let [x = 1, y = x] = [2]; // x=2; y=2
  3. let [x = 1, y = x] = [1, 2]; // x=1; y=2
  4. let [x = y, y = 1] = []; // ReferenceError: y is not defined

对象的解构赋值(常用)

  1. let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
  2. foo // "aaa"
  3. bar // "bbb"

与数组解构赋值的重要区别:
数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

适用场景:方便地将现有对象的方法,赋值到某个变量上

取别名

  1. let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
  2. baz // "aaa"
  3. foo // error: foo is not defined
  4. //foo是匹配的模式,baz才是变量。
  5. 真正被赋值的是变量baz
  6. 而不是模式foo

对象默认值的设置

前提:
ES6内部使用严格相等运算符===,当一个对象属性严格等于undefined,默认值才会生效(null不严格等于undefined)
应用场景:当后端传递过来的对象中没有xxx属性,即const {xxx} = {…}会报错,因为等式右边对象不存在xxx属性
此时我们可以将xxx在undefined的时候取默认属性值

  1. var {x = 3} = {};
  2. x // 3
  3. var {x, y = 5} = {x: 1};
  4. x // 1
  5. y // 5
  6. var {x: y = 3} = {};
  7. y // 3
  8. var {x: y = 3} = {x: 5};
  9. y // 5
  10. var { message: msg = 'Something went wrong' } = {};
  11. msg // "Something went wrong"

函数参数的解构赋值(实用)

适用场景:函数参数默认值的设置,减少if-else的使用

  1. function move({x = 0, y = 0} = {}) {
  2. return [x, y];
  3. }
  4. move({x: 3, y: 8}); // [3, 8]
  5. move({x: 3}); // [3, 0]
  6. move({}); // [0, 0]
  7. move(); // [0, 0]
  8. //函数move的参数是一个对象,
  9. 通过对这个对象进行解构,得到变量xy的值。
  10. 如果解构失败,xy等于默认值

字符串-函数-数组-对象的扩展

字符串扩展

用反引号(`)标识

  1. //1.多行字符串
  2. let str = `
  3. hi,
  4. This is ha.com.
  5. You are very good
  6. `
  7. //2.字符串模板嵌入JS变量、表达式
  8. let website = 'ha'
  9. let who ='you'
  10. let str =`
  11. this is ${website}.
  12. ${who} are very good
  13. `

函数参数默认值的设置

前置:ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法

  1. function log(x, y) {
  2. y = y || 'World';
  3. console.log(x, y);
  4. }
  5. log('Hello') // Hello World
  6. log('Hello', 'China') // Hello China
  7. log('Hello', '') // Hello World

缺点:如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

ES6允许为函数参数设置默认值,即直接写在参数定义的后面

  1. function log(x, y = 'World') {
  2. console.log(x, y);
  3. }
  4. log('Hello') // Hello World
  5. log('Hello', 'China') // Hello China
  6. log('Hello', '') // Hello

箭头函数及其this

  1. var f = () => 5;
  2. // 等同于
  3. var f = function () { return 5 };
  4. var sum = (num1, num2) => num1 + num2;
  5. // 等同于
  6. var sum = function(num1, num2) {
  7. return num1 + num2;
  8. };

注意:
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

  1. // 报错
  2. let getTempItem = id => { id: id, name: "Temp" };
  3. // 不报错
  4. let getTempItem = id => ({ id: id, name: "Temp" });

箭头函数没有自己的this对象

数组的扩展运算符

扩展运算符…
将一个数组转为用逗号分隔的参数序列

  1. console.log(...[1, 2, 3])
  2. // 1 2 3
  3. console.log(1, ...[2, 3, 4], 5)
  4. // 1 2 3 4 5
  5. [...document.querySelectorAll('div')]
  6. // [<div>, <div>, <div>]

适用场景:

用于函数的调用

  1. function push(array, ...items) {
  2. array.push(...items);
  3. }
  4. function add(x, y) {
  5. return x + y;
  6. }
  7. const numbers = [4, 38];
  8. add(...numbers) // 42

复制数组(深拷贝)

前置:ES5传统复制:变通式concat()

  1. const a1 = [1, 2];
  2. const a2 = a1.concat();
  3. a2[0] = 2;
  4. a1 // [1, 2]

扩展运算符… (深拷贝)

  1. const a1 = [1, 2];
  2. // 写法一
  3. const a2 = [...a1];
  4. // 写法二
  5. const [...a2] = a1;

合并数组(浅拷贝)

  1. const arr1 = ['a', 'b'];
  2. const arr2 = ['c'];
  3. const arr3 = ['d', 'e'];
  4. // ES5 的合并数组
  5. arr1.concat(arr2, arr3);
  6. // [ 'a', 'b', 'c', 'd', 'e' ]
  7. // ES6 的合并数组
  8. [...arr1, ...arr2, ...arr3]
  9. // [ 'a', 'b', 'c', 'd', 'e' ]
  1. const a1 = [{ foo: 1 }];
  2. const a2 = [{ bar: 2 }];
  3. const a3 = a1.concat(a2);
  4. const a4 = [...a1, ...a2];
  5. a3[0] === a1[0] // true
  6. a4[0] === a1[0] // true

注意:
上面代码中,a3和a4是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。

ES6模块化

Module模块功能主要由两个命令构成:export和import;export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。

export命令

可输出:变量、函数或类(class)

  1. 法一:直接声明后export
  2. // profile.js
  3. export var firstName = 'Michael';
  4. export var lastName = 'Jackson';
  5. export var year = 1958;
  6. 法二:统一声明后尾部export
  7. // profile.js
  8. var firstName = 'Michael';
  9. var lastName = 'Jackson';
  10. var year = 1958;
  11. export { firstName, lastName, year };
  12. //优先考虑使用这种写法。
  13. 因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量

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’

  1. <a name="iV1WD"></a>
  2. ## import命令
  3. 注意:<br />import命令具有提升效果,会提升到整个模块的头部,首先执行,即import命令是编译阶段执行的,在代码运行之前
  4. import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
  5. ```javascript
  6. import {a} from './xxx.js'
  7. a = {}; // Syntax Error : 'a' is read-only;

如果输入的是一个对象,改写对象的属性是允许的

  1. import {a} from './xxx.js'
  2. a.foo = 'hello'; // 合法操作

建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性。

Class的基本语法及其继承

class可以看作只是一个语法糖,
class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法

前置:

生成实例对象:

传统ES5方法:通过构造函数和原型

  1. function Point(x, y) {
  2. this.x = x;
  3. this.y = y;
  4. }
  5. Point.prototype.toString = function () {
  6. return '(' + this.x + ', ' + this.y + ')';
  7. };
  8. var p = new Point(1, 2);

ES6的class:

注意:方法与方法之间不需要逗号分隔,加了会报错

  1. class Point {
  2. constructor(x, y) { //初始化:使用 new 操作符实例化 Person 类的时候
  3. 传入的参数会被 constructor 接收并使用
  4. this.x = x;
  5. this.y = y;
  6. }
  7. static name = 'npc' //挂在class类上----类属性(相当于对象的原型上)
  8. toString() {
  9. return '(' + this.x + ', ' + this.y + ')'; ////挂在class类上----类方法(相当于对象的原型上)
  10. }
  11. }

Class的继承

  1. class ColorPoint extends Point { //默认实现propotype上静态方法的继承
  2. constructor(x, y, color) {
  3. super(x, y); //实现私有属性的继承;注意必须先super再写自身属性如下句
  4. this.color = color;
  5. }
  6. toString() {
  7. return this.color + ' ' + super.toString(); // 可调用父类的toString()
  8. }
  9. }

注意:

  • super关键字表示父类的构造函数,用来新建父类的this对象
  • 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象

Set、Map、WeakSet、WeakMap

强引用和弱引用

Map和WeakMap的区别就在于WeakMap是弱引用,key置空后,value的内存会释放

  1. var wm = new Map();
  2. var b = new Object();
  3. wm.set(b, new Array(5*1024*1024)); // 开辟内存
  4. b = null; //key置空

image.png

Proxy代理对象

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚
举、函数调用等)
const p = new Proxy(target, handler)