好记性不如烂笔头 - ECMAScript 6 (一)

深入了解ES6语法特性,一共三篇。

let const

let 声明变量,带块级作用域;
const 声明只读常量;

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

父级块域定义let后,子级块域var则不可定义同变量名

1
2
3
4
5
6
7
8
9
10
11
12
13
var b = 10
{
let b = 20;
b; //20
}
b //10
//相反:
let b = 10
{
var b = 20;
b; //Uncaught SyntaxError: Identifier 'b' has already been declared
}
b //Uncaught SyntaxError: Identifier 'b' has already been declared

常量不可更改

1
2
3
4
5
const b = 20;
{
b = 10;
b; //Uncaught TypeError: Assignment to constant variable.
}

JavaScript解释器是先捕捉变量,在执行函数 && 变量提升

1
2
3
4
5
let b = 20; 
console.log('2、'+b); //2、20
b; //10
console.log('3、'+b); //3、20
b = 10;

Variable 解构赋值

ES6 可以按一定模式,从数组和对象中提取值,对变量进行赋值。

Array

列举部分用法:

1
2
3
4
5
6
7
8
let [a, b, c] = [{a:'a'},{b:'b'},{c:'c'}];
a; //{a:'a'}
b; //{b:'b'}
c; //{c:'c'}

let [a, ,c] = [{a:'a'},{b:'b'},{c:'c'}]
a; //{ a: 'a' }
c; //{ c: 'c' }

利用spread

1
2
3
let [a, ...c] = [{a:'a'},{b:'b'},{c:'c'}]
a; //{ a: 'a' }
c; //[ { b: 'b' }, { c: 'c' } ]

使用spread,值就为[]

1
2
3
let [a,...c] = [{a:'a'}]
a; //{ a: 'a' }
c; //[]

解析不成功,值就为undefined

1
2
3
let [a,c] = [{a:'a'}]
a; //{ a: 'a' }
c; //undefined

使用默认值

1
2
3
let [a,c = {c:'c'}] = [{a:'a'}]
a; //{ a: 'a' }
c; //{ c: 'c' }

b只会取得当前[]的第一个2,不会取得整个[2,3]

1
2
3
4
let [ a, [b], d ] = [ 1, [2, 3], 4 ];
a // 1
b // 2
d // 4

只要某种数据结构具有Set结构和 Iterator 接口都可以用此方法赋值

Object

列举部分用法:

1
2
3
4
let { a, b , c } = { a: 'aaa', b: 'bbb', c: 'ccc' };
a; //"aaa"
b; //"bbb"
c; //"ccc"

Object中取出指定key对应的value

1
2
3
let { c } = { a: 'aaa', b: 'bbb', c: 'ccc' };
c; //"ccc"
d; //解构失败undefined

Object中取出指定方法

1
2
const { log } = console;
log('hello')

重命名取到的key

1
2
3
let { b: d } = { a: 'aaa', b: 'bbb', c: 'ccc' };
d; //"bbb"
b; //Uncaught ReferenceError: b is not defined

使用默认值

1
2
3
4
5
6
7
8
9
10
11
let { a , d = 'ddd' } = { a: 'aaa', b: 'bbb', c: 'ccc' };
d; //"ddd"

let { x : y = 3} = {x: 4};
y; // 4

let {x = 3} = {x: undefined};
x; // 3

let {x = 3} = {x: null};
x; // null

注意

1
2
3
4
5
6
7
let x;
{x} = {x: 1};
// SyntaxError: syntax error
let x;
({x} = {x: 1});
x; //1
// JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误

String

列举部分用法:

1
2
3
4
5
6
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

还可调用length属性

1
2
let {length : len} = 'hello';
len // 5

function参数的解构赋值

列举部分用法:

1
2
3
4
5
function add([a, b]){
return a + b;
}

add([1, 2]); // 3

String的扩展

常用的新String增扩展:

遍历器接口

String可以被for..of遍历

1
2
3
4
5
6
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"

模板字符串

列举部分用法:

1
2
3
let add = 'hello '
let foo = `${add}world`
foo; //hello world

调用函数

1
2
3
4
function fn() {
return "Hello World";
}
`foo ${fn()} bar` //foo Hello World bar

多层级嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },];

console.log(tmpl(data));
//<table>
//
//<tr><td><Jane></td></tr>
//<tr><td>Bond</td></tr>
//
//<tr><td>Lars</td></tr>
//<tr><td><Croft></td></tr>
//
//</table>

“标签模板”功能(tagged template)

紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let a = 5;
let b = 10;

function tag(s, v1, v2) {
console.log(s); //[ 'Hello ', ' world ', '' ]
console.log(v1); //15
console.log(v2); //50

let result = '';
let i = 0;

while (i < s.length) {
result += s[i++];
if (i < arguments.length) {
result += arguments[i];
}
}
return result;
}

tag`Hello ${ a + b } world ${ a * b}`; //Hello 15 world 50

Number的扩展

Number.isFinite(),Number.isNAN()

新增Number.isFinite()Number.isNAN()isFinite()检查是否是有限(finite),和不是InfinityisNAN()检查一个值是否为NaN。

1
2
3
4
5
6
7
8
9
10
11
12
Number.isFinite(1); // true
Number.isFinite(0.1); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('1'); // false

Number.isNaN(NaN) // true
Number.isNaN(1) // false
Number.isNaN('1') // false
Number.isNaN(true) // false
Number.isNaN('true' / 0) // true

Number.parseInt(), Number.parseFloat()

ES6把全局方法parseInt()parseFloat,封装到了Number对象下

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()

Number.isInteger()判断一个数值是否为整数

1
2
Number.isInteger(2.0) // true
Number.isInteger(2.1) // false

JavaScriptc采用IEEE 754标准。数值存储为64位双精度格式,数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位)在第54位以后会丢弃

1
Number.isInteger(2.0000000000000002) // true

Function的扩展

ES6为Function加了些新的特性

默认值

指定参数默认值

1
2
3
4
5
6
7
function  test(x = 0, y = 0) {
this.x = x;
this.y = y;
}

const p = new Point();
p // { x: 0, y: 0 }

指定参数必须,赋值

1
2
3
4
5
6
7
8
9
function a() {
throw new Error('Missing parameter');
}

function b(c = a()) {
return c
}b

b() // Error: Missing parameter

rest

rest 参数用于获取函数的多余参数;(...变量名); 用rest后不能有其他参数 ;

1
2
3
4
5
6
7
8
9
10
function add (...values){
let num = 1;

for(let val of values){
num += val;
}
return num ;
}

add( 2 , 3 , 4); // 10
严格模式

函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

name 属性

返回该函数的函数名

1
2
function foo(){}
foo.name // "foo"
箭头函数

=> 箭头函数

1
2
3
4
5
var f = v => v;
// 等同于
var f = function (v) {
return v;
};

如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

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

rest 参数与箭头函数结合

1
2
3
4
5
6
7
8
9
const numbers = (...nums) => nums;

numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]

const headAndTail = (head, ...tail) => [head, tail];

headAndTail(1, 2, 3, 4, 5)
// [1,[2,3,4,5]]

注意点

1.函数内this对象,指向调用它的function
2.不可以当作构造函数;
3.不可以使用arguments对象,可以使用rest参数
4.不能用作 Generator 函数

尾调用优化

尾调用(Tail Call),某函数在最后一步是调用另外一个函数;

1
2
3
function a(x){
b(x)
}

执行b()a()未结束。

尾调用优化
🌰例子,用return来结束a方法。那么在执行过程中,调用栈的调用帧只有一条,这样就可以节省很大一部分内存;
注意必须使用严格模式

1
2
3
4
5
6
7
8
"use strict"
function a(x){
return b(x)
}
function b(x){
console.log(x)
}
a();

尾递归

函数自身调用自身,称为归递。在尾部调用自己,称为尾归递;

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误。尾递归可以永远防止“栈溢出”错误。

1
2
3
4
5
function factorial(n){
if( n === 1 ) return 1;
return n * factorial( n - 1 )
}
factorial(5)//120

return n * factorial( n - 1 )factorial()需要不断下探直到满足if( n === 1 ) return 1。保存多个n的调用记录。

用尾递归,只会有一个调用记录。

1
2
3
4
function factorial(n,total){
if( n === 1 ) return total;
return factorial( n - 1 , n * total )
}

每一次调用factorial()都会保存结果到total,不会像上面不断在函数内部调用。

Array的扩展

扩展运算

扩展运算符:...,将一个数组转化为用逗号分隔的参数序列。

1
2
3
4
console.log( ... [ 1 , 2 , 3 ] )
//1 2 3
console.log( 0 , ... [ 1 , ... [ 2 , 3 ] ])
//0 1 2 3

函数的调用:

1
2
3
4
5
6
7
8
function sum( a , b ){
return a + b
}

let arr = [ 3 , 4 ];

sum( ...arr );
// 7

扩展运算符的应用

复制
在js中数组是复合数据类型,直接复制,是指向底层数据地址的。而不是开辟新的底层数据地址。

1
2
3
4
const a1 = [1, 2];
const a2 = [...a1];
a1 === a2
// false

合并

1
2
3
4
5
6
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

let arr4 = [...arr1 , ...arr2 , ...arr3]
//[ 'a', 'b', 'c', 'd', 'e' ]

字符串转化为数组

1
2
[...'hello']
//['h','e','l','l','o']
Array.from()

Array.from()转成真正的数组

1
2
3
4
5
6
7
8
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
Array.from(arrayLike);
//['a', 'b', 'c']
Array.of()

Array.of()将多个人数值,合并成数组。这个方法主要是弥补Array()返回结果不一样。

1
2
3
4
5
6
Array.of(1,23,341,)
// [ 1, 23, 341 ]
Array()
//[]
Array(3)
//[empty × 3]
数组实例的function
copyWithin()

copyWithin()指定位置的成员复制到其他位置上。

1
2
3
4
Array.prototype.copyWithin(target, start = 0, end = this.length)

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

find() & findIndex()

find()用于找出第一个符合条件的数组成员。不满足undefined
findInde()用于找出第一个满足条件成员位置。不满足-1

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

fill()覆盖,填充一个数组。

1
2
3
4
['a', 'b', 'c'].fill(2)
//[2, 2, 2]
['a', 'b', 'c'].fill(2, 1, 2)
////['a', 2, 'c']
includes()

includes()判断某个数组是否包含给定的值。

1
2
3
4
[1, 2, 3].includes(2)     
// true
[1, 2, 3].includes(4)
// false
flat() & flatMap()

flat() 把数组‘拉平’展开。参数Number&Infinity无限展开。可自定义’拉平‘层速,默认为1

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

flatMap()对原数组的每个成员先执行map()在执行flast()。不过只能展开一层数组。

1
2
[2, 3, 4].flatMap((x) => [x, x * 2])
//[2, 4, 3, 6, 4, 8]

object的扩展

简洁表示法

ES6可以直接在打括号里面,直接写入函数、变量,直接做对象属性方法。

属性简写

1
2
3
4
5
6
const foo = 'bar';
const bar = { foo }
// { foo : 'bar'}

//等同于
const bar = { foo : foo }

方法简写

1
2
3
4
5
6
7
8
9
let birth = '2000/01/01';

const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};

属性名表达式

JavaScript定义对象的属性,有两种方法,一种是识符作为属性名,一种是表达式作为属性名。

1
2
3
4
//第一种
Obj.foo = true;
//第二种
Obj[ 'a' + 'bc' ] = '123';

🌰例子:

1
2
3
4
5
6
7
8
9
10
11
12
let lastWord = 'last word';

const a = {
'firstword': 'hello',
[lastWord]: 'world',
['na' + 'me'](){ return this.firstword + lastWord}
};

a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
a.name()//"hellolast word"

属性名表达式,不能是对象

1
2
3
4
5
6
const key = {a: 1};
const myObject={
[key] : 'value',
}

myObject //{ '[object Object]': 'value' }
name 属性

name返回当前函数名。

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

super关键字

关键字super,指向当前对象的原型对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
const proto = {
foo: 'hello'
};

const obj = {
foo: 'world',
find() {
return super.foo;
}
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
新增方法
Object.is()

Object.is()与严格比较运算符===的行为基本一致。ES5中=====,它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身。

1
2
3
4
5
+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.assign()

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

1
2
3
4
5
6
7
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

assign()为浅拷贝,更改源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。同名属性的替换,遇到同名属性,assign()不是添加,而是覆盖。

Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors()获取一个对象的所有自身属性的描述符。
Object.getOwnPropertyDescriptors(obj)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const obj = {
foo: 123,
get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }

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

Object.keys()返回一个数组,是所有可遍历(enumerable)属性的键名。

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

Object.values()返回一个数组,是所有可遍历(enumerable)属性的键值。

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

Object.entries()返回一个数组,是所有可遍历(enumerable)属性的键值对数组。

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

Object.fromEntries()将一个键值对数组转为对象。

1
2
3
4
5
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
坚持原创技术分享,您的支持将鼓励我继续创作!