对象的扩展
引用自http://es6.ruanyifeng.com/#docs/object
属性的简洁写法
-
ES6允许在大括号里,直接写入变量和函数,作为对象的属性和方法,更加简洁
属性简写 function f(foo,bar){ return (foo,bar) } f(1,2)//{foo:1,bar:2} 这时属性名就是变量名,属性值就是变量值
方法简写 const foo ={ bar(){ return 'baz' } }
const cart = { _wheels: 5, get wheels() { return this._wheels; }, set wheels(value) { if (value < this._wheels) { throw new Error('数值太小了!'); } this._wheels = value; } } //setter属性的赋值器,getter属性的取值器 //set 下wheels大于_wheels时,返回这个值 //小于时,抛错 console.log(cart.wheels = 6)
属性名表达式
-
js定义对象的属性,有两种方法
1.直接使用标识符作为属性名
2.用表达式作为属性名
方法1: var obj = { foo:true, abc:123 } 方法2: let foo = 'foo'; let obj ={ [foo]:true, ['a'+'bc']:123 } ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
-
表达式还可以定义方法名
let obj ={ ['h'+'ello'](){ return 'hi' } } obj.hello()//hi
-
<mark>属性名与简洁表示法不能同时使用,会报错</mark>
// 报错 const foo = 'bar'; const bar = 'abc'; const baz = { [foo] };
-
<mark>属性名表达式如果是一个对象,默认将对象转为[object Object]</mark>
const keyA = { a:1} const keyB = { b:2} const myObject = { [keyA]:'valueAa', [keyB]:"valueBb" } console.log(myObject)//{[object Object]: "valueBb"} //[keyA]和[keyB]得到的都是[object Object],所以[keyB]覆盖了[keyA]
方法的name属性
-
函数的name属性,返回函数名。对象方法也是韩式们也有name
// const person ={ sayName(){ console.log('hello!') } } console.log(person.sayName.name)//sayName 返回函数名
-
如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,
而是该方法的属性的描述对象的get和set属性上
const obj = { get foo() { }, set foo(x) { } } console.log(obj.foo.name)//Uncaught TypeError Object.getOwnPropertyDescriptor(obj, prop) //obj:需要查找的目标对象 //prop:目标对象内属性
-
2种特殊情况;bind方法创造的函数,
name属性返回bound加函数名;
Function构造函数创造的函数,name属性返回
var foo= (new Function()).name console.log(foo)//anonymous var dosth = function(){ } console.log(dosth.bind().name)//bound dosth
-
如果对象的方法时一个Symbol值,name属性返回的是这个Symbol值的描述
const key1 = Symbol('desc') const key2 = Symbol(); let obj = { [key1](){ }, [key2](){ }, } console.log(obj[key1].name)//[desc] console.log(obj[key2].name)//''
属性的可枚举性和遍历
属性的可枚举性
-
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为
Object.getOwnPropertyDescriptor方法可以获取改属性的描述对象,
let obj ={ foo:123} var baz = Object.getOwnPropertyDescriptor(obj,"foo") //{ configurable: true//可枚举性,如果该属性为false,表示某些操作会忽略当前属性 enumerable: true value: 123 writable: true } /*configurable: false时, 目前有4个操作会忽略enumerable为false的属性 for..in循环:只是遍历对象自身的和集成的可枚举的属性 Object.keys():返回对象自身的虽有可枚举的属性的键名 JSON.stringify():只串行化对象自身的可枚举的属性 object.assin()只拷贝对象自身的可枚举属性 这4个操作,前3个时ES5就有的,最后一格Object.assign()是ES6新增的 其中, 只有for...in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理自身的属性 */ 操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。
属性的遍历
ES6共有5种方法可以遍历对象的属性
-
for…in:循环遍历对象自身的和集成的可枚举属性(不含Symbol属性)
-
Object.keys(obj):返回一个数组。包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名
-
Object.getOwnPropertyNames(obj,):返回一个数组,包含对自身的所有属性(不含Symbol属性,但包括不可枚举属性)的键名
-
Object.getOwnPropertySymbols(obj):
Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有 Symbol 属性的键名。 -
Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
以上5种方法属性遍历的次序规则:
首先遍历所有数值键,按照数组升序排列
其次遍历所有字符串键,按照加入时间升序排列
最后遍历所有Symbol键,按照加入时间升序排列
var bar = Reflect.ownKeys({
[Symbol()]:0,b:0,10:0,2:0,a:0})
console.log(bar)// ["2", "10", "b", "a", Symbol()]
super关键字
this关键字总是指向函数所在的当前对象
ES6新增 super关键字,指向当前对象的<mark>原型对象</mark>
const proto ={
foo:"hello"
};
const obj ={
foo:'world',
find(){
return super.foo
}
Object.setPrototypeOf(obj,proto)//Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null
//此时obj的原型指向proto,所以super.foo为proto中的foo
console.log(obj.find())//hello
注:<mark>super关键字表示原型对象时,只能用在对象的方法中,用在其他地方会报错</mark>
<mark>目前只有对象方法的简写可以让js引擎确认,定义的是对象的方法,从而可使 用super</mark>
对象的扩展运算符
- ES2018将(…)扩展运算符引用入到对象中
结构赋值
-
对象的结构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable),但尚未被读取的属性,分配到指定的对象上面。所有的键和值都会拷贝到新对象上
let { x,y,...z} = { x:1,y:2,a:3,b:4}//{x: 1, y: 2, a: 3, b: 4} //变量z是结构赋值所在的对象,它过去等号右边的所有尚未读取的键(a和b)将它们连同值一起拷贝过来
注:由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefined或者 null就会报错,因为它们无法转为对象
-
结构赋值必须是最后一个参数,否则报错 Rest element must be last element
-
解构赋值的拷贝是浅拷贝了,即当一个键的值是复合类型的值(数组,对象,函数)那么结构
赋值拷贝的是这个值的引用,而不是这个值的副本
-
扩展运算符的结构赋值,<mark>不能赋值继承自原型对象的属性</mark>
let o1 = { a:1}; let o2 = { b:2}; o2.__proto__ = o1; let { ...o3} = o2; console.log(o2.a)//1 02的原型__proto__指向了o1 console.log(o3)//{b: 2}//o3复制了o2,但是只复制了o2本身的属性,没有复制它的原型对象o1的属性 Object.create(proto[,propertiesObject]) //创建一个新对象,使用现有的对象来提供新创建的对象的proto 参数: proto : 必须。表示新建对象的原型对象,即该参数会被赋值到目标对象(即新对象,或说是最后返回的对象)的原型上。该参数可以是null, 对象, 函数的prototype属性 (创建空的对象时需传null , 否则会抛出TypeError异常)。 propertiesObject : 可选。 添加到新创建对象的可枚举属性(即其自身的属性,而不是原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。 3 返回值: 在指定原型对象上添加新属性后的对象。
const o = Object.create({ x:1,y:2}); o.z=3 console.log(o){ z: 3} /* z: 3 __proto__: x: 1 y: 2 */ let { x,...newObj} = o let { y,z}=newObj console.log(x,y,z)//1 undefined 3 变量x只是单纯的结构赋值,可以读取o继承的属性得到x=1; 变量y,z是扩展运算符的解构赋值,只能读取o自身的属性,所以变量z可以赋值成功, 而y取不到值
-
ES6规定,变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名
而不能是一个解构赋值表达式,
所以上式中 let {x,…{y,z}} = o //报错
-
结构赋值的用处是扩展某个函数的参数,引入其他操作
扩展运算符
-
对象的扩展运算符(…)用于去除参数对象的所有可比案例属性,拷贝到当前对象之中
-
<mark>数组是特殊的对象,所以对象的扩展运算符也可以用于数组</mark>
let foo ={ ...['a','b','c']} console.log(foo)//{0: "a", 1: "b", 2: "c"}
-
如果扩展运算符后面是一个空对象,则没有任何效果
let foo = { ...{ },a:1} console.log(foo)//{a: 1}
-
如果扩展运算符后面不是对象,则自动将其转为对象
// 等同于 {...Object(1)} { ...1} // {} //扩展运算符后面是整数1,会自动转为数值的包装对象Number{1}。 //由于该对象没有自身属性,所以返回一个空对象。
-
<mark>如果扩展运算符后面是字符串,自动转成一个类似数组的对象,返回的不是空对象</mark>
let foo ={ ...'foo'} console.log(foo)//{0: "f", 1: "o", 2: "o"}
-
对象的扩展运算符等同于使用Object.assign()方法
let a ={ baz:1} let foo = { ...a} let bar = Object.assign({ },a) console.log(foo,bar)//{baz: 1} {baz: 1} //Object.assign() //Object.assign(target, ...sources) target:目标参数, sources:源对象 //用于将所有可枚举属性的值从一个或多个源对象复制到目标对象
-
扩展运算符可以用于合并两个对象
let a = { foo: 1 } let b = { bar: 2 } let ab = { ...a, ...b } console.log(ab)//{foo: 1, bar: 2} Object.assign({ }, a, b) //{ foo: 1, bar: 2 }
-
<mark>可用来修改现有对象的部分属性</mark>
let oldV={ age:23 } let newV = { ...oldV, name:"liu" } console.log(newV)//{age: 24, name: "liu"}
-
与数组的扩展运算符一样,对象的扩展运算符后也可以跟表达式
let x =0 const obj ={ ...(x>1?{ a:1}:{ }),b:2 } console.log(obj)//{b: 2}