计算属性
在引入计算属性之前,如果想使用变量的值作为属性,就必须先申明对象,然后使用中括号语法来添加。如下:
const nameKey = 'name'
const ageKey = 'age'
const jobKey = 'job'
let person = {}
person[nameKey] = 'zhangsan'
person[ageKey] = 30
person[jobKey] = '游戏达人'
console.log(person)//{name: 'zhangsan', age: 30, job: '游戏达人'}
但有了计算属性后,我们就可以在对象字面量中完成动态属性赋值
const nameKey = 'name'
const ageKey = 'age'
const jobKey = 'job'
let person= {
[nameKey]:'zhangsan',
[ageKey]: 30,
[jobKey]: '游戏达人'
}
console.log(person)//{name: 'zhangsan', age: 30, job: '游戏达人'}
方括号语法
方括号的语法 ,可以用于 设置复杂的属性名和以变量的方式访问属性。如下:
let user = {
name: "John",
age: 30,
job:'软件工程师'
}
// 这将提示有语法错误
user.likes birds = true
// 正确
user["likes birds"] = true;
console.log(user["likes birds"])//true
let key = 'job'
console.log(user.key) //undefined
console.log(user[key]) //软件工程师
点符号要求 key 是有效的变量标识符。这意味着:不包含空格,不以数字开头,也不包含特殊字符(允许使用 $ 和 _)。
属性存在性测试
javascript 中,当对象不存在某个属性,但又被访问时,并不会报错,而是会得到undefined,如下:
let user = {};
console.log( user.noSuchProperty === undefined ); // true 意思是没有这个属性
所以当我们要检查是否存在某个属性时,可以这样来检测。但有一种情况会存在问题,那就是属性值是‘undefined’的情况
let user = {
name:undefined
};
console.log( user.name === undefined );//显示 true,所以属性不存在吗?
这种情况很少发生,因为通常情况下不应该给对象赋值 undefined。我们通常会用 null 来表示未知的或者空的值。
为了解决这种情况,我们可以使用 in
操作符,语法如下:
"key" in object
let user = { name: "John", age: 30 ,job:"undefinde"};
console.log( "age" in user ); // true,user.age 存在
console.log( "blabla" in user ); // false,user.blabla 不存在。
console.log( "job" in user );//true user.job 存在
//做一个比较 这里就是不正确的结果
console.log( user.job === undefined );//显示 true,但这个属性是存在的
属性枚举顺序
Object
具有特别的顺序:
- 非负整数会最先被列出,排序是从小到大的数字顺序
- 其他属性则按照创建的顺序显示 ```javascript const symbol1 = Symbol(49); const symbol2 = Symbol(41); let codes = { “49”: “Germany”, “41”: “Switzerland”, “name”:’zhangsan’, “age”:30, ‘1.2’:’小数’, “-23”:’负数’, “44”: “Great Britain”, “1”: “USA”, symbol1:49, symbol2:41, };
for(let code in codes) { console.log(code); } // 1,41,44,49,name,age,1.2,-23,symbol1,symbol2
console.log(Object.keys(codes)) //[ “1”, “41”, “44”, “49”, “name”, “age”, “1.2”, “-23”, “symbol1”, “symbol2” ]
- for-in 循环和 Object.keys()的枚举顺序是不确定的,取决于 JavaScript 引擎,可能因浏览器而异(**目前测试到 edge firefox chrome 的输出是一样的 同上面的例子一样**)
- Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()和 Object.assign()的枚举顺序是确定性的。先以升序枚举数值键,然后以插入顺序枚举字符串和符号键
<a name="U1ks6"></a>
### 浅克隆深克隆
```javascript
let user = {
name: "John",
age: 30
};
let clone = {}; // 新的空对象
// 将 user 中所有的属性拷贝到其中
for (let key in user) {
clone[key] = user[key];
}
// 现在 clone 是带有相同内容的完全独立的对象
clone.name = "Pete"; // 改变了其中的数据
console.log( user.name ); // 原来的对象中的 name 属性依然是 John
我们也可以使用 Object.assign 方法来达成同样的效果。
clone = Object.assign({}, user)//{name: 'John', age: 30}
//如果被拷贝的属性的属性名已经存在,那么它会被覆盖:
clone = Object.assign( user,{age:40})//{name: 'John', age: 40}
以上的两个方式都只是一个浅克隆,当被复制的对象中有对象时,那么只会复制其引用。如下:
let user = {
name: "John",
age: 30,
job:{
title:'软件工程师',
company:'xxx科技'
}
};
let clone = Object.assign({}, user)
console.log(clone)
//age: 30
//job: {title: '软件工程师', company: 'xxx科技'}
//name: "John"
user.name = 'chentao',
user.job.company = 'yyy科技'
console.log(clone)
//修改了user,clone也被改变了
//age: 30
//job: {title: '软件工程师', company: 'yyy科技'}
//name: "John"
如上,当对象中还有对象时,浅克隆的方式复制的是对象的引用。当我们修改user或则clone,就会造成另一个对象也跟着修改了。
为了解决这个问题,我们应该使用一个循环来检查 user[key] 的每个值,如果它是一个对象,那么也复制它的结构。这就是深拷贝,如下的示例:
function cloneDeep(source,clone = {}){
for(let key in source) {
if(source[key] instanceof Object ){
clone[key] = {}
cloneDeep(source[key],clone[key])
}
else{
clone[key] = source[key];
}
}
return clone
}
这个方法只是对正常对象的处理,并没有考虑太多其它情况,如数组/Date对象。一般我们会采用现有的实现,例如 lodash 库的 _.cloneDeep(obj)
合并对象
ECMAScript 6 专门为合并对象提供了 Object.assign()方法。这个方法接受一个目标对象和一个或多个源对象。assign()会将源对象中自身可枚举的属性浅复制给目标对象。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标 对象上的[[Set]]设置属性的值。
如下:
/**
* 浅复制
*/
dest = {};
src = { id: 'src',foo:{name:111} };
result = Object.assign(dest, src);
console.log(dest.foo === src.foo) //true
/**
* 不可枚举的属性不会被合并到目标对象
*/
dest = {};
src = { id: 'src',foo:{name:111} };
Object.defineProperties(src, {
enumerable1: {
value:'enumerable1',
enumerable: false
},
enumerable2: {
value:'enumerable2',
enumerable: true
}
})
const result = Object.assign(dest, src)
console.log(result)
//enumerable2: "enumerable2"
//foo: {name: 111}
//id: "src"
/**
* 通过get和set来观察合并过程
*/
dest = {
set foo(value){
console.log(" invoke set:",value)
}
};
src = {
get foo(){
console.log("invoke get:",'bar')
return 'bar';
}
}
result = Object.assign(dest,src)
//invoke get: bar
//invoke set: bar
// 因为这里的设置函数不执行赋值操作
// 所以实际上并没有把值转移过来
console.log(result); // {}