对象的扩展

9.1属性的简洁表示法

ES6允许直接写入变量和函数作为对象的属性和方法。这样的书写更加简洁。
	var foo = 'bar';
	var baz = {foo};
	baz //{ foo: "bar"}

	//等同于
	var baz = {foo:foo};
	上面的代码表明,ES6允许在对象中只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。下面是另一个例子,

	function f(x,y) {
		return {x,y};
	}

	//等同于
	funciton f(x,y) {
		return {x:x,y:y};
	}

	f(1,2) //Object {x: 1,y: 2}
	除了属性简写,方法也可以简写。

	var o = {
		method() {
			return "Hello!";
		}
	};
	//等同于

	var o = {
		method:function() {
			return "Hello!";
		}
	}
	
	这种写法用于函数的返回值会非常方便。
	function getPoint() {
		var x = 1;
		var y = 10;
		return {x,y};
	}
	getPoint()
	// {x:1,y:10};

9.2 属性名表达式

JavaScript语言定义对象的属性有两种方法。
	//方法一
	obj.foo = true;
	//方法二
	obj['a'+'bc'] = 123;
	上面的方法一是直接用标识符作为属性名;方法二是用表达式作为属性名,这时要将表达式放在方括号内,
	但是,如果使用字面量方式定义对象(使用大括号),则在ES5中只能使用方法yi(标识符)定义属性
	var obj = {
		foo:true;
		abc:123
		};
	ES6允许字面量定义对象时用方法二(表达式作为对象的属性名),即把表达式放在方括号内。
	let propKey = 'foo'l
	let obj = {
		[propKey]:true,
		['a'+'bc']:123
	};
	下面是另一个例子。

	var lastWord = 'last word';
	var a = {
		'first word':'hello',
		[lastWord]:'world'
	};

	a['first word'] //"hello"
	a[lastWord ] //"world"
	a['last word'] //"world"
	表达式还可以用于定义方法名。
	let obj = {
		['h' + 'ello']() {
			return 'hi';
		}
	};
	obj.hello() //hi
	

9.3 方法的name属性

函数的name属性返回函数名。对象方法也是函数,因此也有name属性。
	const person = {
		sayName() {
			console.log('hello!');
		}
	};
	person.sayName.name //"sayName"
	上面的代码中,方法的name属性返回函数名(即方法名)。
	如果对象的方法使用了取值函数

如果对象的方法使用了取值函数(getter)和存值函数(setter)则name 属性不实在该方法上面,而是在该方法属性的描述对象的get和set属性上面,返回值是方法名加上get和get。

有两种特殊情况:bind方法创造的函数,name属性返回“bound”加上原函数的名字,Function构造函数创造的函数,name属性返回anonymous

9.4 Object.is()

ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符( ===)。他们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0.JavaScript缺乏这样一种运算:在所有环境中,只要两个值是一样的,他们就应该相等。
ES6提出 “Same-value equality”(同值相等)算法用来解决这个问题。Object.is就是部署这个算法的新方案。它用来比较两个值是否严格相等,与严格相等运算符( === )的行为基本一致。
不同之处只有两个:一是+0 不等于-0 二十NaN等于自身。

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

9.5 Object.assign()

9.5.1 基本用法
Object.assign 方法用于将源对象(source)的所有可枚举属性复制到目标对象(target)。

	var target = { a:1 };

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

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

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象
注意! 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
	var target = { a:1,b:1 };

	var source1 = { b:2,c:2 };
	var source2 = { c:3 };

	Object.assign(target,source1,source2);
	
	target // {a:1,b:2,c:3}
	
	如果只有一个参数Object.assign会直接返回该参数。
	
	var obj = { a:1 };
	Object.assign(obj) === obj //true;
	
	如果该参数不是对象,则会先转成对象,然后返回
	由于undefinednull无法转为对象,所以如果将他们作为参数,就会报错。
	
	Object.assign(undefined)  //报错;
	Object.assign(null) //报错;

	如果非对象参数出现在源对象位置,那么处理规则将有所不同,首先这些参数都会转成对象,如果无法转成对象便会跳过。这意味着。如果undefinednull不在首参数变不会报错。
	
	var obj= { a:1 };
	Object.assign(obj,undefined) === obj //true;
	Object.assign(obj,null) === obj //true;
	
	其他类型的值(即数值,字符串和布尔值)不再首参数也不会报错。但是,除了字符串会以数组的形式复制到目标对象,其他值都不会产生效果。
	

Object.assign复制的属性是有限制的,只复制源对象的自身属性(不复制继承属性)。也不复制不可枚举的属性

9.5.2 注意点

Object.assign方法实行的浅复制,而不是深复制。也就是说,如果源对象某个属性的值是对象,那么目标对象复制得到的是这个对象的引用。

	var obj1 = {a: {b:1}};
	var obj2 = Object.assign({},obj1);
	obj1.a.b = 2;
	obj2.a.b //2
上面的代码中,源对象obj1的a属性的值是一个对象,Object.assign复制得到的是这个对象的引用。这个对象的任何变化都会反映到目标对象上面。
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换而不是添加。
	var target = { a: { b:'c',d: 'e'} }
	var source = {a : {b: 'hello' }}
	Object.assign(target,source)
	//{ a: {b: 'hello' } }
上面的代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a:{ b:'hello', d:'e' } }的结果。这通常不是开发者想要的,需要特别小心。
有一些函数库提供Object.assign的定制版本,可以解决浅复制的问题,得到深复制的合并。
注意,Object.assign可以用来处理数组,但是会把数组视为对象来处理

	Object.assign([1,2,3],[4,5])
	// [4,5,3]
	上面的代码中,Object.assign把数组视为属性名为012的对象,因此目标数组的0号属性4覆盖了原数组的0号属性1

9.5.3 常见用途

Object.assign方法有很多用处。
为对象添加属性
为对象添加方法

Object.assign(SomeClass.prototype,{
	someMethod(arg1,arg2){
		。。。}anotherMethod() {
		...
	}
});

克隆对象

function clone(origin){
	return Object.assign({},origin);
	}
上面的代码将原始对象复制到一个空对象中,就得到了原始对象的克隆。
不过,采用这种方法只能克隆原始对象自身的值,不能克隆他继承的值。如果想要保持继承链,可以采用下面的代码

	function clone(origin) {
		let originPrptp = Object.getPrototypeOf(orighin);
		return Object.assign(Object.create(originProto),origin);
	}

合并对象
将多个对象合并到某个对象。

	const merge = (target,...sources) => Object.assign(target,...sources);
	如果希望合并后返回一个新对象,可以改写上面的函数,对一个空对象合并。
	const merge = (...sources) => Object.assign({},...sources);

为属性指定默认值

9.6 属性的可枚举性

对象的每一个属性都具有一个描述对象,用于控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
	let obj = {foo: 123};
	Object.getOwnPropertyDescriptor(obj,'foo')
	//{
	//	value :123,
	//	writable:true,
	//  enumerable:true,
	//	configurable:true,
	//}
	描述对象的enumerable属性称为可枚举性,如果该属性为false,就表示某些操作会忽略当前属性

ES5有3哥操作会忽略enumerable为false的属性。

  • for…in循环,只便利对象自身的和继承的可枚举属性,
  • Object.keys();只返回对象自身的所有可枚举属性。
  • JSON.stringify(); 只串行化对象自身可枚举属性。
    ES6新增1个操作Object.assign(),会忽略enumerable为false的属性,只复制对象自身的可枚举属性。
    这4个操作之中,只有for … in 会返回继承的属性。实际上,引入enumerable的最初目的就是让某些属性可以规避掉for…in操作,比如,对象原型的toString方法以及数组的length属性,就通过这种手段而不被for…in遍历到。
	Object.getOwnPropertyDescriptor(Object.prototype,'toString').enumerable
	// false
	Object.getOwnPropertyDescriptor([],'length').enumerable
	// false
	上面的代码中,toString和length属性的enumerable都是false,因此for...in不会遍历到这两个继承自原型的属性。
	另外,ES6规定,所有的class的原型的方法都是不可枚举的。

9.7 属性的遍历

ES6一共有5种方法可以遍历对象的属性。

  1. for…in
    for…in循环遍历对象自身的和继承的可枚举属性

  2. Object,keys(obj)
    Object,keys返回一个数组,不包括对象自身的(不含继承的)所有可枚举属性

  3. Object.getOwnOrioertyNames(obj)
    Object.getOwnOrioertyNames返回一个属性,包含对象自身的所有属性

  4. Object.getOwnPropertySymbol(obj)
    Object.getOwnPropertySymbol返回一个数组,包含对象自身所有的Symbol属性。

  5. Reflect.ownKeys(obj)
    Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管属性名是Symbol还是字符串,也不管对象是否可枚举。

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

9.8.1 Object.keys()
ES5引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不包含继承的)所有可遍历属性的键名。

	var obj = { foo:'bar',baz:42 };
	Object.keys(obj)
	//{"foo","bza"}
ES2017中有一个提案,其中引入了与Object.keys配套的Object.values和Object.entries作为遍历一个对象的补充手段,供for...of循环使用。

	let {keys,values,entries} = Object;
	let obj = { a:1,b:2,c:3 };
	for (let key of keys(obj)){
		console.log(key);// 'a','b','c'
	}
	for(let value of values(obj)) {
		console.log(values);// 1,2,3
	}
	for(let (key , value) of entries(obj)) {
		console.log(values);// ['a':1],['b':2],['c':3],
	}
	

9.8.2 Object.values()

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历顺序ing的键值。

	var obj = {foo:'bar',baz:42};
	Object.values(obj)
	//["bar",42]
	var obj = [100:'a',2:'b',7:'c'};
	Object.values(obj)
	//["b","c","a"]
***属性名为数值的属性,是按照数值大小从小到大遍历的,因此返回的顺序是b,c,a***

	Object.values()只返回对象自身的可遍历属性。
	var obj = Object.create({},{p:{value:42}});
	Object.values(obj) //[]
	上面的代码中,Object.create方法的第二个参数添加的对象属性(p)如果不显示声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性,只要把enumerable改为true,Object.values就会返回属性P的值

	var obj = Object.create({},{p:
	{
		value:42,
		enumerable:true
	}
});
Object.values(obj) [42]
Object.values会过滤属性名为Symbol值的属性
Object.values({ [Symbol()];123,foo:'abc'});
//['abc']
如果Object.values方法的参数是一个字符串,则会返回各个字符传组成的一个数组.
Object.values('foo')
//['f','o','o']
上面的代码中,字符串会先转成一个类是数组的对象,字符串的每个字符就是该对象的一个属性,因此,Object.values返回每个属性的键值就是各个字符组成的数组.
	如果参数不是对象Object.values会先将其转为对象.由于数值和布尔值的包装对象都不会为实例添加非继承的属性,所以Object.values会返回空数组.
	Object.values(42)//[]
	Object.values(true)//[]

9.8.3 Object.entries

Object.entries方法返回一个数组,成员是参数对象自身(不包含继承)所有可遍历的(enumerable)属性的键值对数组。

	var obj = { foo:'bar',baz:42 };
	Object.entries(obj)
	// [ [ "foo","bar"],{"baz",42] ]
	除了返回值不一样,该方法的行为与Object.value基本一致。
	如果原对象的属性名是一个Symbol值,该属性会被忽略。

9.12 Null传导运算符

编程实务中,如果读取对象内部的某个属性,往往需要判断该对象是否存在,比如要读取message.body.user.firstName,安全的写法如下。

	const firstName = (message
		&& message.body
		&& message.body.user
		&& message.body.user.firstName) || 'default';
这样层层判断非常麻烦,因此现在有一个提案,
	const firstName = mewssage?.body?.user?.firstName || 'default';
上面的代码有3?.运算符,只要其中一个返回nullundefined,就不再继续运算,而是返回undefined

“Null传导运算符”有4种用法

  • obj?.prop;读取对象属性
  • obj?.[expr];同上
  • func?.(…args);函数或对象方法的调用
  • new C?.(…args)
    传导运算符之所以写成obj?.prop而不是obj?prop,是为了方便编译器能够区分三元运算符?:
//如果a是null或undefined返回undefined
//否则返回a.b.c().d
	a?.b.c().d

//如果a是null或undefined,下面的语句不产生任何效果
//否则执行a.b = 42
	a?.b = 42
//如果a是null或undefined,下面的语句不产生任何效果
delete	a?.b 
  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值