3JavaScript: The Good Parts

对象

对象的力量

对象字面量原型链属性访问删除

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:对象(Objects)

一、导读

1.1 本章学习目标

第3章深入讲解 JavaScript 中最重要的数据类型——对象。在 JavaScript 中,几乎所有东西都是对象。理解对象的本质,是掌握 JavaScript 的关键。

本章的核心学习目标包括:

  • 理解对象的本质:对象是属性的集合,属性是键值对。
  • 掌握对象字面量:学会用简洁的方式创建对象。
  • 理解原型链:掌握 JavaScript 的继承机制。
  • 学会使用 Object.create:掌握原型继承的正确方式。
  • 理解 delete 操作符:知道如何正确删除属性。

1.2 为什么第三章如此重要

对象是 JavaScript 的核心。函数是对象,数组是对象,正则表达式是对象,甚至原始值在需要时也会被包装为对象。理解对象的创建、访问、继承和删除,是写出高质量 JavaScript 代码的基础。

1.3 核心问题引导

JavaScript 对象与 Java 对象有什么本质区别?

原型链是如何工作的?__proto__ 和 prototype 有什么区别?

Object.create 和 new 有什么区别?

如何安全地检查对象是否拥有某个属性?

delete 操作符能删除什么?不能删除什么?


二、核心概念详解

3.1 对象字面量

javascript
// 对象字面量
var empty = {};
var point = {x: 0, y: 0};
var person = {
    name: "Alice",
    age: 30,
    greet: function() {
        return "Hello, I'm " + this.name;
    }
};

// 属性访问
person.name;       // "Alice"
person["name"];    // "Alice"
person.greet();    // "Hello, I'm Alice"

// 动态属性名
var key = "name";
person[key];       // "Alice"

// 属性赋值
person.email = "alice@example.com";
person["phone"] = "123-456-7890";

3.2 原型(Prototype)

每个对象都有一个原型对象,对象从原型继承方法和属性。

javascript
// 原型链
var animal = {
    type: "animal",
    speak: function() {
        return "I am a " + this.type;
    }
};

var dog = Object.create(animal);
dog.type = "dog";
dog.bark = function() { return "Woof!"; };

dog.speak();  // "I am a dog"(继承自 animal)
dog.bark();   // "Woof!"(自己的方法)

// 原型链查找
// dog.speak → dog 自身没有 → 查找 dog.__proto__(即 animal)→ 找到

// 验证原型
Object.getPrototypeOf(dog) === animal;  // true
animal.isPrototypeOf(dog);              // true

3.3 Object.create

Object.create 是创建新对象并指定其原型的标准方式。

javascript
// Object.create 创建对象
var base = {
    init: function(name) {
        this.name = name;
        return this;
    },
    getName: function() {
        return this.name;
    }
};

var person = Object.create(base).init("Alice");
person.getName();  // "Alice"

// 带属性描述符的 Object.create
var obj = Object.create(Object.prototype, {
    name: {
        value: "Alice",
        writable: true,
        enumerable: true,
        configurable: true
    },
    age: {
        value: 30,
        writable: false  // 不可修改
    }
});

obj.name = "Bob";   // 成功
obj.age = 40;       // 严格模式下报错,非严格模式静默失败

3.4 属性查找与 hasOwnProperty

javascript
// 属性查找
var obj = {a: 1};
"a" in obj;              // true(包括原型链)
obj.hasOwnProperty("a"); // true(仅自身属性)
obj.hasOwnProperty("toString"); // false(原型链上的)

// Object.keys 只返回自身可枚举属性
Object.keys(obj);  // ["a"]

// for in 遍历所有可枚举属性(包括原型链)
for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(key + ": " + obj[key]);
    }
}

// Object.getOwnPropertyNames 返回所有自身属性(包括不可枚举的)
Object.getOwnPropertyNames(obj);

3.5 delete 操作符

javascript
var obj = {a: 1, b: 2, c: 3};

delete obj.a;  // true
obj.a;         // undefined
"a" in obj;    // false

// delete 的局限
delete obj.toString;  // false(不能删除继承的属性)

var global_var = 1;
delete global_var;    // false(不能删除 var 声明的变量)

global_prop = 2;
delete global_prop;   // true(可以删除隐式全局变量)

3.6 函数式继承

Crockford 提出了一种函数式的继承方式,避免了 new 关键字的问题。

javascript
// 函数式继承
function mammal(spec) {
    var that = {};
    
    that.getName = function() {
        return spec.name;
    };
    
    that.says = function() {
        return spec.saying || "";
    };
    
    return that;
}

function cat(spec) {
    spec.saying = spec.saying || "meow";
    var that = mammal(spec);
    
    that.purr = function() {
        return "purr";
    };
    
    that.getName = function() {
        return spec.name + " (cat)";
    };
    
    return that;
}

var myCat = cat({name: "Henrietta"});
myCat.getName();  // "Henrietta (cat)"
myCat.says();     // "meow"
myCat.purr();     // "purr"

3.7 多态

javascript
// 通过方法覆盖实现多态
var coolcat = cat({name: "Bix"});
var speak = function(name) {
    return name + " says " + name.says();
};

speak(coolcat);  // "Bix says meow"

// 差异化继承
var myCat2 = cat({name: "Henrietta"});
myCat2.says = function() { return "squeak!"; };
myCat2.getName = function() { return "Lady " + this.name; };

speak(myCat2);  // "Lady Henrietta says squeak!"

三、实战案例

3.1 对象工厂模式

javascript
// 对象工厂
function createVehicle(spec) {
    var that = {};
    
    that.getMake = function() { return spec.make; };
    that.getModel = function() { return spec.model; };
    that.getYear = function() { return spec.year; };
    
    that.describe = function() {
        return that.getYear() + " " + that.getMake() + " " + that.getModel();
    };
    
    return that;
}

function createCar(spec) {
    spec.doors = spec.doors || 4;
    var that = createVehicle(spec);
    
    that.getDoors = function() { return spec.doors; };
    that.describe = function() {
        return createVehicle(spec).describe() + " with " + spec.doors + " doors";
    };
    
    return that;
}

function createTruck(spec) {
    spec.bedLength = spec.bedLength || "standard";
    var that = createVehicle(spec);
    
    that.getBedLength = function() { return spec.bedLength; };
    that.describe = function() {
        return createVehicle(spec).describe() + " truck with " + spec.bedLength + " bed";
    };
    
    return that;
}

var myCar = createCar({make: "Toyota", model: "Camry", year: 2023});
var myTruck = createTruck({make: "Ford", model: "F-150", year: 2023, bedLength: "long"});

console.log(myCar.describe());   // "2023 Toyota Camry with 4 doors"
console.log(myTruck.describe()); // "2023 Ford F-150 truck with long bed"

3.2 深拷贝

javascript
// 深拷贝对象
function deepClone(obj) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    
    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }
    
    if (obj instanceof Array) {
        return obj.map(deepClone);
    }
    
    if (obj instanceof Object) {
        var copy = Object.create(Object.getPrototypeOf(obj));
        Object.keys(obj).forEach(function(key) {
            copy[key] = deepClone(obj[key]);
        });
        return copy;
    }
    
    return obj;
}

var original = {
    name: "Alice",
    address: {city: "NYC", zip: "10001"},
    hobbies: ["reading", "coding"]
};

var cloned = deepClone(original);
cloned.address.city = "LA";
cloned.hobbies.push("swimming");

console.log(original.address.city);  // "NYC"(未被修改)
console.log(original.hobbies);       // ["reading", "coding"](未被修改)

四、本章小结

第3章深入讲解了 JavaScript 对象的核心知识:

对象是属性的集合:属性是键值对,值可以是任何类型。

原型链:对象通过原型链继承属性和方法。

Object.create:是创建对象并指定原型的标准方式。

hasOwnProperty:用于区分自身属性和继承属性。

函数式继承:避免 new 关键字的问题,使用闭包实现私有状态。

delete:只能删除对象自身的属性。

对象是 JavaScript 的灵魂。理解对象的创建、访问、继承和删除,是掌握 JavaScript 的关键。


五、思考与练习

思考题

JavaScript 对象和 Java 对象有什么本质区别?

原型链的查找过程是怎样的?

Object.create(null) 和 {} 有什么区别?

函数式继承相比构造器+原型有什么优势?

如何实现一个不可变的对象?

编程练习

实现对象工厂:创建一个 Shape 层级结构(Shape → Circle/Rectangle)。

实现深拷贝:处理循环引用。

实现 mixin:将多个对象的能力合并到一个对象上。


3/9对象
0%