引用类型

Object对象

对象是JavaScript中最基本的引用类型,是属性的集合。

对象的概念

// 对象是键值对的集合
let user = {
    name: "东巴文",
    age: 1,
    isActive: true
};

console.log(typeof user);  // "object"

对象的特点

特点 说明 东巴文评价
动态性 可以随时添加/删除属性 ✅ 灵活
引用传递 赋值时传递引用地址 ⚠️ 注意共享问题
属性类型 支持数据属性和访问器属性 ✅ 功能强大
键的类型 字符串或Symbol -

对象创建

对象字面量

// 最常用的创建方式
let user = {
    name: "东巴文",
    age: 1,
    greet: function() {
        return `你好,我是${this.name}`;
    }
};

// ES6增强写法
let name = "东巴文";
let age = 1;

let user2 = {
    name,  // 等同于 name: name
    age,   // 等同于 age: age
    greet() {  // 方法定义简写
        return `你好,我是${this.name}`;
    }
};

new Object()

let user = new Object();
user.name = "东巴文";
user.age = 1;

// 不推荐,更繁琐

Object.create()

// 创建一个没有原型的对象
let obj = Object.create(null);

// 创建一个继承原型的对象
let person = {
    greet() {
        return "你好";
    }
};

let user = Object.create(person);
user.name = "东巴文";
console.log(user.greet());  // "你好"

构造函数

function User(name, age) {
    this.name = name;
    this.age = age;
    this.greet = function() {
        return `你好,我是${this.name}`;
    };
}

let user = new User("东巴文", 1);
console.log(user.name);  // "东巴文"

工厂函数

function createUser(name, age) {
    return {
        name,
        age,
        greet() {
            return `你好,我是${this.name}`;
        }
    };
}

let user = createUser("东巴文", 1);
console.log(user.greet());  // "你好,我是东巴文"

属性访问

点表示法

let user = {
    name: "东巴文",
    age: 1
};

console.log(user.name);  // "东巴文"
user.age = 2;  // 修改属性
user.email = "test@example.com";  // 添加属性

方括号表示法

let user = {
    name: "东巴文",
    "full name": "东巴文学习平台"
};

console.log(user["name"]);       // "东巴文"
console.log(user["full name"]);  // "东巴文学习平台"

// 动态属性名
let key = "name";
console.log(user[key]);  // "东巴文"

// 计算属性名(ES6)
let prop = "age";
let obj = {
    [prop]: 1,
    ["na" + "me"]: "东巴文"
};
console.log(obj.age);   // 1
console.log(obj.name);  // "东巴文"

两种方式的区别

特性 点表示法 方括号表示法 东巴文建议
标识符规则 必须是有效标识符 可以是任意字符串 点法更简洁
空格和特殊字符 不支持 支持 方括号更灵活
动态属性名 不支持 支持 方括号必需
数字键 不支持 支持 方括号必需
let obj = {
    "1": "数字键",
    "my-key": "连字符键"
};

// console.log(obj.1);      // SyntaxError
console.log(obj["1"]);      // "数字键"

// console.log(obj.my-key); // 被解析为减法
console.log(obj["my-key"]); // "连字符键"

属性描述符

每个属性都有描述符,定义属性的行为。

获取属性描述符

let user = {
    name: "东巴文"
};

let descriptor = Object.getOwnPropertyDescriptor(user, "name");
console.log(descriptor);
// {
//   value: "东巴文",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

数据属性描述符

属性 默认值 说明 东巴文解释
value undefined 属性值 存储的数据
writable false 是否可修改 控制写入
enumerable false 是否可枚举 控制遍历
configurable false 是否可配置 控制删除和修改描述符
let user = {};

Object.defineProperty(user, "name", {
    value: "东巴文",
    writable: false,      // 不可修改
    enumerable: true,     // 可枚举
    configurable: false   // 不可删除
});

console.log(user.name);  // "东巴文"
user.name = "新名字";    // 静默失败(严格模式报错)
console.log(user.name);  // "东巴文"

// delete user.name;     // 删除失败

访问器属性

let user = {
    firstName: "东",
    lastName: "巴文"
};

Object.defineProperty(user, "fullName", {
    get() {
        return `${this.firstName}${this.lastName}`;
    },
    set(value) {
        [this.firstName, this.lastName] = value.split("");
    },
    enumerable: true,
    configurable: true
});

console.log(user.fullName);  // "东巴文"
user.fullName = "新名字";
console.log(user.firstName);  // "新"
console.log(user.lastName);   // "名字"

定义多个属性

let user = {};

Object.defineProperties(user, {
    name: {
        value: "东巴文",
        writable: true
    },
    age: {
        value: 1,
        writable: false
    }
});

对象方法

Object.keys()

let user = {
    name: "东巴文",
    age: 1,
    email: "test@example.com"
};

console.log(Object.keys(user));  // ["name", "age", "email"]

Object.values()

console.log(Object.values(user));  // ["东巴文", 1, "test@example.com"]

Object.entries()

console.log(Object.entries(user));
// [["name", "东巴文"], ["age", 1], ["email", "test@example.com"]]

// 遍历键值对
for (let [key, value] of Object.entries(user)) {
    console.log(`${key}: ${value}`);
}

Object.assign()

let target = { a: 1 };
let source = { b: 2, c: 3 };

let result = Object.assign(target, source);
console.log(result);  // { a: 1, b: 2, c: 3 }
console.log(target);  // { a: 1, b: 2, c: 3 }(target被修改)

// 合并多个对象
let merged = Object.assign({}, { a: 1 }, { b: 2 }, { c: 3 });
console.log(merged);  // { a: 1, b: 2, c: 3 }

Object.freeze()

let user = {
    name: "东巴文",
    age: 1
};

Object.freeze(user);

user.name = "新名字";  // 静默失败
user.email = "test@example.com";  // 静默失败
delete user.age;  // 静默失败

console.log(user);  // { name: "东巴文", age: 1 }

// 检查是否被冻结
console.log(Object.isFrozen(user));  // true

Object.seal()

let user = {
    name: "东巴文",
    age: 1
};

Object.seal(user);

user.name = "新名字";  // 可以修改
// delete user.age;   // 不能删除
// user.email = "";   // 不能添加

console.log(user);  // { name: "新名字", age: 1 }

console.log(Object.isSealed(user));  // true

freeze vs seal

操作 freeze seal 东巴文说明
修改属性 freeze完全冻结
添加属性 都不能添加
删除属性 都不能删除
修改描述符 都不能修改

对象拷贝

浅拷贝

// 方法1:Object.assign()
let obj1 = { a: 1, b: { c: 2 } };
let copy1 = Object.assign({}, obj1);

// 方法2:展开运算符
let copy2 = { ...obj1 };

// 方法3:JSON方法
let copy3 = JSON.parse(JSON.stringify(obj1));

// 浅拷贝的问题
obj1.b.c = 100;
console.log(copy1.b.c);  // 100(受影响,因为b是引用类型)

深拷贝

// 方法1:JSON方法(有局限性)
let obj = { 
    name: "东巴文", 
    date: new Date(),
    func: function() {}
};

let copy = JSON.parse(JSON.stringify(obj));
console.log(copy.name);   // "东巴文"
console.log(copy.date);   // 字符串,不是Date对象
console.log(copy.func);   // undefined(函数丢失)

// 方法2:递归实现
function deepClone(obj, hash = new WeakMap()) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    
    if (hash.has(obj)) {
        return hash.get(obj);
    }
    
    let clone = Array.isArray(obj) ? [] : {};
    hash.set(obj, clone);
    
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key], hash);
        }
    }
    
    return clone;
}

// 方法3:structuredClone(现代浏览器支持)
let obj2 = { a: 1, b: { c: 2 } };
let copy2 = structuredClone(obj2);

深拷贝方案对比

方案 优点 缺点 东巴文建议
JSON方法 简单 不支持函数、Date等 简单场景
递归实现 完整支持 需要自己实现 学习理解
structuredClone 原生支持 兼容性问题 现代项目
lodash.cloneDeep 功能完整 需要引入库 生产环境

对象比较

引用比较

let obj1 = { name: "东巴文" };
let obj2 = { name: "东巴文" };
let obj3 = obj1;

console.log(obj1 === obj2);  // false(不同引用)
console.log(obj1 === obj3);  // true(同一引用)

浅比较

function shallowEqual(obj1, obj2) {
    if (obj1 === obj2) return true;
    
    if (typeof obj1 !== "object" || obj1 === null ||
        typeof obj2 !== "object" || obj2 === null) {
        return false;
    }
    
    let keys1 = Object.keys(obj1);
    let keys2 = Object.keys(obj2);
    
    if (keys1.length !== keys2.length) return false;
    
    for (let key of keys1) {
        if (obj1[key] !== obj2[key]) {
            return false;
        }
    }
    
    return true;
}

let a = { name: "东巴文", age: 1 };
let b = { name: "东巴文", age: 1 };
console.log(shallowEqual(a, b));  // true

深比较

function deepEqual(obj1, obj2) {
    if (obj1 === obj2) return true;
    
    if (typeof obj1 !== "object" || obj1 === null ||
        typeof obj2 !== "object" || obj2 === null) {
        return false;
    }
    
    let keys1 = Object.keys(obj1);
    let keys2 = Object.keys(obj2);
    
    if (keys1.length !== keys2.length) return false;
    
    for (let key of keys1) {
        if (!deepEqual(obj1[key], obj2[key])) {
            return false;
        }
    }
    
    return true;
}

let a = { name: "东巴文", info: { age: 1 } };
let b = { name: "东巴文", info: { age: 1 } };
console.log(deepEqual(a, b));  // true

下一步

掌握了引用类型后,让我们继续学习:

  1. 类型转换 - 深入学习类型转换
  2. 运算符概述 - 学习运算符
  3. 算术运算符 - 学习算术运算

东巴文(db-w.cn) - 让编程学习更简单

📦 东巴文寄语:对象是JavaScript的核心,理解对象的创建、属性操作和拷贝机制,是掌握JavaScript的关键。在 db-w.cn,我们帮你深入理解每一个概念!