JavaScript原型链深度解析:从底层原理到Vue/React实践
原型链(Prototype Chain)是JavaScript实现继承的核心机制。它是一种基于对象的继承模型,而非传统的基于类的继承。在JavaScript中,几乎所有对象都有一个内部链接指向另一个对象,这个对象被称为"原型"(prototype),形成一条链式结构,直到终点`null`。
目录
一、原型链概述
1.1 什么是原型链
原型链(Prototype Chain)是JavaScript实现继承的核心机制。它是一种基于对象的继承模型,而非传统的基于类的继承。在JavaScript中,几乎所有对象都有一个内部链接指向另一个对象,这个对象被称为"原型"(prototype),形成一条链式结构,直到终点null。
1.2 为什么需要原型链
- 代码复用:通过原型链,多个实例可以共享方法和属性,避免重复定义
- 内存优化:共享的方法只在原型上存储一份,减少内存占用
- 动态继承:可以在运行时动态修改原型,实现灵活的继承关系
- 实现多态:不同对象可以通过原型链实现方法重写和多态特性
二、核心概念详解
2.1 prototype(显式原型)
prototype 是函数对象特有的属性,指向一个对象,这个对象包含了所有实例共享的属性和方法。
function Person(name) {
this.name = name;
}
// 在prototype上定义共享方法
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
console.log(typeof Person.prototype); // "object"
console.log(Person.prototype.constructor === Person); // true
关键点:
- 只有函数对象才有
prototype属性 prototype是一个对象,包含constructor属性指向函数本身- 通过构造函数创建的实例会继承
prototype上的属性和方法
2.2 proto(隐式原型)
__proto__ 是每个对象都有的属性,指向创建该对象的构造函数的原型对象。
const person = new Person('Alice');
console.log(person.__proto__ === Person.prototype); // true
console.log(person.__proto__.constructor === Person); // true
注意:
__proto__是非标准属性,ES6引入了Object.getPrototypeOf()和Object.setPrototypeOf()作为标准方法- 现代开发中应避免直接操作
__proto__
2.3 constructor(构造函数)
constructor 是原型对象的一个属性,指向关联的构造函数。
function Animal(type) {
this.type = type;
}
const dog = new Animal('dog');
console.log(dog.constructor === Animal); // true
console.log(dog.__proto__.constructor === Animal); // true
console.log(Animal.prototype.constructor === Animal); // true
2.4 三者关系图
┌─────────────────┐
│ Function │
│ (构造函数) │
└────────┬────────┘
│
│ .prototype
↓
┌────────────────────────────────┐
│ Prototype Object │
│ { constructor: Function } │
└────────────┬───────────────────┘
↑
│ .__proto__
│
┌──────┴──────┐
│ Instance │
│ (实例对象) │
└──────────────┘
三、原型链的底层原理
3.1 对象创建过程
当使用 new 操作符创建对象时,JavaScript引擎执行以下步骤:
// 模拟 new 操作符的实现
function myNew(Constructor, ...args) {
// 1. 创建一个新对象
const obj = {};
// 2. 将新对象的 __proto__ 指向构造函数的 prototype
obj.__proto__ = Constructor.prototype;
// 或使用标准方法:Object.setPrototypeOf(obj, Constructor.prototype);
// 3. 执行构造函数,将 this 绑定到新对象
const result = Constructor.apply(obj, args);
// 4. 如果构造函数返回对象,则返回该对象;否则返回新创建的对象
return result instanceof Object ? result : obj;
}
// 测试
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
const person = myNew(Person, 'Bob');
person.sayHello(); // "Hello, Bob"
3.2 属性查找机制
JavaScript引擎在访问对象属性时,遵循以下查找顺序:
function Parent() {
this.parentProp = 'parent';
}
Parent.prototype.sharedMethod = function() {
return 'shared';
};
function Child() {
Parent.call(this);
this.childProp = 'child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const instance = new Child();
// 查找过程:
console.log(instance.childProp); // 1. 在实例自身找到 ✓
console.log(instance.parentProp); // 2. 在实例自身找到 ✓
console.log(instance.sharedMethod()); // 3. 实例 → Child.prototype → Parent.prototype ✓
console.log(instance.toString()); // 4. 继续向上到 Object.prototype ✓
console.log(instance.nonExist); // 5. 直到 null,返回 undefined
查找算法伪代码:
function getProperty(obj, prop) {
let current = obj;
while (current !== null) {
// 检查当前对象自身是否有该属性
if (current.hasOwnProperty(prop)) {
return current[prop];
}
// 沿着原型链向上查找
current = Object.getPrototypeOf(current);
}
return undefined;
}
3.3 原型链的终点
所有原型链的终点都是 Object.prototype,而 Object.prototype.__proto__ 为 null:
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
// 完整的原型链
function Foo() {}
const foo = new Foo();
console.log(foo.__proto__ === Foo.prototype); // true
console.log(Foo.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
3.4 函数的原型链
函数是特殊的对象,具有双重身份:
function MyFunction() {}
// 作为对象,有 __proto__
console.log(MyFunction.__proto__ === Function.prototype); // true
// 作为构造函数,有 prototype
console.log(typeof MyFunction.prototype); // "object"
// Function.prototype 的原型是 Object.prototype
console.log(Function.prototype.__proto__ === Object.prototype); // true
// Function 自己也是一个函数
console.log(Function.__proto__ === Function.prototype); // true (特殊情况)
四、原型链的查找机制
4.1 属性读取
function Animal(name) {
this.name = name;
this.colors = ['black', 'white'];
}
Animal.prototype.species = 'mammal';
Animal.prototype.getSpecies = function() {
return this.species;
};
const cat = new Animal('Cat');
// 读取实例属性(在对象自身)
console.log(cat.name); // "Cat" - 直接找到
// 读取原型属性(在原型链上)
console.log(cat.species); // "mammal" - 从 Animal.prototype 找到
console.log(cat.getSpecies()); // "mammal"
// 读取更上层的原型属性
console.log(cat.toString()); // "[object Object]" - 从 Object.prototype 找到
4.2 属性写入(屏蔽效应)
function Person() {}
Person.prototype.name = 'Prototype Name';
const person1 = new Person();
const person2 = new Person();
// 读取原型属性
console.log(person1.name); // "Prototype Name"
console.log(person2.name); // "Prototype Name"
// 写入实例属性(不会修改原型)
person1.name = 'Instance Name';
console.log(person1.name); // "Instance Name" - 实例属性屏蔽了原型属性
console.log(person2.name); // "Prototype Name" - 不受影响
console.log(Person.prototype.name); // "Prototype Name" - 原型未改变
// 删除实例属性后,原型属性重新可见
delete person1.name;
console.log(person1.name); // "Prototype Name"
4.3 引用类型的陷阱
function Parent() {
this.list = [1, 2, 3];
}
function Child() {}
Child.prototype = new Parent();
const child1 = new Child();
const child2 = new Child();
// 修改引用类型会影响所有实例
child1.list.push(4);
console.log(child2.list); // [1, 2, 3, 4] - 被污染了!
// 正确做法:在构造函数中初始化引用类型
function BetterChild() {
Parent.call(this); // 每个实例都有独立的 list
}
BetterChild.prototype = Object.create(Parent.prototype);
BetterChild.prototype.constructor = BetterChild;
4.4 hasOwnProperty vs in 操作符
function Person() {
this.ownProp = 'own';
}
Person.prototype.protoProp = 'proto';
const person = new Person();
// hasOwnProperty: 只检查自身属性
console.log(person.hasOwnProperty('ownProp')); // true
console.log(person.hasOwnProperty('protoProp')); // false
// in 操作符: 检查整个原型链
console.log('ownProp' in person); // true
console.log('protoProp' in person); // true
// 判断属性是否在原型上
function isPrototypeProperty(obj, prop) {
return (prop in obj) && !obj.hasOwnProperty(prop);
}
console.log(isPrototypeProperty(person, 'protoProp')); // true
五、在Vue中的应用
5.1 Vue 2.x的原型链扩展
Vue 2通过原型链实现全局方法和实例方法的共享:
// Vue源码中的实现(简化版)
function Vue(options) {
// 初始化逻辑
this._init(options);
}
// 在原型上定义方法,所有实例共享
Vue.prototype._init = function(options) {
const vm = this;
vm.$options = options;
// 初始化数据、计算属性、侦听器等
};
// 全局API也通过原型挂载
Vue.prototype.$set = function(target, key, value) {
// 响应式设置逻辑
};
Vue.prototype.$watch = function(expOrFn, cb, options) {
// 侦听器逻辑
};
// 实例使用
const vm = new Vue({
data: { message: 'Hello' }
});
vm.$set(vm, 'newProp', 'value'); // 可以访问原型方法
5.2 Vue插件系统
Vue插件通过修改Vue.prototype来扩展功能:
// 自定义插件
const MyPlugin = {
install(Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function() {
console.log('全局方法');
};
// 2. 添加实例方法(通过原型链)
Vue.prototype.$myMethod = function(message) {
console.log(`实例方法: ${message}`);
};
// 3. 注入组件选项
Vue.mixin({
created() {
console.log('全局混入');
}
});
}
};
// 使用插件
Vue.use(MyPlugin);
// 在组件中使用
export default {
mounted() {
this.$myMethod('Hello'); // 通过原型链访问
}
};
5.3 Vue Router的原型链应用
// Vue Router源码实现(简化)
class VueRouter {
constructor(options) {
this.routes = options.routes || [];
}
push(location) {
// 路由跳转逻辑
}
}
VueRouter.install = function(Vue) {
// 在Vue原型上挂载$router和$route
Object.defineProperty(Vue.prototype, '$router', {
get() {
return this._routerRoot._router;
}
});
Object.defineProperty(Vue.prototype, '$route', {
get() {
return this._routerRoot._route;
}
});
};
// 使用
Vue.use(VueRouter);
// 在组件中
export default {
methods: {
navigate() {
this.$router.push('/home'); // 通过原型链访问
console.log(this.$route.params); // 通过原型链访问
}
}
};
5.4 Vue 3的变化
Vue 3改用Composition API,减少了对原型链的依赖:
// Vue 2: 依赖原型链
export default {
mounted() {
this.$axios.get('/api'); // 通过原型
this.$router.push('/'); // 通过原型
}
};
// Vue 3: 使用组合式函数
import { getCurrentInstance } from 'vue';
import { useRouter } from 'vue-router';
export default {
setup() {
// 方式1: 使用组合式API
const router = useRouter();
// 方式2: 仍可访问实例(不推荐)
const instance = getCurrentInstance();
const axios = instance.appContext.config.globalProperties.$axios;
return {
navigate: () => router.push('/')
};
}
};
六、在React中的应用
6.1 React类组件的原型链
// React.Component 的简化实现
function Component(props, context) {
this.props = props;
this.context = context;
this.refs = {};
}
// 在原型上定义共享方法
Component.prototype.setState = function(partialState, callback) {
// 状态更新逻辑
this.updater.enqueueSetState(this, partialState, callback);
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback);
};
// 用户定义的组件继承Component
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
// 实例方法
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return <div onClick={this.handleClick}>{this.state.count}</div>;
}
}
// 原型链结构
const instance = new MyComponent({ name: 'Test' });
console.log(instance.__proto__ === MyComponent.prototype); // true
console.log(MyComponent.prototype.__proto__ === Component.prototype); // true
console.log(instance.setState === Component.prototype.setState); // true
6.2 高阶组件(HOC)与原型链
// 高阶组件示例
function withLogger(WrappedComponent) {
return class extends Component {
componentDidMount() {
console.log(`${WrappedComponent.name} mounted`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// 使用
class MyComponent extends Component {
render() {
return <div>Content</div>;
}
}
// HOC包装后保持原型链
const EnhancedComponent = withLogger(MyComponent);
// 注意:displayName需要手动设置
EnhancedComponent.displayName = `withLogger(${MyComponent.name})`;
6.3 React Hooks取代原型链
React 16.8引入Hooks后,减少了对类和原型链的依赖:
// 类组件(基于原型链)
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.increment}>+</button>
</div>
);
}
}
// 函数组件(不使用原型链)
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
</div>
);
}
6.4 自定义Hook的实现
// 自定义Hook不依赖原型链,而是闭包
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
const decrement = useCallback(() => {
setCount(c => c - 1);
}, []);
return { count, increment, decrement };
}
// 使用
function App() {
const { count, increment, decrement } = useCounter(10);
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
七、实战案例分析
7.1 实现继承的多种方式
1) 原型链继承
function Parent() {
this.name = 'parent';
this.colors = ['red', 'blue'];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child() {}
Child.prototype = new Parent();
const child1 = new Child();
const child2 = new Child();
// 缺点:引用类型共享
child1.colors.push('green');
console.log(child2.colors); // ['red', 'blue', 'green']
2) 构造函数继承
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name) {
Parent.call(this, name); // 借用构造函数
}
const child = new Child('child');
child.colors.push('green');
// 优点:引用类型独立
// 缺点:无法继承原型方法
console.log(child.getName); // undefined
3) 组合继承(最常用)
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 第一次调用Parent
this.age = age;
}
Child.prototype = new Parent(); // 第二次调用Parent
Child.prototype.constructor = Child;
const child = new Child('child', 10);
// 优点:结合了两者优点
console.log(child.getName()); // "child"
child.colors.push('green');
// 缺点:调用了两次父类构造函数
4) 寄生组合继承(最优方案)
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 只调用一次Parent
this.age = age;
}
// 关键:使用Object.create
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
return this.age;
};
const child = new Child('child', 10);
console.log(child.getName()); // "child"
console.log(child.getAge()); // 10
// 原型链完整
console.log(child instanceof Child); // true
console.log(child instanceof Parent); // true
5) ES6 Class继承
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 必须先调用super
this.age = age;
}
getAge() {
return this.age;
}
}
const child = new Child('child', 10);
console.log(child.getName()); // "child"
// ES6 Class本质上是语法糖,底层仍使用原型链
console.log(Child.prototype.__proto__ === Parent.prototype); // true
7.2 手写instanceof
function myInstanceof(instance, Constructor) {
// 获取实例的原型
let proto = Object.getPrototypeOf(instance);
// 获取构造函数的原型对象
const prototype = Constructor.prototype;
// 沿着原型链查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
// 测试
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(myInstanceof(dog, Dog)); // true
console.log(myInstanceof(dog, Animal)); // true
console.log(myInstanceof(dog, Object)); // true
console.log(myInstanceof(dog, Array)); // false
7.3 实现私有属性
使用闭包(不依赖原型链)
function Person(name) {
// 私有变量
let _age = 0;
// 公共属性
this.name = name;
// 公共方法(访问私有变量)
this.getAge = function() {
return _age;
};
this.setAge = function(age) {
if (age > 0 && age < 150) {
_age = age;
}
};
}
const person = new Person('Alice');
person.setAge(25);
console.log(person.getAge()); // 25
console.log(person._age); // undefined - 无法直接访问
使用Symbol(ES6)
const _age = Symbol('age');
const _salary = Symbol('salary');
class Person {
constructor(name, age, salary) {
this.name = name;
this[_age] = age;
this[_salary] = salary;
}
getAge() {
return this[_age];
}
getSalary() {
return this[_salary];
}
}
const person = new Person('Bob', 30, 50000);
console.log(person.name); // "Bob"
console.log(person.getAge()); // 30
console.log(person[_age]); // undefined - Symbol作用域外无法访问
console.log(Object.keys(person)); // ["name"] - Symbol属性不可枚举
使用私有字段(ES2022)
class Person {
#age; // 私有字段
#salary;
constructor(name, age, salary) {
this.name = name;
this.#age = age;
this.#salary = salary;
}
getAge() {
return this.#age;
}
#calculateTax() { // 私有方法
return this.#salary * 0.2;
}
getNetSalary() {
return this.#salary - this.#calculateTax();
}
}
const person = new Person('Charlie', 35, 60000);
console.log(person.name); // "Charlie"
console.log(person.getAge()); // 35
// console.log(person.#age); // SyntaxError: 私有字段不可访问
7.4 实现发布-订阅模式
class EventEmitter {
constructor() {
// 存储事件及其回调
this.events = {};
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
// 触发事件
emit(eventName, ...args) {
const callbacks = this.events[eventName];
if (callbacks) {
callbacks.forEach(callback => callback(...args));
}
}
// 取消订阅
off(eventName, callback) {
const callbacks = this.events[eventName];
if (callbacks) {
this.events[eventName] = callbacks.filter(cb => cb !== callback);
}
}
// 一次性订阅
once(eventName, callback) {
const wrapper = (...args) => {
callback(...args);
this.off(eventName, wrapper);
};
this.on(eventName, wrapper);
}
}
// 使用
const emitter = new EventEmitter();
const handler1 = (data) => console.log('Handler 1:', data);
const handler2 = (data) => console.log('Handler 2:', data);
emitter.on('message', handler1);
emitter.on('message', handler2);
emitter.emit('message', 'Hello'); // 两个handler都被调用
emitter.off('message', handler1);
emitter.emit('message', 'World'); // 只有handler2被调用
emitter.once('greeting', (msg) => console.log(msg));
emitter.emit('greeting', 'Hi'); // "Hi"
emitter.emit('greeting', 'Hey'); // 无输出
八、性能优化与最佳实践
8.1 避免原型链过长
// 不推荐:原型链过长
function A() {}
function B() {}
B.prototype = new A();
function C() {}
C.prototype = new B();
function D() {}
D.prototype = new C();
const d = new D();
// 查找属性时需要遍历很长的原型链
// 推荐:扁平化设计或使用组合
class Component {
constructor() {
this.mixins = [
new LoggerMixin(),
new ValidationMixin(),
new CacheMixin()
];
}
callMixinMethod(method, ...args) {
this.mixins.forEach(mixin => {
if (mixin[method]) {
mixin[method](...args);
}
});
}
}
8.2 方法定义在原型上
// 不推荐:方法定义在构造函数内
function Person(name) {
this.name = name;
// 每个实例都创建新的函数
this.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
}
const p1 = new Person('A');
const p2 = new Person('B');
console.log(p1.sayHello === p2.sayHello); // false - 浪费内存
// 推荐:方法定义在原型上
function BetterPerson(name) {
this.name = name;
}
BetterPerson.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
const bp1 = new BetterPerson('A');
const bp2 = new BetterPerson('B');
console.log(bp1.sayHello === bp2.sayHello); // true - 共享方法
8.3 使用Object.create优化
// 不推荐
function Child() {}
Child.prototype = new Parent(); // 调用构造函数,可能有副作用
// 推荐
function Child() {}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// 或使用工具函数
function inherit(Child, Parent) {
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
});
}
8.4 属性查找缓存
class ExpensiveLookup {
constructor() {
this._cache = new Map();
}
getValue(key) {
// 检查缓存
if (this._cache.has(key)) {
return this._cache.get(key);
}
// 昂贵的查找操作
const value = this._expensiveOperation(key);
// 缓存结果
this._cache.set(key, value);
return value;
}
_expensiveOperation(key) {
// 模拟复杂计算
let result = key;
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i);
}
return result;
}
}
8.5 避免动态修改原型
// 不推荐:运行时修改原型会导致去优化
function Person() {}
const person = new Person();
// 引擎已经优化了Person的原型链
Person.prototype.newMethod = function() {
// 这会导致引擎重新优化
};
// 推荐:在创建实例前定义好所有原型方法
function BetterPerson() {}
BetterPerson.prototype.method1 = function() {};
BetterPerson.prototype.method2 = function() {};
BetterPerson.prototype.method3 = function() {};
// 或使用Object.assign一次性添加
Object.assign(BetterPerson.prototype, {
method1() {},
method2() {},
method3() {}
});
const betterPerson = new BetterPerson();
九、常见问题与陷阱
9.1 忘记使用new操作符
function Person(name) {
this.name = name;
}
// 忘记使用new
const person = Person('Alice');
console.log(person); // undefined
console.log(window.name); // "Alice" - 污染了全局对象
// 解决方案1:检测new
function SafePerson(name) {
if (!(this instanceof SafePerson)) {
return new SafePerson(name);
}
this.name = name;
}
// 解决方案2:使用ES6 Class(会强制new)
class ModernPerson {
constructor(name) {
this.name = name;
}
}
// const wrong = ModernPerson('Bob'); // TypeError: Class constructor cannot be invoked without 'new'
9.2 原型属性被意外覆盖
function Person() {}
Person.prototype.friends = ['Alice'];
const p1 = new Person();
const p2 = new Person();
// 错误:修改了原型上的数组
p1.friends.push('Bob');
console.log(p2.friends); // ['Alice', 'Bob'] - 被污染
// 正确:赋值操作会创建实例属性
p1.friends = ['Charlie'];
console.log(p2.friends); // ['Alice'] - 不受影响
console.log(Person.prototype.friends); // ['Alice', 'Bob'] - 仍然被污染
// 最佳实践:在构造函数中初始化
function SafePerson() {
this.friends = ['Alice']; // 每个实例独立
}
9.3 constructor属性丢失
function Parent() {}
function Child() {}
// 错误:直接赋值导致constructor丢失
Child.prototype = Parent.prototype;
console.log(new Child().constructor === Child); // false
// 错误:创建新对象但没有设置constructor
Child.prototype = Object.create(Parent.prototype);
console.log(new Child().constructor === Child); // false
// 正确:手动恢复constructor
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
console.log(new Child().constructor === Child); // true
9.4 instanceof的局限性
// 跨iframe或跨上下文失效
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = iframe.contentWindow.Array;
const arr = new iframeArray();
console.log(arr instanceof Array); // false
console.log(arr instanceof iframeArray); // true
// 解决方案:使用Array.isArray()
console.log(Array.isArray(arr)); // true
// 原型可以被修改
function Fake() {}
const obj = {};
Object.setPrototypeOf(obj, Fake.prototype);
console.log(obj instanceof Fake); // true - 但obj不是由Fake创建的
9.5 使用箭头函数的陷阱
function Person(name) {
this.name = name;
}
// 错误:箭头函数没有prototype
Person.prototype.sayHello = () => {
console.log(`Hello, ${this.name}`); // this指向错误
};
const person = new Person('Alice');
person.sayHello(); // "Hello, undefined"
// 正确:使用普通函数
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
// 箭头函数适合内部回调
Person.prototype.greetAll = function(people) {
people.forEach(person => {
console.log(`${this.name} greets ${person}`); // 正确捕获外层this
});
};
9.6 Object.create(null)的特殊性
// 普通对象有原型链
const normal = {};
console.log(normal.toString); // [Function: toString]
console.log(normal.__proto__ === Object.prototype); // true
// 无原型对象
const pure = Object.create(null);
console.log(pure.toString); // undefined
console.log(pure.__proto__); // undefined
// 适用场景:纯粹的字典对象
const map = Object.create(null);
map['__proto__'] = 'value'; // 安全:作为普通属性
console.log(map['__proto__']); // "value"
// 普通对象会有问题
const normalMap = {};
normalMap['__proto__'] = 'value'; // 尝试设置原型
console.log(normalMap['__proto__']); // {} - 不是我们期望的字符串
总结
核心要点
-
原型链是JavaScript继承的基础
- 通过
__proto__连接对象,形成链式查找 prototype是函数特有的,用于定义共享属性和方法constructor连接原型与构造函数
- 通过
-
属性查找遵循就近原则
- 先查找自身属性
- 再沿原型链向上查找
- 直到
Object.prototype或null
-
继承方式的演进
- 原型链继承 → 构造函数继承 → 组合继承 → 寄生组合继承
- ES6 Class是语法糖,底层仍是原型链
- 现代框架倾向于使用Hooks/Composition API减少对原型链的依赖
-
性能优化建议
- 方法定义在原型上,避免重复创建
- 避免原型链过长
- 不要在运行时频繁修改原型
- 使用缓存优化属性查找
-
在Vue/React中的实践
- Vue 2大量使用原型链实现全局API和插件系统
- Vue 3和React Hooks减少了对原型链的依赖
- 理解原型链有助于深入理解框架内部机制
–
更多推荐


所有评论(0)