原型继承中如何阻止间接子类访问父类的属性与方法?
问题说明
先看第一个原型链示例:
let animal = { eats: true, walk() { alert("Animal walk"); } }; let rabbit = { jumps: true, __proto__: animal }; let longEar = { earLength: 10, __proto__: rabbit }; // walk 通过原型链被调用 longEar.walk(); // Animal walk alert(longEar.jumps); // true (来自rabbit)
原本以为只有直接子类rabbit能访问animal的属性和方法,但实际上间接子类longEar也能通过原型链调用walk并访问相关属性。
再看第二个场景:
let horse = { jumps: true, __proto__: animal }; let longHoof = { hoofLength: 10, __proto__: horse };
需求是让直接子类horse能访问animal的属性和方法,但间接子类longHoof无法访问,原生原型链机制做不到,下面是几种实现思路:
方法1:Symbol+原型检查
用Symbol标记私有属性/方法,再通过检查调用者的直接原型是否为animal来限制访问:
// 用Symbol定义私有键,避免外部直接访问 const privateKeys = { eats: Symbol('eats'), walk: Symbol('walk') }; let animal = { [privateKeys.eats]: true, [privateKeys.walk]() { alert("Animal walk"); }, // 对外暴露的访问入口,加原型检查 walk() { if (Object.getPrototypeOf(this) !== animal) { throw new Error('无权访问此方法'); } this[privateKeys.walk](); }, getEats() { if (Object.getPrototypeOf(this) !== animal) { throw new Error('无权访问此属性'); } return this[privateKeys.eats]; } }; let horse = { jumps: true, __proto__: animal }; let longHoof = { hoofLength: 10, __proto__: horse }; // 直接子类正常访问 horse.walk(); // 弹出"Animal walk" console.log(horse.getEats()); // true // 间接子类被拦截 longHoof.walk(); // 抛出错误:无权访问此方法 console.log(longHoof.getEats()); // 抛出错误:无权访问此属性
方法2:闭包封装私有逻辑
把animal的核心属性和方法放在闭包里,仅允许直接原型指向animal的对象访问:
const createAnimal = () => { // 闭包内的私有变量,外部无法直接访问 const eats = true; const walk = () => alert("Animal walk"); return { walk() { if (Object.getPrototypeOf(this) === this) { walk(); } else { throw new Error('无权访问此方法'); } }, getEats() { if (Object.getPrototypeOf(this) === this) { return eats; } else { throw new Error('无权访问此属性'); } } }; }; let animal = createAnimal(); let horse = { jumps: true, __proto__: animal }; let longHoof = { hoofLength: 10, __proto__: horse }; horse.walk(); // 正常执行 console.log(horse.getEats()); // true longHoof.walk(); // 报错 console.log(longHoof.getEats()); // 报错
方法3:ES6类+私有字段+原型检查
用ES6类的私有字段(#开头)结合原型检查,更符合现代JS语法:
class Animal { // 私有字段,仅类内部可访问 #eats = true; walk() { // 检查调用者的直接原型是否是Animal的原型 if (Object.getPrototypeOf(this) !== Animal.prototype) { throw new Error('无权访问此方法'); } alert("Animal walk"); } getEats() { if (Object.getPrototypeOf(this) !== Animal.prototype) { throw new Error('无权访问此属性'); } return this.#eats; } } class Horse extends Animal { jumps = true; } class LongHoof extends Horse { hoofLength = 10; } const horse = new Horse(); const longHoof = new LongHoof(); horse.walk(); // 正常执行 console.log(horse.getEats()); // true longHoof.walk(); // 报错 console.log(longHoof.getEats()); // 报错
核心思路
所有方法的核心都是检查调用者的直接原型是否为animal(或Animal.prototype):直接子类horse的this原型指向animal,所以能通过检查;而间接子类longHoof的this原型指向horse,会被拦截。
内容的提问来源于stack exchange,提问作者x3rx3s
相关产品推荐
相关产品推荐

