JavaScript 原型式继承
原型式继承是一种无需构造函数的继承方式,基于现有对象直接创建新对象,通过 Object.create() 实现。
Object.create() 方法
JavaScript
// 基本用法:基于原型对象创建新对象
const proto = {
name: 'default',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
const obj = Object.create(proto);
obj.name = 'Tom'; // 添加自身属性
obj.sayHello(); // 'Hello, Tom'
console.log(obj.__proto__ === proto); // true
原理实现
JavaScript
// Object.create 的简易实现
function create(proto) {
function F() {} // 创建临时构造函数
F.prototype = proto; // 原型指向目标对象
return new F(); // 返回新实例
}
// 使用
const proto = { greet: 'hello' };
const obj = create(proto);
console.log(obj.__proto__ === proto); // true
// ES5 标准 Object.create 还支持第二个参数
const obj2 = Object.create(proto, {
name: {
value: 'Tom',
writable: true,
enumerable: true,
configurable: true
}
});
console.log(obj2.name); // 'Tom'
使用场景
JavaScript
// 场景一:浅拷贝对象
const original = {
name: 'Original',
data: [1, 2, 3]
};
const copy = Object.create(original);
copy.name = 'Copy'; // 自身属性,不影响原对象
console.log(original.name); // 'Original'
console.log(copy.name); // 'Copy'
// 注意:引用类型仍共享
console.log(copy.data); // [1, 2, 3](继承)
copy.data.push(4);
console.log(original.data); // [1, 2, 3, 4](被修改!)
// 场景二:创建纯净对象(无原型)
const pureObj = Object.create(null);
console.log(pureObj.__proto__); // undefined
console.log(pureObj.toString); // undefined(无继承方法)
pureObj.foo = 'bar';
console.log(JSON.stringify(pureObj)); // '{"foo":"bar"}'
// 场景三:原型链中间层
function Animal() {}
Animal.prototype.speak = function() {};
function Dog() {}
// 不用 new Animal(),用 Object.create 避免冗余属性
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
与其他继承方式对比
JavaScript
// 原型链继承:创建父类实例作为原型
Dog.prototype = new Animal(); // 有实例属性
// 原型式继承:直接使用父类原型
Dog.prototype = Object.create(Animal.prototype); // 无实例属性
// 对比
function Animal(name) {
this.name = name; // 实例属性
this.colors = [];
}
Animal.prototype.speak = function() {};
function Dog() {}
// 原型链继承:Dog.prototype 有 name 和 colors
Dog.prototype = new Animal('default');
console.log(Dog.prototype.colors); // [](存在)
// 原型式继承:Dog.prototype 无实例属性
Dog.prototype = Object.create(Animal.prototype);
console.log(Dog.prototype.colors); // undefined(不存在)
Object.create() 参数详解
JavaScript
// 第一个参数:原型对象
const proto = { x: 1, y: 2 };
const obj = Object.create(proto);
// 第二个参数:属性描述符(可选)
const obj2 = Object.create(proto, {
z: {
value: 3,
writable: true,
enumerable: true,
configurable: true
},
secret: {
value: 'hidden',
enumerable: false // 不可枚举
}
});
console.log(obj2.x); // 1(继承)
console.log(obj2.z); // 3(自身)
console.log(obj2.secret); // 'hidden'
console.log(Object.keys(obj2)); // ['z'](secret 不可枚举)
寄生继承
JavaScript
// 原型式继承 + 增强
function createEnhanced(original) {
const clone = Object.create(original);
// 增强对象
clone.sayHi = function() {
console.log('Hi from ' + this.name);
};
return clone;
}
const proto = { name: 'Proto' };
const enhanced = createEnhanced(proto);
enhanced.sayHi(); // 'Hi from Proto'
// 注意:增强方法每个实例都创建一次(非共享)
const another = createEnhanced(proto);
console.log(enhanced.sayHi === another.sayHi); // false
注意事项
Object.create(null)创建无原型对象,适合纯数据字典- 引用类型属性仍被共享,需在自身属性中覆盖
- 寄生继承为每个实例创建增强方法,内存效率低
- ES5 之前需手动实现
create()函数
JavaScript
// 创建纯净对象的优势
const dict = Object.create(null);
dict.foo = 'bar';
dict.constructor = 'safe'; // 无冲突
console.log(dict.hasOwnProperty); // undefined
// 普通对象的问题
const normal = {};
normal.constructor = 'safe';
console.log(normal.constructor); // 'safe'(覆盖了原型)
要点总结
Object.create(proto)创建以proto为原型的新对象- 无需构造函数,直接基于现有对象继承
Object.create(null)创建无原型对象,适合数据字典- 寄生继承:原型式继承 + 对象增强
- 引用类型仍共享,需额外处理
- 与原型链继承区别:不创建父类实例,只有原型链接
📝 发现内容有误?点击此处直接编辑