Appearance
JavaScript原型与原型链
一、原型(prototype)
1.1 基本概念
在JavaScript中,每个函数都有一个prototype
属性,这个属性指向一个对象,这个对象就是调用该构造函数而创建的实例的原型。
javascript
function Person() {}
// Person的原型对象
console.log(Person.prototype);
/*
{
constructor: ƒ Person(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
...
}
}
*/
1.2 原型对象的属性
每个原型对象都有以下特点:
- constructor属性
javascript
function Person() {}
console.log(Person.prototype.constructor === Person); // true
let person = new Person();
console.log(person.constructor === Person); // true
- __proto__属性
javascript
function Person() {}
let person = new Person();
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
1.3 原型的动态性
javascript
function Person() {}
let person = new Person();
// 先创建实例
Person.prototype.sayName = function() {
console.log('hello');
};
person.sayName(); // 'hello'
// 重写原型
Person.prototype = {
constructor: Person,
sayName: function() {
console.log('hi');
}
};
person.sayName(); // 'hello' - 仍然输出'hello'
二、原型链(Prototype Chain)
2.1 概念解析
原型链是JavaScript实现继承的主要方法。基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
javascript
// 构造函数
function SuperType() {
this.superProperty = true;
}
SuperType.prototype.getSuperProperty = function() {
return this.superProperty;
};
function SubType() {
this.subProperty = false;
}
// 继承SuperType
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.getSubProperty = function() {
return this.subProperty;
};
let instance = new SubType();
console.log(instance.getSuperProperty()); // true
2.2 原型链的完整实现
javascript
// 1. Object构造函数
console.log(Object.prototype.__proto__); // null
console.log(Object.__proto__ === Function.prototype); // true
// 2. Function构造函数
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
// 3. 自定义构造函数
function Person() {}
console.log(Person.__proto__ === Function.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
2.3 属性查找机制
javascript
function Person() {
this.name = 'person';
}
Person.prototype.name = 'prototype';
Object.prototype.name = 'object';
let person = new Person();
console.log(person.name); // 'person' - 实例属性
delete person.name;
console.log(person.name); // 'prototype' - 原型属性
delete Person.prototype.name;
console.log(person.name); // 'object' - Object原型属性
三、继承模式
3.1 原型链继承
javascript
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {}
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // ["red", "blue", "green", "black"]
let instance2 = new SubType();
console.log(instance2.colors); // ["red", "blue", "green", "black"]
3.2 构造函数继承
javascript
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType() {
SuperType.call(this, "Nicholas");
this.age = 29;
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // ["red", "blue", "green", "black"]
let instance2 = new SubType();
console.log(instance2.colors); // ["red", "blue", "green"]
3.3 组合继承
javascript
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name); // 第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); // 第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
};
3.4 寄生组合继承
javascript
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
四、实际应用
4.1 原型方法扩展
javascript
// 为所有数组添加去重方法
Array.prototype.unique = function() {
return [...new Set(this)];
};
// 为所有字符串添加反转方法
String.prototype.reverse = function() {
return this.split('').reverse().join('');
};
4.2 混入模式(Mixin)
javascript
const mixin = {
sayHi() {
console.log(`Hi ${this.name}`);
},
sayBye() {
console.log(`Bye ${this.name}`);
}
};
class User {
constructor(name) {
this.name = name;
}
}
// 将mixin中的方法复制到User的原型中
Object.assign(User.prototype, mixin);
const user = new User("John");
user.sayHi(); // Hi John
五、注意事项与最佳实践
5.1 原型污染防护
javascript
// 防止原型污染的对象创建函数
function createSafeObject(obj) {
return Object.create(null, Object.getOwnPropertyDescriptors(obj));
}
// 使用Object.freeze()冻结原型
Object.freeze(Object.prototype);
5.2 性能优化
javascript
// 避免在原型链上查找属性
function optimizedSearch() {
// 将常用的原型方法缓存
const hasOwn = Object.prototype.hasOwnProperty;
return function(obj, prop) {
return hasOwn.call(obj, prop);
};
}
总结
- 原型机制是JavaScript实现继承的核心
- 原型链是对象属性查找的路径
- 不同继承模式有其适用场景:
- 原型链继承:共享属性和方法
- 构造函数继承:实例属性独立
- 组合继承:最常用的继承模式
- 寄生组合继承:最理想的继承范式
- 实际应用中要注意:
- 防止原型污染
- 合理使用原型扩展
- 注意性能影响