文章目录
  1. 1. ECMASCRIPT和JavaScript的关系
  2. 2. let 命令
  3. 3. const 命令
  4. 4. 顶层对象属性
  5. 5. 数组的解构赋值
  6. 6. 变量的解构赋值
  7. 7. 字符串的解构赋值
  8. 8. 数值和布尔值的解构赋值
  9. 9. 函数参数的解构赋值
  10. 10. 变量赋值的用途
  11. 11. 字符串的扩展
    1. 11.1. includes(), startsWith(),endsWith()
    2. 11.2. repeat()
  12. 12. 数值的扩展
    1. 12.1. Number.isFinite(),Number.isNaN()
    2. 12.2. Number.parseInt(), Number.parseFloat()
    3. 12.3. Number.isInteger()
    4. 12.4. Math对象的扩展
      1. 12.4.1. Math.trunc()
      2. 12.4.2. Math.sign(4)
  13. 13. Array.form()
    1. 13.1. Array.of()
    2. 13.2. 数组实例find()和findIndex()
    3. 13.3. 数组的fill()
    4. 13.4. 数组实例的entries(),keys()和values()
    5. 13.5. 数组的includes()
    6. 13.6. 数组的空位
  14. 14. 函数的扩展
    1. 14.1. 基本用法
    2. 14.2. 与解构赋值相结合
    3. 14.3. 函数的length
    4. 14.4. rest参数
    5. 14.5. 扩展运算符的应用
      1. 14.5.1. 合并数组
      2. 14.5.2. Map和Set结构,Generator函数
    6. 14.6. name 属性
  15. 15. 箭头函数
  16. 16. 属性的简洁用法
    1. 16.1. 方法名的name
    2. 16.2. Object.is()
  17. 17. 属性的遍历
    1. 17.1. Object.keys(),Object.values(),Object.entries()
    2. 17.2. Object.entries
  18. 18. set
  19. 19. map
    1. 19.1. size()属性
    2. 19.2. set(key, value)
    3. 19.3. get(key)
    4. 19.4. has(key)
    5. 19.5. delete(key) 返回true
    6. 19.6. clear()
    7. 19.7. 便利方法

ECMASCRIPT和JavaScript的关系

一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?

要讲清楚这个问题,需要回顾历史。1996年11月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是1.0版。

该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合,这两个词是可以互换的。


let 命令

let命令类似于var,但是只在块级作用域内生效,不能进行变量提升。

1
2
3
4
5
6
7
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1

const 命令

const类似于var,定义常量,一旦声明,常量的值不能改变。常量必须赋值,如果不赋值就会报错。

1
2
const foo;
VM1586:1 Uncaught SyntaxError: Missing initializer in const declaration

const 命令也不能进行变量提升

顶层对象属性

1
2
3
4
5
6
7
var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1
let b = 1;
window.b // undefined

上面代码中,全局变量a由var命令声明,所以它是顶层对象的属性;全局变量b由let命令声明,所以它不是顶层对象的属性,返回undefined。

数组的解构赋值

1
2
3
4
5
let [a, b, c] = [1, 2, 3];
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

变量的解构赋值

1
2
3
4
5
6
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

字符串的解构赋值

1
2
3
4
5
6
7
8
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

1
2
3
4
5
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true

函数参数的解构赋值

1
2
3
4
5
function add([x, y]){
return x + y;
}
add([1, 2]); // 3

变量赋值的用途

交换变量值

1
2
3
4
let x = 1;
let y = 2;
[x, y] = [y, x];

变量赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();

字符串的扩展

includes(), startsWith(),endsWith()

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

1
2
3
4
5
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

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

数值的扩展

Number.isFinite(),Number.isNaN()

1
2
3
4
5
6
7
Number.isFinite(15)
Number.isFinite(0.8); // true
Number.isFinite(NaN); //
Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false

Number.parseInt(), Number.parseFloat()

1
2
3
4
5
6
7
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45

Number.isInteger()

1
2
3
4
5
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false

Math对象的扩展

Math.trunc()

Math.trunc方法用于去除一个数的小数部分,返回整数部分

1
2
3
4
5
6
Math.trunc()
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0

Math.sign(4)

Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

它会返回五种值。

参数为正数,返回+1;
参数为负数,返回-1;
参数为0,返回0;
参数为-0,返回-0;
其他值,返回NaN。

Array.form()

Array.from 方法用于将两类对象转换成真正的数组

1
2
3
4
5
6
7
8
9
10
11
12
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

1
2
3
4
5
6
7
8
9
10
11
// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});
// arguments对象
function foo() {
var args = Array.from(arguments);
// ...
}

Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

1
2
3
4
5
6
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

Array.of()

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

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

数组实例find()和findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

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

上面代码找出数组中第一个小于0的成员。

1
2
3
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10

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

1
2
3
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。

1
2
3
4
5
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0

数组的fill()

fill方法使用给定值,填充一个数组。

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

数组实例的entries(),keys()和values()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"

数组的includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

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

数组的空位

forEach(), filter(), every() 和some()都会跳过空位。
map()会跳过空位,但会保留这个值
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
// forEach方法
[,’a’].forEach((x,i) => console.log(i)); // 1

// filter方法
[‘a’,,’b’].filter(x => true) // [‘a’,’b’]

// every方法
[,’a’].every(x => x===’a’) // true

// some方法
[,’a’].some(x => x !== ‘a’) // false

// map方法
[,’a’].map(x => 1) // [,1]

// join方法
[,’a’,undefined,null].join(‘#’) // “#a##”

// toString方法
[,’a’,undefined,null].toString() // “,a,,”

函数的扩展

基本用法

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

与解构赋值相结合

参数默认值可以与解构赋值的默认值,结合起来使用。

1
2
3
4
5
6
7
8
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // TypeError: Cannot read property 'x' of undefined

函数的length

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

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

rest参数

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

1
2
3
4
5
6
7
8
9
10
11
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10

函数的length属性,不包括 rest 参数。

1
2
3
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1

扩展运算符的应用

合并数组

扩展运算符提供了数组合并的新写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

(2)与解构赋值结合

扩展运算符可以与解构赋值结合起来,用于生成数组。

1
2
3
4
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list

Map和Set结构,Generator函数

扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。

1
2
3
4
5
6
7
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]

name 属性

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

1
2
function foo() {}
foo.name // "foo"

箭头函数

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

属性的简洁用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};

方法名的name

函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

1
2
3
4
5
6
7
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"

Object.is()

ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

1
2
3
4
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

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

1
2
3
4
5
6
7
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

属性的遍历

ES6 一共有5种方法可以遍历对象的属性。

(1)for…in

for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举。

以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。

首先遍历所有属性名为数值的属性,按照数字排序。
其次遍历所有属性名为字符串的属性,按照生成时间排序。
最后遍历所有属性名为 Symbol 值的属性,按照生成时间排序。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// [‘2’, ‘10’, ‘b’, ‘a’, Symbol()]
上面代码中,Reflect.ownKeys方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2和10,其次是字符串属性b和a,最后是 Symbol 属性。

Object.keys(),Object.values(),Object.entries()

Object.keys()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。

1
2
3
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

Object.entries

Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

1
2
3
var obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

set

Array.from方法可以将 Set 结构转为数组。

1
2
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

map

1
2
3
4
5
6
7
8
9
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false

size()属性

size属性返回 Map 结构的成员总数。

1
2
3
4
5
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2

set(key, value)

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

1
2
3
4
5
const m = new Map();
m.set('edition', 6) // 键是字符串
m.set(262, 'standard') // 键是数值
m.set(undefined, 'nah') // 键是 undefined

get(key)

has(key)

delete(key) 返回true

clear()

便利方法

Map 结构原生提供三个遍历器生成函数和一个遍历方法。

keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。

文章目录
  1. 1. ECMASCRIPT和JavaScript的关系
  2. 2. let 命令
  3. 3. const 命令
  4. 4. 顶层对象属性
  5. 5. 数组的解构赋值
  6. 6. 变量的解构赋值
  7. 7. 字符串的解构赋值
  8. 8. 数值和布尔值的解构赋值
  9. 9. 函数参数的解构赋值
  10. 10. 变量赋值的用途
  11. 11. 字符串的扩展
    1. 11.1. includes(), startsWith(),endsWith()
    2. 11.2. repeat()
  12. 12. 数值的扩展
    1. 12.1. Number.isFinite(),Number.isNaN()
    2. 12.2. Number.parseInt(), Number.parseFloat()
    3. 12.3. Number.isInteger()
    4. 12.4. Math对象的扩展
      1. 12.4.1. Math.trunc()
      2. 12.4.2. Math.sign(4)
  13. 13. Array.form()
    1. 13.1. Array.of()
    2. 13.2. 数组实例find()和findIndex()
    3. 13.3. 数组的fill()
    4. 13.4. 数组实例的entries(),keys()和values()
    5. 13.5. 数组的includes()
    6. 13.6. 数组的空位
  14. 14. 函数的扩展
    1. 14.1. 基本用法
    2. 14.2. 与解构赋值相结合
    3. 14.3. 函数的length
    4. 14.4. rest参数
    5. 14.5. 扩展运算符的应用
      1. 14.5.1. 合并数组
      2. 14.5.2. Map和Set结构,Generator函数
    6. 14.6. name 属性
  15. 15. 箭头函数
  16. 16. 属性的简洁用法
    1. 16.1. 方法名的name
    2. 16.2. Object.is()
  17. 17. 属性的遍历
    1. 17.1. Object.keys(),Object.values(),Object.entries()
    2. 17.2. Object.entries
  18. 18. set
  19. 19. map
    1. 19.1. size()属性
    2. 19.2. set(key, value)
    3. 19.3. get(key)
    4. 19.4. has(key)
    5. 19.5. delete(key) 返回true
    6. 19.6. clear()
    7. 19.7. 便利方法