步遥——对象的扩展
对象的扩展
对象本身的数据结构的改变:
1:属性的简洁表示:允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简单
const foo = 'df'
const baz = {foo} //等同于{foo:foo}
baz // {foo: "df"}
2:方法也可以简写
const o = {
method(){return 'hello!'} //等同于 method:function(){return 'hello!'}
}
o.method() // 'hello!'
3:属性名表达式
es5只有标识符定义属性,es6允许表达式做对象的属性名,即把表达式放在方括号内。
let propKey = 'foo'
let obj= {
[propKey]:true
}
obj // {foo: true}
4:方法名也可以是表达式
let obj = {
['h' + 'eo']() {
return 'hi';
}};
obj.heo() // hi
如果属性名是对象,会被转为字符串[object Object]
对象的属性可以是字符串和symbol类型
5:对象方法的name属性
因为对象的方法名可以是字符串,symbol,方法也可以是get,set方法,所以name的值也有不同
普通的字符串,则返回对应的字符串
get,set方法的name需要在属性描述符的set,get属性上获取
const obj = {
get foo() {},
set foo(x) {}
};
obj.foo.name
// TypeError: Cannot read property 'name' of undefined
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
descriptor.get.name // "get foo"
descriptor.set.name // "set foo"
symbol值的会返回symbol的描述
6:属性的可枚举和遍历
每个属性都可以通过Object.getOwnPropertyDescriptor获取到任意一个属性的可配置的属性:比如值,是否可枚举,是否可配置,是否可修改等
class的原型的方法都是不可枚举的,在循环时尽量不要使用for...in ,而使用Object.keys();
7:遍历属性的方法:
7.1:for...in // 循环遍历对象自身的和继承的可枚举属性,不含symbol
7.2:Object.keys() // 返回一个数组,包括对象自身的不含继承的所有可枚举属性的键名,不含symbol
7.3:Object.getOwnPropertyNames //返回一个数组,包含对象自身的所有属性不包含symbol,但是包含不可枚举属性的键名
7.4:Object.getOwnPropertySymbols // 返回一个数组,包含对象自身的所有symbol属性的键名
7.5:Reflect.ownKeys //返回一个数组,包含对象自身的不含继承的所有键名,不管键名是symbol或字符串,也不管是否可枚举。
遍历顺序:
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串键,按照加入时间升序排列。
- 最后遍历所有 Symbol 键,按照加入时间升序排列。
8:super关键字
指向当前对象的原型对象,并且只能用在对象的方法中。目前只有对象方法的简写法可以让js引擎确认,定义的是对象的方法:
let obj ={
foo:2
}
let obj1 ={
method(){ //对象方法的简写中可以使用super
return super.foo
}
}
obj1.method()//2
setPrototypeOf & getPrototypeOf
Object.setPrototypeOf(obj,prototype) //将prototype设置为obj的原型
Object.getPrototypeOf(obj) // 返回obj的原型对象
8:对象的解构赋值:
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面
解构赋值要求等号右边是一个对象,undefined和null会报错
解构赋值必须是最后一个参数
解构赋值是浅拷贝
解构赋值不能复制继承自原型对象的属性
用处:扩展某个函数的参数,引入其他操作
9:扩展运算符
用于取出参数对象的所有可遍历属性,拷贝到当前对象之中
扩展运算符后面不是对象,则会自动转为对象
扩展运算符后是数字,布尔,undefined,null,都会返回空对象
对象的扩展运算符等同于使用Object.assign()方法
let aClone = {...obj} //
let aClone1 = Object.assign({},obj)//同上只拷贝对象实例属性,像完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法
const aClone2 = Object.assign(Object.create(Object.getPrototyOf(obj)),obj) // 将原型和对象合并
如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。 这用来修改现有对象部分的属性就很方便了,新的值会覆盖前面的属性
与数组的扩展运算符一样,对象的扩展运算符后面可以跟表达式
用处:合并对象
10:链判断运算符
const firstName = message.body.user.firstName;
es5:
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
es6:
const firstName = message?.body?.user?.firstName || 'default';
判断方法是否存在并执行
iterator.return?.()
obj1.method?.() // 2
obj1.aaa?.() // undefined
链判断语法:
* obj?.prop // 对象属性
* obj?.[expr] // 同上
* func?.(...args) // 函数或对象方法的调用
判断数组是否有值
let hex = "#C0FFEE".match(/#([A-Z]+)/i)?.[1];
链判断特点:
短路机制
delete运算符,失败则不删除
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响
链判断运算符的左侧不能是 super
链判断不能用来赋值
右侧不得为十进制数值
12:Null判断运算符 ??
左侧只有等于null或undefined的时候,才会返回右侧的值,如果为‘’ 或false的时候,还返回‘’或false
用处:
就是跟链判断运算符?.配合使用,为null或undefined的值设置默认值。
如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
lhs && middle ?? rhs //报错
(lhs && middle) ?? rhs // 不报错,必须使用括号表明优先级