ES6小计(2)

7. 函数的扩展

7.1 参数的默认值

函数定义时,可以直接用等于为参数设定默认值。传参全等于undefined的时候,默认值有效。参数默认值与解构赋值同时使用时,参数默认值先有效,其次再进行解构赋值。
函数添加了length属性,表示函数参数总个数-已设定默认值的参数个数。

function log(x, y = 'Y') { console.log(x, y) }
log('X') // XY
(function(a, b, c = 5) {}).length // 2

函数的变量初始化的作用域不属于函数内部,而在函数外层。

let x = 1
function f(y = x) {
    let x = 2
    console.log(y)
}
f() // 1

7.2 rest参数

函数传参时,可以使用...变量名的方式定义函数参数,变量名为一个传参组成的数组。rest参数后不能再有参数。

function f(...arr) {
    console.log(arr)
}
f(1, 2, 3) // [1, 2, 3]

7.3 ...扩展运算符

类似于rest参数的逆运算。将数组返还为多个项目参数。

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

常有以下使用方法:

7.3.1 不定参数个数的传参。

let ag = ['name1', 1, 'name2', 2, 'name3', 3]
redis.client.hmset('key', ...ag, function(err, data) {}) // 简化传参的书写,此句等同于下句
redis.client.hmset('key', 'name1', 1, 'name2', 2, 'name3', 3, function(err, data) {})

7.3.2 数据合并

let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
arr1.push(...arr2) // arr1 = [1, 2, 3, 4, 5, 6]
arr1.concat(arr2)
[...arr1, ...arr2]

7.3.3 字符串转数组

[...'hello'] // ['h', 'e', 'l', 'l', 'o'] 可识别UTF-16,依据数组长度,可有效判断字符串长度

7.3.4 类数组对象转换为真数组

[].slice.call(arrLike)
Array.from(arrLike)
[...arrLike]

7.4 箭头函数

用箭头=>定义的函数。

var f = v => v // 等同于如下:
var f = function(v) {
    return v
}

var sum = (num1, num2) => { return num1 + num2 }

箭头函数仅仅是简化了函数定义的书写,并没有新增多少实际有用的功能。为了便于阅读,建议箭头函数的参数部分的()不要省略。
箭头函数与普通函数的区别有以下四点:

  1. 箭头函数的this指向定义时所在对象,不是使用时所在对象;也可以说是箭头函数没有自己的this对象,在其内使用this其实是查找到外层的this
  2. 不能使用new,不可当做构造函数;
  3. 不能使用arguments对象,但可以使用rest参数;箭头函数没有自己的arguments参数;
  4. 不可使用yield命令。

7.5 尾调用优化与尾递归优化

函数尾调用时,不需要用到上层函数的环境时,使用尾调用就会不将上层环境保留在内存中,从而节省内存。尾递归同理。但是,只有开启严格模式,尾调用优化才会生效。

8. 对象的扩展

8.1 对象属性与方法名的简洁缩写

es6允许在对象中只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。

let x = 1, y = 2
let obj = {x, y} // let obj = {x: 1, y: 2}
let objF = {
    testFunc(x) {
        return x
    }
}

8.2 属性名可用表达式定义

用表达式作为属性名时,表达式要放在[]内。属性名表达式与简洁表示法不能同时使用,否则会报错。

obj['a' + 'b'] = 123
obj = {
    abc: 123,
    ['a' + 'b']: 321
}
var foo = 'bar'
var baz = { [foo] } // error
var baz = { [foo]: 'abc' } // {bar: 'abc'}

8.3 Object.is()

Object.is()用来比较两个值是否严格相等,类似于===,但有以下两点不同:

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

8.4 Object.assign()

Object.assign(目标obj, 源obj1, 源obj2...),将源obj的可枚举属性复制到目标obj上,靠后的源obj属性会覆盖靠前的源obj的同名属性。继承的属性不会复制。该复制只是浅复制,只会复制当前层。

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

常用的应用有:克隆对象、合并多个对象、为属性指定默认值

8.5 属性的可枚举性

遍历对象属性时,注意使用的遍历方法与可遍历属性的关系。
for...in遍历时,会遍历继承属性,不会遍历不可枚举属性。
Object.keys()返回自身可枚举属性组成的数组。
JSON.stringify()只字符串化自身可枚举属性。
Object.assign()只复制自身可枚举属性。
Reflect.enumerate()返回for...in会遍历的属性组成的数组。
故,循环遍历时,为了避免继承上的属性,建议用Object.keys()获取属性名后再循环属性名数组进行遍历。

9. Symbol

Symbol是一个新的原始数据类型,表示独一无二的值。成为第七种数据类型:undefined、null、boolean、string、number、object、symbol。Symbol就是一个类似于不会重复的字符串。

9.1 Symbol类型的创建

Symbol类型不是对象,不能用new方式创建。创建函数为Symbol(str),创建时,可以传入一个字符串参数,用来描述该Symbol值。Symbol值不能与其余类型值进行运算,但可以显示转换为字符串和布尔值。

let s1 = Symbol('s1')
let s2 = Symbol('s1')
let s3 = Symbol('s3')

s1 === s2 // false
console.log(s1) // Symbol(s1)
console.log(String(s1)) // Symbol(s1)
console.log(s1.toString()) // Symbol(s1)
console.log(Boolean(s1)) // true

9.2 Symbol作为属性名

Symbol作为属性名时,不能用.运算符,只能用[]变量传值。

let s1 = Symbol()
a[s1] = 123
var a = {}
a = {
    [s1]: 123
}
Object.defineProperty(a, s1, {value: 123})

9.3 对象Symbol属性的遍历

Symbol作为属性名时,不会被for...infor...of循环遍历到,也不会被Object.keys()Object.getOwnPropertyNames()返回。可以通过Object.getOwnPropertySymbols()通过数组返回。因此,可以为对象定义一些非私有,但只希望用于内部的方法。

9.4 Symbol.for()Symbol.keyFor()

当需要使用同一个Symbol值时,除了通过变量传参以外,还可以通过Symbol.for()获取。Symbol.for()的机制是检索全局中是否有注册该描述的Symbol值,若有,则返回该值;若没有,则注册后返回该值。Symbol.keyFor()则是通过Symbol值,获取注册描述。Symbol()创建的Symbol值不会进行注册。

Symbol.for('desc') === Symbol.for('desc') // true
Symbol('desc') === Symbol.for('desc') // false
let s1 = Symbol.for('desc')
let s2 = Symbol('desc')
Symbol.keyFor(s1) === 'desc' // true
Symbol.keyFor(s2) // undefined

es6内置的Symbol值此处不讲,有需求可以查询相应资料。

TO BE CONTINUED!