工厂模式是一种创建对象的设计模式。
function createPerson(name, age) {
return {
name: name,
age: age,
greet() {
console.log(`你好,我是${this.name}`);
}
};
}
const person1 = createPerson("东巴文", 1);
const person2 = createPerson("张三", 20);
person1.greet(); // 你好,我是东巴文
person2.greet(); // 你好,我是张三
// 优点:
// 1. 简单易用
// 2. 避免重复代码
// 缺点:
// 1. 无法识别对象类型
// 2. 每个对象都创建新的方法副本
console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person); // Person未定义
console.log(person1.greet === person2.greet); // false
构造函数用于创建特定类型的对象。
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`你好,我是${this.name}`);
};
}
const person1 = new Person("东巴文", 1);
const person2 = new Person("张三", 20);
person1.greet(); // 你好,我是东巴文
// new Person("东巴文", 1) 做了四件事:
// 1. 创建一个新对象
// 2. 将this绑定到新对象
// 3. 执行构造函数代码
// 4. 返回新对象
// 手动模拟new
function myNew(Constructor, ...args) {
const obj = Object.create(Constructor.prototype);
const result = Constructor.apply(obj, args);
return result instanceof Object ? result : obj;
}
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`你好,我是${this.name}`);
};
}
// 每个实例都创建新的方法
const person1 = new Person("东巴文", 1);
const person2 = new Person("张三", 20);
console.log(person1.greet === person2.greet); // false
// 解决:将方法定义在外部
function greet() {
console.log(`你好,我是${this.name}`);
}
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = greet;
}
console.log(person1.greet === person2.greet); // true
原型模式使用原型对象共享属性和方法。
function Person() {}
// 原型对象
Person.prototype.name = "默认名称";
Person.prototype.age = 0;
Person.prototype.greet = function() {
console.log(`你好,我是${this.name}`);
};
const person1 = new Person();
const person2 = new Person();
person1.name = "东巴文"; // 实例属性遮蔽原型属性
console.log(person1.name); // "东巴文"(实例属性)
console.log(person2.name); // "默认名称"(原型属性)
console.log(person1.greet === person2.greet); // true(共享方法)
function Person() {}
// 对象字面量重写原型
Person.prototype = {
constructor: Person, // 需要手动设置
name: "默认名称",
age: 0,
greet() {
console.log(`你好,我是${this.name}`);
}
};
function Person(name) {
this.name = name;
}
const person = new Person("东巴文");
// 实例与原型的关系
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(person instanceof Person); // true
// 原型链
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
原型链是实现继承的主要方法。
实例 → 构造函数.prototype → Object.prototype → null
person → Person.prototype → Object.prototype → null
function Person(name) {
this.name = name;
}
Person.prototype.age = 0;
const person = new Person("东巴文");
// 查找name:在实例上找到
console.log(person.name); // "东巴文"
// 查找age:在原型上找到
console.log(person.age); // 0
// 查找不存在属性:返回undefined
console.log(person.gender); // undefined
function Person(name) {
this.name = name;
}
Person.prototype.name = "原型名称";
const person = new Person("东巴文");
console.log(person.name); // "东巴文"(实例属性遮蔽原型)
delete person.name;
console.log(person.name); // "原型名称"(删除后访问原型)
function Person(name) {
this.name = name;
}
Person.prototype.age = 0;
const person = new Person("东巴文");
// hasOwnProperty:检查自身属性
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("age")); // false
// in操作符:检查可访问属性
console.log("name" in person); // true
console.log("age" in person); // true
原型继承通过原型链实现继承。
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name}在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name); // 继承属性
this.breed = breed;
}
// 继承方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(`${this.name}在叫`);
};
const dog = new Dog("小黑", "拉布拉多");
dog.eat(); // 小黑在吃东西
dog.bark(); // 小黑在叫
dog → Dog.prototype → Animal.prototype → Object.prototype → null
Object.create创建一个新对象,使用现有对象作为原型。
const proto = {
greet() {
console.log(`你好,我是${this.name}`);
}
};
const obj = Object.create(proto);
obj.name = "东巴文";
obj.greet(); // 你好,我是东巴文
// 没有原型的对象
const pureObj = Object.create(null);
console.log(pureObj.toString); // undefined
// 用作字典
const dict = Object.create(null);
dict.key = "value";
const obj = Object.create(
{ proto: "原型属性" },
{
name: {
value: "东巴文",
writable: true,
enumerable: true,
configurable: true
}
}
);
console.log(obj.proto); // "原型属性"
console.log(obj.name); // "东巴文"
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name}在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog,
writable: true,
configurable: true
}
});
Dog.prototype.bark = function() {
console.log(`${this.name}在叫`);
};
寄生继承创建一个封装继承过程的函数。
function createAnother(original) {
const clone = Object.create(original);
clone.sayHi = function() {
console.log("你好!");
};
return clone;
}
const person = {
name: "东巴文",
greet() {
console.log(`我是${this.name}`);
}
};
const another = createAnother(person);
another.greet(); // 我是东巴文
another.sayHi(); // 你好!
function inheritPrototype(SubType, SuperType) {
const prototype = Object.create(SuperType.prototype);
prototype.constructor = SubType;
SubType.prototype = prototype;
}
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name}在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
inheritPrototype(Dog, Animal);
Dog.prototype.bark = function() {
console.log(`${this.name}在叫`);
};
组合继承结合了原型链和构造函数继承。
function Animal(name) {
this.name = name;
this.colors = [];
}
Animal.prototype.eat = function() {
console.log(`${this.name}在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name); // 第二次调用Animal
this.breed = breed;
}
Dog.prototype = new Animal(); // 第一次调用Animal
Dog.prototype.constructor = Dog;
const dog1 = new Dog("小黑", "拉布拉多");
const dog2 = new Dog("小白", "金毛");
dog1.colors.push("黑色");
console.log(dog2.colors); // [](独立的数组)
// 调用了两次父类构造函数
// 1. Dog.prototype = new Animal()
// 2. Animal.call(this, name)
// 导致原型上有重复属性
console.log(Dog.prototype.name); // undefined
寄生组合继承是最理想的继承方式。
function inheritPrototype(SubType, SuperType) {
const prototype = Object.create(SuperType.prototype);
prototype.constructor = SubType;
SubType.prototype = prototype;
}
function Animal(name) {
this.name = name;
this.colors = [];
}
Animal.prototype.eat = function() {
console.log(`${this.name}在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name); // 只调用一次
this.breed = breed;
}
inheritPrototype(Dog, Animal);
Dog.prototype.bark = function() {
console.log(`${this.name}在叫`);
};
const dog = new Dog("小黑", "拉布拉多");
dog.eat(); // 小黑在吃东西
dog.bark(); // 小黑在叫
| 继承方式 | 调用父类次数 | 共享引用 | 东巴文建议 |
|---|---|---|---|
| 原型继承 | 1 | 是 | 不推荐 |
| 构造函数继承 | N | 否 | 不推荐 |
| 组合继承 | 2 | 否 | 可用 |
| 寄生组合继承 | 1 | 否 | 推荐 |
掌握了对象创建与继承后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🧬 东巴文寄语:理解原型链和继承是JavaScript进阶的关键,掌握各种继承方式的优缺点,选择最合适的方案。在ES6中,class语法让继承更加简洁。在 db-w.cn,我们帮你掌握面向对象编程!