对象
创建
在这里插入代码let user = new Object(); // “构造函数” 的语法
let user = {}; // “字面量” 的语法片
let user = { // 一个对象
name: "John", // 键 "name",值 "John"
age: 30 // 键 "age",值 30
// 读取文件的属性:
alert( user.name ); // John
alert( user.age ); // 30
删除
delete user.age;
可以用多字词语来作为属性名
let user = {
name: "John",
age: 30,
"likes birds": true // 多词属性名必须加引号
};
对于多词属性,点操作就不能用了:
// 这将提示有语法错误
user.likes birds = true
};
使用 const 声明的对象是可以被修改的
const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete
仅当我们尝试将 user=... 作为一个整体进行赋值时,const 会抛出错误。
点符号要求 key 是有效的变量标识符。这意味着:不包含空格,不以数字开头,也不包含特殊字符(允许使用 $ 和 _)。
使用方括号,可用于任何字符串,可以通过任意表达式来获取属性名的方法:
let user = {};
// 设置
user["likes birds"] = true;
// 读取
alert(user["likes birds"]); // true
// 删除
delete user["likes birds"];
当创建一个对象时,我们可以在对象字面量中使用方括号。这叫做 计算属性
let fruit = prompt("Which fruit to buy?", "apple");
let bag = {
[fruit]: 5, // 属性名是从 fruit 变量中得到的
};
alert( bag.apple ); // 5 如果 fruit="apple"
计算属性的含义很简单:[fruit] 含义是属性名应该从 fruit 变量中获取。
所以,如果一个用户输入 "apple",bag 将变为 {apple: 5}。
属性名简写
function makeUser(name, age) {
return {
name, // 与 name: name 相同
age, // 与 age: age 相同
// ...
};
}
属性名可以是任何字符串或者 symbol(一种特殊的标志符类型,将在后面介绍)。
其他类型会被自动地转换为字符串。
例如,当数字 0 被用作对象的属性的键时,会被转换为字符串 “0”:
属性存在性测试,“in” 操作符
例如:
let user = { name: "John", age: 30 };
alert( "age" in user ); // true,user.age 存在
alert( "blabla" in user ); // false,user.blabla 不存在。
遍历对象所有键
for (key in object) {
// 对此对象属性中的每个键执行的代码
}
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 user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 通过 "admin" 引用来修改
alert(user.name); // 'Pete',修改能通过 "user" 引用看到
比较
当两个对象为同一对象时,两者才相等。
克隆与合并,Object.assign
复制新建一个独立的对象
方法一
let user = {
name: "John",
age: 30
};
let clone = {}; // 新的空对象
// 将 user 中所有的属性拷贝到其中
for (let key in user) {
clone[key] = user[key];
}
// 现在 clone 是带有相同内容的完全独立的对象
clone.name = "Pete"; // 改变了其中的数据
alert( user.name ); // 原来的对象中的 name 属性依然是 John
方法二
Object.assign(dest, [src1, src2, src3…])
第一个参数 dest 是指目标对象。
更后面的参数 src1, …, srcN(可按需传递多个参数)是源对象。
该方法将所有源对象的属性拷贝到目标对象 dest 中。换句话说,从第二个开始的所有参数的属性都被拷贝到第一个参数的对象中。
调用结果返回 dest。
例如
合并多个对象
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中
Object.assign(user, permissions1, permissions2);
// 现在 user = { name: "John", canView: true, canEdit: true }
如果被拷贝的属性的属性名已经存在,那么它会被覆盖:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // 现在 user = { name: "Pete" }
深层克隆
当对象属性是对其他对象的引用,使用拷贝 clone.sizes = user.sizes 已经不足够了,拷贝的仍然是引用对象的地址
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true,同一个对象
// user 和 clone 分享同一个 sizes
user.sizes.width++; // 通过其中一个改变属性值
alert(clone.sizes.width); // 51,能从另外一个看到变更的结果
为了解决此问题,我们应该使用会检查每个 user[key] 的值的克隆循环,如果值是一个对象,那么也要复制它的结构。这就叫“深拷贝”。
对象通过引用被赋值和拷贝。换句话说,一个变量存储的不是“对象的值”,而是一个对值的“引用”(内存地址)。因此,拷贝此类变量或将其作为函数参数传递时,所拷贝的是引用,而不是对象本身。
所有通过被拷贝的引用的操作(如添加、删除属性)都作用在同一个对象上。
为了创建“真正的拷贝”(一个克隆),我们可以使用 Object.assign 来做所谓的“浅拷贝”(嵌套对象被通过引用进行拷贝)或者使用“深拷贝”函数,例如 _.cloneDeep(obj)。