ES6 语法精简整理

it2024-10-27  38

ECMAScript 总结

简介

ECMAScript中的变量无特定的类型,定义变量时只用var运算符 每行结尾的分号可有可无,但为了保持良好的编程习惯,最好总是加入分号 注释与 Java、C 和 PHP 语言的注释相同 括号表示代码块 ECMAScript中的变量并不一定要初始化。 ECMAScript有5种原始类型(primitive type),即Undefined、Null、Boolean、Number和String。 Undefined类型具有唯一的值,即undefined。当声明的变量未初始化时,该变量的默认值是undefined。 另一种只有一个值的类型是Null,它有唯一专用值null,即它的字面量。值undefined实际上是从值null派生来的 alert(null == undefined); //输出 “true” undefined表示声明未赋值,null表示尚未存在的对象。如果返回的是对象,那么找不到该对象时,返回的通常是null。

运算符

逻辑NOT运算符 对象,false 数字0,true 0以外的任何数字,false null,true NaN,true undefined,发生错误

加法运算符 某个运算数是NaN,那么结果为NaN。 -Infinity加-Infinity,结果为-Infinity。 Infinity加-Infinity,结果为NaN。 +0加+0,结果为+0。 -0加+0,结果为+0。 -0加-0,结果为-0。

减法运算符 在处理特殊值时,减法运算符也有一些特殊行为: 某个运算数是NaN,那么结果为NaN。 Infinity减Infinity,结果为NaN。 -Infinity减-Infinity,结果为NaN。 Infinity减-Infinity,结果为Infinity。 -Infinity减Infinity,结果为-Infinity。 +0减+0,结果为+0。 -0减-0,结果为-0。 +0减-0,结果为+0。 某个运算符不是数字,那么结果为NaN。

等号和非等号 特殊情况

null == undefined true "NaN" == NaN false 5 == NaN false NaN == NaN false NaN != NaN true false == 0 true true == 1 true true == 2 false undefined == 0 false null == 0 false "5" == 5 true

闭包

函数可以使用函数之外定义的变量 var sMessage = "hello world"; function sayHelloWorld() { alert(sMessage); } sayHelloWorld();

对象

在 ECMAScript 中,只能访问对象的引用。每次创建对象,存储在变量中的都是该对象的引用,而不是对象本身。 约定规则,私有的属性和方法:在前后加下划线: obj._color_ = "blue" (注意这只是书写规则,并不能改变变量的属性) 通过prototype属性为本地对象添加属性和方法

let&const

let let声明的变量只在它所在的代码块有效。 变量一定要在声明后使用,否则报错。 如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 let不允许在相同作用域内,重复声明同一个变量。 let为JavaScript新增了块级作用域。 (注意:考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。)

const const声明一个只读的常量。一旦声明,常量的值就不能改变。 const一旦声明变量,就必须立即初始化,不能留到以后赋值。 const的作用域与let命令相同:只在声明所在的块级作用域内有效。 const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。 const声明的常量不可重复声明。 const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。

为了保持兼容性,var命令和function命令声明的全局变量,依旧是全局对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。

解构赋值

经典

var [a, b, c] = [1, 2, 3]; let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined z // []

如果解构不成功,变量的值就等于undefined。

对象的解构赋值

let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb"

如果变量名与属性名不一致,必须写成下面这样。

let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world'

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: ‘aaa’, bar: ‘bbb’ }; baz // “aaa” foo // error: foo is not defined

嵌套赋值

let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true]

对象默认值

var { message: msg = 'Something went wrong' } = {}; msg // "Something went wrong"

默认值生效的条件是,对象的属性值严格等于undefined。

var {x = 3} = {x: undefined}; x // 3 var {x = 3} = {x: null}; x // null

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。

函数参数的解构赋值

function add([x, y]){ return x + y; } add([1, 2]); // 3 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]

字符串

at() 可以识别Unicode编号大于0xFFFF的字符,返回正确的字符。

'abc'.at(0) // "a" '????'.at(0) // "????"

normalize() 用来将字符的不同表示方法统一为同样的形式,主要用来处理欧洲语言 includes():返回布尔值,表示是否找到了参数字符串。 startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。 endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。 repeat(): 返回一个新字符串,表示将原字符串重复n次。

'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" 'na'.repeat(0) // ""

padStart()用于头部补全 padEnd()用于尾部补全 (如果省略第二个参数,则会用空格补全长度。)

'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'

(如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。) 模板字符串,用反引号(`)标识 字符串中嵌入变量,可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`

String.raw() 往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串。

String.raw`Hi\n${2+3}!`; // "Hi\\n5!" String.raw`Hi\u000A!`; // 'Hi\\u000A!'

数值

Number.isFinite()用来检查一个数值是否为有限的(finite) Number.isNaN()用来检查一个值是否为NaN。 与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,非数值一律返回false。 Number.parseInt(), Number.parseFloat() 取代传统的parseInt, parseFloat

// ES6的写法 Number.parseInt('12.34') // 12 Number.parseFloat('123.45#') // 123.45

Number.isInteger() 判断一个值是否为整数 注意3和0.3视为同一个值

Number.isInteger(25) // true Number.isInteger(25.0) // true

Number.EPSILON 引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。如果这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。

5.551115123125783e-17 < Number.EPSILON // true

Number.EPSILON的实质是一个可以接受的误差范围。

function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON; } withinErrorMargin(0.1 + 0.2, 0.3) // true withinErrorMargin(0.2 + 0.2, 0.3) // false

Math.trunc() 去除一个数的小数部分,返回整数部分(不会四舍五入),非数值先转为数值,空值和无法截取整数的值,返回NaN Math.sign() 用来判断一个数到底是正数、负数、还是零 参数为正数,返回+1; 参数为负数,返回-1; 参数为0,返回0; 参数为-0,返回-0; 其他值,返回NaN。 指数运算符 ** 进行指数运算

let a = 2; a **= 2; // 等同于 a = a * a; let b = 3; b **= 3; // 等同于 b = b * b * b;

扩展运算符

扩展运算符(spread)是三个点(…)。它将一个数组转为用逗号分隔的参数序列。 主要用于函数调用

function push(array, ...items) { array.push(...items); } function add(x, y) { return x + y; } const numbers = [4, 38]; add(...numbers) // 42

其他用法:

Math.max(...[14, 3, 77]) // 等同于 Math.max(14, 3, 77); // push方法 let arr1 = [0, 1, 2]; let arr2 = [3, 4, 5]; arr1.push(...arr2); //Date new Date(...[2015, 1, 1]);

数组扩展

Array.from()将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象

let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array.of(3).length // 1

数组实例的copyWithin() 将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组

Array.prototype.copyWithin(target, start = 0, end = this.length) // 将3号位复制到0号位 [1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5] // -2相当于3号位,-1相当于4号位 [1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5] // 将3号位复制到0号位 [].copyWithin.call({length: 5, 3: 1}, 0, 3) // {0: 1, 3: 1, length: 5}

数组实例的find()方法,用于找出第一个符合条件的数组成员。

[1, 4, -5, 10].find((n) => n < 0) // -5

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2 注意这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足。 [NaN].indexOf(NaN) // -1 [NaN].findIndex(y => Object.is(NaN, y)) // 0

数组实例的fill()方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7]

数组中已有的元素,会被全部抹去。 fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']

注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

数组实例的 includes() 方法返回一个布尔值,表示某个数组是否包含给定的值

[1, 2, NaN].includes(NaN) // true

该方法的第二个参数表示搜索的起始位置,默认为0

[1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true

includes使用的是不一样的判断算法,不会误判NaN。

[NaN].includes(NaN) // true

数组实例的 flat() 用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

[1, 2, [3, 4]].flat() // [1, 2, 3, 4]

flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1

[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]

如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。

[1, [2, [3]]].flat(Infinity) // [1, 2, 3]

flat()方法会跳过空位

数组实例的 flatMap() 对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。 // 相当于 [[2, 4], [3, 6], [4, 8]].flat() [2, 3, 4].flatMap((x) => [x, x * 2]) // [2, 4, 3, 6, 4, 8] flatMap()只能展开一层数组。

函数

箭头函数

var f = () => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };

箭头函数可以与变量解构结合使用。

const full = ({ first, last }) => first + ' ' + last; // 等同于 function full(person) { return person.first + ' ' + person.last; }

使用注意点 (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。 (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。 (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。 (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

箭头函数导致this总是指向函数定义生效时所在的对象,这是因为箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。

不适用场合 第一个场合是定义对象的方法,且该方法内部包括this。 第二个场合是需要动态this的时候,也不应使用箭头函数。(按钮绑定时间的时候)

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

function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello

函数的 length 属性 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2

name 属性 返回该函数的函数名。

function foo() {} 如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。 const bar = function baz() {}; // ES5 bar.name // "baz" // ES6 bar.name // "baz"/ "foo"

对象

Object.is() 用来比较两个值是否严格相等

注意的是:一是+0不等于-0,二是NaN等于自身。 +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true

Object.assign() 用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

const target = { a: 1 }; const source1 = { b: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3} 注意:Object.assign()方法实行的是浅拷贝

Set

构造Set

const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4

用来去除数组重复成员

[...new Set(array)]

Set 实例的属性和方法 Set.prototype.add(value):添加某个值,返回 Set 结构本身。 Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。 Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。 Set.prototype.clear():清除所有成员,没有返回值。 Set遍历 for…of

let set = new Set(['red', 'green', 'blue']); for (let x of set) { console.log(x); } // red // green // blue

forEach

let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)) // 1 : 1 // 4 : 4 // 9 : 9

Map

构造

const map = new Map([ ['name', '张三'], ['title', 'Author'] ]);

如果对同一个键多次赋值,后面的值将覆盖前面的值。 如果读取一个未知的键,则返回undefined。

Map 实例的属性和方法 size 返回 Map 结构的成员总数 set(key, value): set方法设置键名key对应的键值为value,然后返回整个 Map 结构 get(key): get方法读取key对应的键值,如果找不到key,返回undefined。 has(key): has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中 delete(key): delete方法删除某个键,返回true。如果删除失败,返回false。 clear(): clear方法清除所有成员,没有返回值。 keys():返回键名的遍历器。 values():返回键值的遍历器。 entries():返回所有成员的遍历器。 forEach():遍历 Map 的所有成员。

WeakSet

WeakSet 的成员只能是对象,而不能是其他类型的值。 WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用 WeakSet 适合临时存放一组对象,防止内存泄漏 WeakSet能使用的方法: add(), delete(), has()

WeakMap

WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。 WeakMap的键名所指向的对象,不计入垃圾回收机制。 WeakMap解决内存泄漏问题,只要外部的引用消失,WeakMap 内部的引用,就会自动被垃圾回收清除。 WeakMap能使用的方法 get(), set(), has(), delete() 一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。

for…of 循环

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。 for…of循环可以代替数组实例的forEach方法。 注意:JavaScript 原有的for…in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for…of循环,允许遍历获得键值。

var arr = ['a', 'b', 'c', 'd']; for (let a in arr) { console.log(a); // 0 1 2 3 } for (let a of arr) { console.log(a); // a b c d }

for…of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for…in循环也不一样。

let arr = [3, 5, 7]; arr.foo = 'hello'; for (let i in arr) { console.log(i); // "0", "1", "2", "foo" } for (let i of arr) { console.log(i); // "3", "5", "7" }

for…in 与 for…of的比较

for…in循环有几个缺点。 数组的键名是数字,但是for…in循环是以字符串作为键名“0”、“1”、“2”等等。 …in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。 某些情况下,for…in循环会以任意顺序遍历键名。 总之,for…in循环主要是为遍历对象而设计的,不适用于遍历数组。

for…of循环相比上面几种做法,有一些显著的优点。 有着同for…in一样的简洁语法,但是没有for…in那些缺点。 不同于forEach方法,它可以与break、continue和return配合使用。 提供了遍历所有数据结构的统一操作接口。

Promise

Promise 是异步编程的一种解决方案。 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 Promise有三种状态:1. pending; 2. fulfilled; 3.rejected。除了异步操作,其他任何操作都不能改变此状态。 Promise的状态改变只有两种可能:从pending到fulfilled; 从pending到rejected。

Promise对象是一个构造函数,用来生成Promise实例。

const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) { // success }, function(error) { // failure });

Promise.try让同步函数同步执行,异步函数异步执行

const f = () => console.log('now'); Promise.try(f); console.log('next'); // now // next

由于Promise.try为所有操作提供了统一的处理机制,所以如果想用then方法管理流程,最好都用Promise.try包装一下。这样有许多好处,其中一点就是可以更好地管理异常。

Promise.try(() => database.users.get({id: userId})) .then(...) .catch(...)

事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

最新回复(0)