基础
列表中的最后一个属性应以逗号结尾:
let user = {
name: "John",
age: 30,
}
这叫做尾随(trailing)或悬挂(hanging)逗号。这样便于我们添加、删除和移动属性,因为所有的行都是相似的.
多字词做属性名,用[]访问
let user = {
name: "John",
age: 30,
"likes birds": true // 多词属性名必须加引号
};
“likes birds”
就是一个多字词做为对象的属性名。
获取他的值得时候,不能像正常的user.name
那样获取。
需要使用[]
alert(user["likes birds"])//true
可以先定义后使用
let key = "likes birds";
delete user[key];
但是正常的user.name
这种点符号不可以像上面那样定义使用;
let key = "name";
alert( user.key ) // undefined
属性简写
function makeUser(name, age) {
return {
name, // 与 name: name 相同
age // 与 age: age 相同
// ...
};
}
存在性检查
===undefined
key in Object
let obj = {
test: undefined
};
alert( obj.test ); // 显示 undefined,所以属性不存在?
alert( "test" in obj ); // true,属性存在!
for … in
let user = {
name: "John",
age: 30,
isAdmin: true
};
for (let key in user) {
// keys
alert( key ); // name, age, isAdmin
// 属性键的值
alert( user[key] ); // John, 30, true
}
属性排序
- 整数属性(这里指的四不做任何更改的情况下转换为证书的字符串)
是按照数字顺序排的
let codes = {
"49": "Germany",
"41": "Switzerland",
"44": "Great Britain",
// ..,
"1": "USA"
};
for(let code in codes) {
alert(code); // 1, 41, 44, 49
}
如果客户不希望按照整数那么排,我们可以使用非整数属性名来欺骗程序,在每个键名前加一个“+”
号
let codes = {
"+49": "Germany",
"+41": "Switzerland",
"+44": "Great Britain",
// ..,
"+1": "USA"
};
for (let code in codes) {
console.log(+code); // 1, 41, 44, 49
console.log(typeof +code);//number
}
- 不是整 数属性,按照创建的时候的顺序来排序
let user = {
name: "John",
surname: "Smith"
};
user.age = 25; // 增加一个
// 非整数属性是按照创建的顺序来排列的
for (let prop in user) {
alert( prop ); // name, surname, age
}
对象的引用
引用复制
对象同其他基本类型不同,对象是通过引用存储和复制的。
基本类型的复制:
let message = "Hello!";
let phrase = message;
console.log(message,phrase);//Hello! Hello!
phrase = "world";
console.log(message,phrase);//Hello! world
对象通过引用复制:
let user = {
name : 'John',
}
let admin = user;
console.log(admin,user);//{name: "John"} {name: "John"}
admin.name = 'Peter';
console.log(admin,user)//{name: "Peter"} {name: "Peter"}
当对象被复制的时候——引用被复制了,对象本身并没有被复制。
怎么说呢,对象相当于一个抽屉,uer
和admin
是两把钥匙,用admin
把对象这个抽屉中的内容改变了,那么再用user
这本钥匙去开对象抽屉的时候,里面的内容就是改变后的内容。
比较引用
两个对象引用相同时==
和 ===
才会返回true
let a = {};
let b = a; // 复制引用
alert( a == b ); // true,两个变量指向同一个对象
alert( a === b ); // true
两个对象返回的是false
let a = {};
let b = {}; // 两个独立的对象
alert(a == b); // false
alert(a === b); //false
常量对象
被 const
修饰过得对象是可以被修改的
这里指的修改是修改值,不是引用
const user = {
name: 'aa'
}
user.name = 'bb';
console.log(user.name);//bb
user = {
name: 'cc'
}
console.log(user.name);//Uncaught TypeError: Assignment to constant variable.
复制和合并 Object.assign
复制一个对象,对其进行克隆,或者对个对象进行合并。
Object.assign(dest,[sec1,sec2])
这个方法将 src1, …, srcN 这些所有的对象复制到 dest。换句话说,从第二个参数开始,所有对象的属性都复制给了第一个参数对象,然后返回 dest.
let user = { name: "John" };
let permissions1 = { name: "Peter" };
let permissions2 = { canEdit: true };
Object.assign(user,permissions1,permissions2);
console.log(user);//{name: "Peter", canEdit: true}
permissions1.name = "Happy";
console.log(permissions1.name)//Happy
console.log(user.name);//Peter
- 如果属性名相同,会被覆盖
- 合并之后,在修改被复制的对象是行之改变,不会影响克隆后的结果。
Object.assign
来替代循环赋值进行简单的克隆操作。
let user = { name: "John" };
let clone = Object.assign({},user);
console.log(clone)//{name: "John"}
深度克隆
如果一个对象的属性是其他对象的引用,就绪要深度克隆。
例如下面的这个对象
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({},user);
console.log(clone.sizes);//{height: 182, width: 50}
console.log(clone.sizes == user.sizes)//true
user.sizes.height ++;//183
console.log(clone.sizes.height)//183
像上面这样是不能够克隆出user
对象的,因为仅仅复制了sizes
的这个引用。
为了解决这个问题,我们在复制的时候应该检查 user[key] 的每一个值,如果它是一个对象,那么把它也复制一遍,这叫做深拷贝(deep cloning)。
一个 JavaScript 实现的库 lodash,方法名叫做 _.cloneDeep(obj)。
<script src="lodash.js"></script>
<script>
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = _.cloneDeep(user);
console.log(clone.sizes == user.sizes)//false
对象的键、值、项
- Object.keys(obj) —— 返回一个包含该对象全部的键的数组。
- Object.values(obj) —— 返回一个包含该对象全部的值的数组。
- Object.entries(obj) —— 返回一个包含该对象全部 [key, value] 键值对的数组。
Object.***
返回的是一个数组对象。- 会忽略
Symbol
作为键得属性 - 要想获得Symbol类型的键,可以使用
Object.gatOwnPropertySymbol
返回一个只包含Symbol类型的数组 - 也可以使用
Reflect.ownKeys(obj)
方法会返回「所有」键。
let id = Symbol('id');
let user = {
name: "John",
age: 30,
[id]: '123'
};
let result = Object.keys(user);
console.log(result);
// ["name", "age"]
// 0: "name"
// 1: "age"
// length: 2
console.log(typeof result); //object 数组对象
for (let values of Object.values(user)) {
console.log(values);
}
console.log(Object.getOwnPropertySymbols(user)); //[Symbol(id)]
console.log(Reflect.ownKeys(user));
// ["name", "age", Symbol(id)]
// 0: "name"
// 1: "age"
// 2: Symbol(id)
// length: 3
Symbol类型
“Symbol” 值表示唯一的标识符。
基本使用
- 创建
let id = Symbol('id');
Symbol('id')
中的id
是一个描述。
描述相同值也是不同的。
let id = Symbol('id');
let id1 = Symbol('id');
console.log(id == id1);//false
- Symbol不会被自动转换成字符串
alert(id)//Uncaught TypeError: Cannot convert a Symbol value to a string
alert(id.toString())//Symbol(id)
alert(id.description);//id
可以调用.toString()
和 symbol.description
只显示描述
隐藏属性
let user = { // 属于另一个代码
name: "John"
};
let id = Symbol("id");
user[id] = 1;
console.log(user[id]); // 1 我们可以使用 Symbol 作为键来访问数据
let id1 = Symbol("id");
user[id1] = "Their id value";
console.log(user)// {name: "John", Symbol(id): 1, Symbol(id): "Their id value"}
在字符串 “id” 上使用 Symbol(“id”) 有什么好处?
因为 user 属于另一个代码,另一个代码也使用它执行一些操作,所以我们不应该在它上面加任何字段,这样很不安全。但是 Symbol 不会被意外访问到,所以第三方代码看不到它,所以使用 Symbol 也许不会有什么问题。
对象字面量中的Symbol
- 需要使用方括号把它括起来。
- 不能用点运算符。
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // 而不是 "id:123"
};
alert(user[id])//123
for …in 跳过 Object.keys也忽略
let id = Symbol("id");
let user = {
name: "John",
age: 30,
[id]: 123
};
for (let key in user) alert(key); // name, age (no symbols)
// 使用 Symbol 任务直接访问
alert( "Direct: " + user[id] )
Object.assign 会同时复制字符串和 symbol 属性:
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
全局Symbol
Symbol.for()
有一个全局 Symbol 注册表。
要从注册表中读取(不存在则创建)Symbol,请使用 Symbol.for(key)
。
该调用会检查全局注册表,如果有一个描述为 key
的 Symbol
,则返回该 Symbol
,否则将创建一个新 Symbol(Symbol(key))
,并通过给定的 key
将其存储在注册表中。
// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 Symbol 不存在,则创建它
// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");
// 相同的 Symbol
alert( id === idAgain ); // true
Symbol.keyFor()
根据注册表的键返回一个名字
// 通过 name 获取 Symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
// 通过 Symbol 获取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
所以它不适用于非全局 Symbol。
let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name,全局 Symbol
alert( Symbol.keyFor(localSymbol) ); // undefined,非全局
alert( localSymbol.description ); // name
如果 Symbol 不是全局的,它将无法找到它并返回 undefined。