Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript原型链教程,详解原型链查找机制、Object.prototype终点、原型污染问题。包含完整代码示例和安全防护,适合前端开发者快速掌握原型链工作原理和最佳实践。
核心关键词:JavaScript原型链2024、原型链查找机制、Object.prototype、原型污染、JavaScript继承原理
长尾关键词:JavaScript原型链怎么工作、原型链查找过程、Object.prototype作用、原型污染攻击防护、JavaScript原型链详解
通过本节JavaScript原型链的工作原理,你将系统性掌握:
原型链是JavaScript实现继承的核心机制,它通过对象之间的链式连接实现了属性和方法的共享。理解原型链的工作原理,就理解了JavaScript面向对象编程的本质和性能优化的关键。
💡 核心理解:原型链是一条由__proto__连接的对象链,JavaScript引擎沿着这条链查找属性和方法
原型链查找是JavaScript引擎访问对象属性时的核心算法,它按照特定的顺序在原型链上查找属性。
// 🎉 原型链查找机制演示
function Animal(name) {
this.name = name;
this.instanceMethod = function() {
return `${this.name} instance method`;
};
}
Animal.prototype.prototypeMethod = function() {
return `${this.name} prototype method`;
};
Animal.prototype.species = 'Unknown';
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() {
return `${this.name} is barking`;
};
const myDog = new Dog('Buddy', 'Golden Retriever');
// 演示查找过程
console.log('=== 原型链查找演示 ===');
// 1. 访问实例属性(第一层:实例对象)
console.log('myDog.name:', myDog.name); // "Buddy" - 在实例上找到
// 2. 访问实例方法(第一层:实例对象)
console.log('myDog.instanceMethod():', myDog.instanceMethod()); // 在实例上找到
// 3. 访问Dog原型方法(第二层:Dog.prototype)
console.log('myDog.bark():', myDog.bark()); // 在Dog.prototype上找到
// 4. 访问Animal原型方法(第三层:Animal.prototype)
console.log('myDog.prototypeMethod():', myDog.prototypeMethod()); // 在Animal.prototype上找到
// 5. 访问Animal原型属性(第三层:Animal.prototype)
console.log('myDog.species:', myDog.species); // 在Animal.prototype上找到
// 6. 访问Object原型方法(第四层:Object.prototype)
console.log('myDog.toString():', myDog.toString()); // 在Object.prototype上找到
// 7. 访问不存在的属性(查找到null,返回undefined)
console.log('myDog.nonExistent:', myDog.nonExistent); // undefined// 🎉 模拟原型链查找过程
function simulatePropertyLookup(obj, propertyName) {
let current = obj;
let level = 0;
const lookupPath = [];
console.log(`=== 查找属性 "${propertyName}" ===`);
while (current !== null) {
const levelName = level === 0 ? '实例对象' :
current.constructor ? `${current.constructor.name}.prototype` :
'Object.prototype';
lookupPath.push(levelName);
console.log(`第${level + 1}层 - ${levelName}:`);
// 检查当前层是否有该属性
if (current.hasOwnProperty(propertyName)) {
console.log(` ✓ 找到属性 "${propertyName}"`);
console.log(` ✓ 值: ${current[propertyName]}`);
console.log(` ✓ 类型: ${typeof current[propertyName]}`);
console.log(` ✓ 查找路径: ${lookupPath.join(' → ')}`);
return {
found: true,
value: current[propertyName],
level: level,
path: lookupPath
};
} else {
console.log(` ✗ 未找到,继续向上查找...`);
}
current = current.__proto__;
level++;
}
console.log(` ✗ 查找结束,属性 "${propertyName}" 不存在`);
console.log(` ✓ 查找路径: ${lookupPath.join(' → ')} → null`);
return {
found: false,
value: undefined,
level: -1,
path: lookupPath
};
}
// 测试查找过程
simulatePropertyLookup(myDog, 'name'); // 实例属性
simulatePropertyLookup(myDog, 'bark'); // Dog.prototype方法
simulatePropertyLookup(myDog, 'species'); // Animal.prototype属性
simulatePropertyLookup(myDog, 'toString'); // Object.prototype方法
simulatePropertyLookup(myDog, 'nonExistent'); // 不存在的属性// 🎉 属性遮蔽机制
function Parent() {}
Parent.prototype.value = 'parent value';
Parent.prototype.method = function() {
return 'parent method';
};
function Child() {
this.value = 'child value'; // 遮蔽父类属性
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// 重写父类方法
Child.prototype.method = function() {
return 'child method';
};
const child = new Child();
console.log('=== 属性遮蔽演示 ===');
console.log('child.value:', child.value); // "child value" - 实例属性遮蔽原型属性
console.log('child.method():', child.method()); // "child method" - 子类方法遮蔽父类方法
// 访问被遮蔽的父类成员
console.log('Parent.prototype.value:', Parent.prototype.value); // "parent value"
console.log('Parent.prototype.method.call(child):', Parent.prototype.method.call(child)); // "parent method"
// 删除遮蔽属性后的查找
delete child.value;
console.log('child.value after delete:', child.value); // "parent value" - 回到原型属性
// 动态遮蔽
child.method = function() {
return 'instance method';
};
console.log('child.method() after instance assignment:', child.method()); // "instance method"
delete child.method;
console.log('child.method() after delete:', child.method()); // "child method" - 回到原型方法原型链查找特点:
Object.prototype是所有对象原型链的终点,它提供了所有JavaScript对象的基础方法。
// 🎉 Object.prototype详解
console.log('=== Object.prototype分析 ===');
// Object.prototype的原型是null
console.log('Object.prototype.__proto__:', Object.prototype.__proto__); // null
// Object.prototype的内置方法
const objectMethods = Object.getOwnPropertyNames(Object.prototype)
.filter(name => typeof Object.prototype[name] === 'function');
console.log('Object.prototype的方法:', objectMethods);
// ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf']
// 演示Object.prototype方法的使用
const testObj = {
name: 'Test Object',
value: 42
};
console.log('=== Object.prototype方法演示 ===');
// toString方法
console.log('testObj.toString():', testObj.toString()); // "[object Object]"
// valueOf方法
console.log('testObj.valueOf():', testObj.valueOf()); // 返回对象本身
// hasOwnProperty方法
console.log('testObj.hasOwnProperty("name"):', testObj.hasOwnProperty('name')); // true
console.log('testObj.hasOwnProperty("toString"):', testObj.hasOwnProperty('toString')); // false
// isPrototypeOf方法
console.log('Object.prototype.isPrototypeOf(testObj):', Object.prototype.isPrototypeOf(testObj)); // true
// propertyIsEnumerable方法
console.log('testObj.propertyIsEnumerable("name"):', testObj.propertyIsEnumerable('name')); // true
console.log('testObj.propertyIsEnumerable("toString"):', testObj.propertyIsEnumerable('toString')); // false// 🎉 Object.prototype方法的重写
function CustomObject(data) {
this.data = data;
}
// 重写toString方法
CustomObject.prototype.toString = function() {
return `CustomObject: ${JSON.stringify(this.data)}`;
};
// 重写valueOf方法
CustomObject.prototype.valueOf = function() {
return this.data;
};
const customObj = new CustomObject({ name: 'Test', value: 123 });
console.log('=== 重写Object.prototype方法 ===');
console.log('customObj.toString():', customObj.toString()); // 自定义toString
console.log('customObj.valueOf():', customObj.valueOf()); // 自定义valueOf
// 调用原始的Object.prototype方法
console.log('Object.prototype.toString.call(customObj):', Object.prototype.toString.call(customObj)); // "[object Object]"
console.log('Object.prototype.valueOf.call(customObj):', Object.prototype.valueOf.call(customObj)); // 返回对象本身
// 类型转换中的方法调用
console.log('String(customObj):', String(customObj)); // 调用toString
console.log('Number(customObj):', Number(customObj)); // 调用valueOf,然后转换
// 恢复原始方法
delete CustomObject.prototype.toString;
delete CustomObject.prototype.valueOf;
console.log('=== 恢复原始方法后 ===');
console.log('customObj.toString():', customObj.toString()); // "[object Object]"
console.log('customObj.valueOf():', customObj.valueOf()); // 返回对象本身// 🎉 创建没有原型的纯净对象
// 方法1:使用Object.create(null)
const pureObject1 = Object.create(null);
pureObject1.name = 'Pure Object';
console.log('=== 纯净对象(无原型)===');
console.log('pureObject1.__proto__:', pureObject1.__proto__); // undefined
console.log('pureObject1.toString:', pureObject1.toString); // undefined
console.log('pureObject1.hasOwnProperty:', pureObject1.hasOwnProperty); // undefined
// 无法使用Object.prototype的方法
try {
console.log(pureObject1.toString());
} catch (error) {
console.log('Error:', error.message); // "pureObject1.toString is not a function"
}
// 但可以直接调用Object.prototype的方法
console.log('Object.prototype.toString.call(pureObject1):', Object.prototype.toString.call(pureObject1)); // "[object Object]"
// 方法2:使用Object.setPrototypeOf设置为null
const pureObject2 = {};
Object.setPrototypeOf(pureObject2, null);
console.log('pureObject2.__proto__:', pureObject2.__proto__); // undefined
// 纯净对象的用途:作为字典使用,避免原型污染
const dictionary = Object.create(null);
dictionary['constructor'] = 'safe value';
dictionary['__proto__'] = 'safe value';
dictionary['toString'] = 'safe value';
console.log('=== 字典对象 ===');
console.log('dictionary.constructor:', dictionary.constructor); // "safe value"
console.log('dictionary.__proto__:', dictionary.__proto__); // "safe value"
console.log('dictionary.toString:', dictionary.toString); // "safe value"Object.prototype特点:
原型污染是一种安全漏洞,攻击者通过修改Object.prototype来影响所有对象。
// 🎉 原型污染攻击演示(仅用于学习,请勿在生产环境使用)
console.log('=== 原型污染攻击演示 ===');
// 正常情况下的对象
const normalObj = {};
console.log('normalObj.isAdmin:', normalObj.isAdmin); // undefined
// 模拟原型污染攻击
Object.prototype.isAdmin = true;
// 所有对象都被污染了
const newObj = {};
console.log('newObj.isAdmin:', newObj.isAdmin); // true
const userObj = { name: 'Alice' };
console.log('userObj.isAdmin:', userObj.isAdmin); // true
// 甚至影响已存在的对象
console.log('normalObj.isAdmin:', normalObj.isAdmin); // true
// 清理污染
delete Object.prototype.isAdmin;
console.log('After cleanup - newObj.isAdmin:', newObj.isAdmin); // undefined// 🎉 原型污染防护措施
// 防护措施1:使用Object.create(null)创建纯净对象
function createSafeObject(data) {
const safeObj = Object.create(null);
if (data && typeof data === 'object') {
Object.assign(safeObj, data);
}
return safeObj;
}
// 防护措施2:安全的属性设置
function safeSetProperty(obj, key, value) {
// 检查危险的属性名
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
if (dangerousKeys.includes(key)) {
console.warn(`Blocked attempt to set dangerous property: ${key}`);
return false;
}
// 检查是否会污染原型链
if (key in Object.prototype) {
console.warn(`Blocked attempt to override Object.prototype property: ${key}`);
return false;
}
obj[key] = value;
return true;
}
// 防护措施3:安全的JSON解析
function safeJSONParse(jsonString) {
try {
const parsed = JSON.parse(jsonString);
return sanitizeObject(parsed);
} catch (error) {
console.error('JSON parse error:', error);
return null;
}
}
function sanitizeObject(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
const sanitized = Object.create(null);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 过滤危险属性
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
// 递归清理嵌套对象
if (typeof obj[key] === 'object' && obj[key] !== null) {
sanitized[key] = sanitizeObject(obj[key]);
} else {
sanitized[key] = obj[key];
}
}
}
return sanitized;
}
// 防护措施4:冻结关键原型
function protectPrototypes() {
// 冻结Object.prototype
Object.freeze(Object.prototype);
// 冻结其他重要原型
Object.freeze(Array.prototype);
Object.freeze(Function.prototype);
Object.freeze(String.prototype);
Object.freeze(Number.prototype);
Object.freeze(Boolean.prototype);
console.log('Critical prototypes have been frozen');
}
// 测试防护措施
console.log('=== 原型污染防护测试 ===');
const safeObj = createSafeObject({ name: 'Safe Object' });
console.log('safeObj.__proto__:', safeObj.__proto__); // undefined
safeSetProperty(safeObj, 'value', 42); // 成功
safeSetProperty(safeObj, '__proto__', {}); // 被阻止
safeSetProperty(safeObj, 'toString', 'hacked'); // 被阻止
// 测试安全JSON解析
const maliciousJSON = '{"name": "user", "__proto__": {"isAdmin": true}}';
const safeParsed = safeJSONParse(maliciousJSON);
console.log('safeParsed:', safeParsed); // 不包含__proto__
// 注意:protectPrototypes()会影响所有代码,谨慎使用
// protectPrototypes();// 🎉 原型污染检测工具
function detectPrototypePollution() {
const results = {
polluted: false,
pollutedProperties: []
};
// 检查Object.prototype是否被污染
const objectProtoKeys = Object.getOwnPropertyNames(Object.prototype);
const expectedKeys = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf'];
for (const key of objectProtoKeys) {
if (!expectedKeys.includes(key)) {
results.polluted = true;
results.pollutedProperties.push({
prototype: 'Object.prototype',
property: key,
value: Object.prototype[key]
});
}
}
// 检查其他重要原型
const prototypesToCheck = [
{ name: 'Array.prototype', proto: Array.prototype },
{ name: 'Function.prototype', proto: Function.prototype },
{ name: 'String.prototype', proto: String.prototype }
];
prototypesToCheck.forEach(({ name, proto }) => {
const keys = Object.getOwnPropertyNames(proto);
// 这里可以添加更详细的检查逻辑
});
return results;
}
// 监控原型污染
function monitorPrototypePollution() {
const originalDefineProperty = Object.defineProperty;
Object.defineProperty = function(obj, prop, descriptor) {
// 检查是否在污染原型
if (obj === Object.prototype || obj === Array.prototype || obj === Function.prototype) {
console.warn(`Potential prototype pollution detected: ${prop} on ${obj.constructor.name}.prototype`);
}
return originalDefineProperty.call(this, obj, prop, descriptor);
};
}
// 使用检测工具
const pollutionResult = detectPrototypePollution();
console.log('=== 原型污染检测结果 ===');
console.log('Is polluted:', pollutionResult.polluted);
console.log('Polluted properties:', pollutionResult.pollutedProperties);
// 启用监控(可选)
// monitorPrototypePollution();原型污染防护要点:
// 🎉 原型链性能测试
function performanceTest() {
// 创建深层原型链
function Level1() { this.level1Prop = 'level1'; }
function Level2() { this.level2Prop = 'level2'; }
function Level3() { this.level3Prop = 'level3'; }
function Level4() { this.level4Prop = 'level4'; }
function Level5() { this.level5Prop = 'level5'; }
Level2.prototype = Object.create(Level1.prototype);
Level3.prototype = Object.create(Level2.prototype);
Level4.prototype = Object.create(Level3.prototype);
Level5.prototype = Object.create(Level4.prototype);
// 在不同层级添加方法
Level1.prototype.method1 = function() { return 'method1'; };
Level3.prototype.method3 = function() { return 'method3'; };
Level5.prototype.method5 = function() { return 'method5'; };
const deepObj = new Level5();
// 性能测试
const iterations = 1000000;
console.log('=== 原型链性能测试 ===');
// 测试实例属性访问
console.time('Instance property access');
for (let i = 0; i < iterations; i++) {
deepObj.level5Prop;
}
console.timeEnd('Instance property access');
// 测试浅层原型方法访问
console.time('Shallow prototype method access');
for (let i = 0; i < iterations; i++) {
deepObj.method5();
}
console.timeEnd('Shallow prototype method access');
// 测试深层原型方法访问
console.time('Deep prototype method access');
for (let i = 0; i < iterations; i++) {
deepObj.method1();
}
console.timeEnd('Deep prototype method access');
// 测试不存在属性访问
console.time('Non-existent property access');
for (let i = 0; i < iterations; i++) {
deepObj.nonExistent;
}
console.timeEnd('Non-existent property access');
}
// 运行性能测试
performanceTest();通过本节JavaScript原型链的工作原理的学习,你已经掌握:
A: 原型链越深,查找性能越差。建议将常用方法放在较浅的原型层级,避免过深的继承层次。现代JavaScript引擎有优化,但仍需注意。
A: 使用Object.create(null)创建纯净对象,验证用户输入,过滤危险属性名,在关键应用中冻结重要原型。
A: 因为Object.prototype是原型链的顶端,它没有更上层的原型,所以__proto__指向null,这是原型链的终点。
A: 是的,修改Object.prototype会影响所有对象(除了用Object.create(null)创建的对象),这就是原型污染的危险所在。
A: 可以编写函数遍历原型链,使用hasOwnProperty()检查每一层,或使用Object.getPrototypeOf()逐层查找。
"原型链是JavaScript继承的脊梁,理解查找机制就理解了性能优化的关键。记住:浅层快速,深层谨慎,安全第一!"