深入理解ES6--对象、函数扩展

时间:2021-10-16 00:55:44


扩展对象的功能性

属性名称简写

let name = 'ligang'
let person = {
name,
sayName() {
console.log(name)
}
}

javascript引擎会在访问作用域中查找其同名变量;如果找到,则变量的值被赋给对象字面量的同名属性。

Object.is()

​Object.is()​​方法是为了弥补全等运算符’===’的准确性,其接受两个参数,如果两个参数类型相同且具有相同的值,则返回true。

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

console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
console.log(isNaN(NaN)) // true

注意:​map.get()​​​底层使用的就是​​Object.is()​​进行判断获取

let myMap = new Map().set('0', 'xxx')
myMap.get(0) // undefined
myMap.get('0') // xxx

Object.assign()

对象组合,浅拷贝

自有属性枚举顺序

ESMAScript6严格定义了对象的自有属性被枚举时的返回顺序,这会影响到​​Object.getOwnPropertyNames()​​​方法及​​Reflect.ownKeys​​​返回属性的方式,​​Object.assign()​​方法处理属性的顺序也将随之改变。

  • 所有数字键按照升序排序;
  • 所有字符串按照它们被加入对象的顺序排序;
  • 所有symbol键按照它们被加入对象的顺序排序。
let obj = {
a: 1,
0: 1,
c: 1,
2: 1,
b: 1,
1: 1
}
Object.getOwnPropertyNames(obj) // ["0", "1", "2", "a", "c", "b"]

注意,for-in并非所有厂商都遵循相同的实现方式;​Object.keys()​​JSON.stringify()​​for-in​相同,所以目前也不明确!

原型访问super

let person = {
sayHi() {
return 'hi'
}
}
let friend = {
sayHi() {
// 获取其原型方法
return Object.getPrototypeOf(this).sayHi.call(this) + ' friend'
}
}
let friend2 = {
sayHi() {
return super.sayHi() + ' friend2'
}
}

Object.setPrototypeOf(friend, person);
friend.sayHi() // "hi friend"

Object.setPrototypeOf(friend2, person);
friend2.sayHi() // "hi friend2"

函数

函数形参的默认值

ES5方式

function test(name) {
name = (typeof name !== 'undefined') ? name : 'ligang'
// name = name || 'ligang' // 当name为假值时会出现问题!!如,test(0)
return name
}

ES6:注意,对于默认参数值,null是一个合法值

function test(name = 'ligang') {
return name
}
test(undefined) // ligang
test(null) // null

默认参数表达式

function getvalue(n) {
return n
}
function add(first, second = getvalue(first)) {
return first + second
}
add(100) // 200

注意:

  • ​getvalue()​​​方法只有当调用​​add()​​函数且不传入第二个参数时才会调用;
  • 引用参数默认值时,只允许引用前面参数的值,即先定义的参数不能访问后面定义的参数。

展开运算符

数组

let ary = [1, 2, 3]
let max = Math.max(...ary, 5)
console.log(max) // 5

对象

let obj = {a: 1, b: {c: 2}}
let obj2 = {...obj}
obj.b.c = 3
console.log(obj2) // {a: 1, b: {c: 3}} 浅拷贝
特别说明:
ES6中展开运算符只针对iterable才起作用,默认有数组、set、map和字符串。并不包含对象!ES6规范中也并未将展开运算符支持对象,但是目前的主流浏览器Chrome和firefox均已实现该特性。这意味着如果想在低版本浏览器中使用需要特别的Babel插件进行转换!​​object-rest-spread​

深入理解ES6--对象、函数扩展

明确函数的多重用途

JavaScript函数有两个不同的内部方法:[[Call]][[Construct]](构造函数)。通过new关键字调用函数时,执行的是[[Construct]]函数,将this绑定到实例上;如果不通过new调用,则执行[[Call]]函数

function Person(name) {
if (this instanceof Person) {
this.name = name
} else {
throw new Error('必须通过new关键词创建')
}
}
let p = new Person('ligang')
Person.call(p, 'lee') // 不会报错

所以,我们无法区分通过​​Person.call()​​​(或​​Person.apply()​​)调用还是new关键字创建!

元属性new.target可以解决,调用[[call]]方法,new.target的值为undefined。

function Person(name) {
if (typeof new.target !== 'undefined') {
this.name = name
} else {
throw new Error('必须通过new关键词创建')
}
}
let p = new Person('ligang')
Person.call(p, 'lee') // 必须通过new关键词创建

箭头函数

  • 没有this、super、arguments河new.target绑定;
  • 不能通过new关键字调用;
  • 没有原型;
  • 不可以改变this的绑定;
    如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this;否则,this的值会被设置为undefined。
let person = {
name: 'ligang',
sayName() {
return (() => this.name)()
}
}
  • 不支持重名的命名参数。

尾调用优化

ES6对尾调用(函数作为另一个函数的最后一条语句被调用)进行了优化。ES5中,尾调用实现为“创建一个新的栈,将其推入调用栈来表示函数调用,即未用完的栈都在内存中,当调用栈变大会造成程序内存溢出”。ES6中不在创建新栈,会清除并重新利用当前栈。

  • 尾调用不访问当前栈的变量(即不是一个闭包);
  • 在函数内部,尾调用是最后一条语句;
  • 尾调用的结果作为函数值返回。
function factorial(n) {
if (n <= 1) {
return 1
}
return n * factorial(n - 1)
}

优化后

function factorial(n, p = 1) {
if (n <= 1) {
return 1 * p
}
let result = n * p
return factorial(n - 1, result)
}