尧图精选

JS原型链与继承,面试必考点通俗讲解

🕒 发布时间:2026/9/3 20:55:04 📁 来源:尧图网络
作为一名前端打工人面试前临时抱佛脚翻看原型链的资料估计是很多人都有过的经历。说实话我第一次看《JavaScript高级程序设计》里关于原型链那章时真的感觉每个字都认识但连在一起就是看不懂。什么__proto__、prototype、constructor这三个家伙绕来绕去简直比男女朋友的关系还难捋。但后来我发现只要换个思路别把它当成“继承”来理解而是当成JavaScript里的**对象找东西的机制**一下子就通了。今天咱们就掰开了揉碎了用人话把这层窗户纸捅破。### 一切要从“找东西”说起假设你有一个空对象obj但你突然想调用一个toString()方法javascriptlet obj {};console.log(obj.toString()); // 输出 [object Object]明明obj里什么都没有为什么没报错因为它顺着一条“隐形链条”往上找了。这条链子就是原型链Prototype Chain。JavaScript里的每个对象肚子里都藏着一个“神秘指针”在浏览器里叫__proto__它指向自己的“爸爸”原型对象。当你访问一个属性时JS会先在自己身上找找不到就顺着__proto__去找爸爸要爸爸没有再找爷爷直到找到顶端的null为止。### 别再搞混那两个“prototype”了这是初学者最容易栽跟头的地方。记住一句口诀**函数才有prototype实例才有__proto__**。- prototype这是**函数**特有的属性。它指向一个对象这个对象就是将来通过new该函数创建出来的实例的“爸爸”。- __proto__这是**实例**对象特有的属性实际上大部分浏览器都实现了它指向当前对象的“爸爸”。我们写个构造函数看看javascriptfunction Person(name) {this.name name;}Person.prototype.sayHello function() {console.log(你好我是 this.name);};let p1 new Person(张三);console.log(p1.__proto__ Person.prototype); // true当你new Person()时JS在背后做了一件事p1.__proto__ Person.prototype。所以p1能调用sayHello是因为p1自己没这方法但通过__proto__找到了Person.prototype。### 原型链的尽头是什么继续往上翻。既然Person.prototype是一个对象那这个对象的爸爸是谁javascriptconsole.log(Person.prototype.__proto__ Object.prototype); // true没错所有普通对象的源头都是Object.prototype。那Object.prototype的爸爸呢javascriptconsole.log(Object.prototype.__proto__); // null到这儿就是尽头了。### 真正的继承组合寄生式继承面试官最烦听到“JS是类继承”其实JS压根没有类的概念ES6的class只是语法糖。真正的继承核心是**让子类的原型指向父类的实例**或者更优雅一点——寄生组合式继承。道理我都懂但以前我写继承总是写成这样结果出问题了javascriptfunction Father() {this.money 100;}Father.prototype.pay function() {};function Son() {Father.call(this); // 借用构造函数解决属性共享问题}Son.prototype new Father(); // 这里有问题把父类的实例当原型这样写虽然能继承但调用了两次父类构造函数而且Son.prototype.constructor指向乱了。正确的写法面试高频手写题应该是javascriptfunction inherit(Son, Father) {let prototype Object.create(Father.prototype); // 创建中间对象prototype.constructor Son; // 修正constructor指向Son.prototype prototype;}这个Object.create说白了就是javascriptfunction create(obj) {function F() {}F.prototype obj;return new F();}找个空函数做“中介”这样就不会污染父类的实例了。### ES6 class 到底改变了什么现在很多项目都用class和extends写着是真爽但面试官会问“这跟原型链有什么关系”javascriptclass Animal {constructor(type) {this.type type;}eat() {console.log(吃东西);}}class Dog extends Animal {constructor(type, name) {super(type);this.name name;}bark() {console.log(汪汪);}}虽然写法像Java但编译成ES5后本质上还是那套原型链操作。extends就是把Dog.prototype.__proto__指向Animal.prototype。所以用class只是换了个皮肤内核没变。### 几个让我“顿悟”的瞬间曾经我为了搞懂instanceof的原理踩过很多坑。instanceof判断的是**右边函数的prototype是否出现在左边对象的原型链上**。javascriptfunction A() {}let a new A();console.log(a instanceof A); // trueconsole.log(a instanceof Object); // true只要顺着a.__proto__往上找能找到A.prototype或Object.prototype就是true。还有个冷知识所有函数本身也是对象它们都是Function的实例。所以javascriptfunction test() {}console.log(test.__proto__ Function.prototype); // trueconsole.log(Function.prototype.__proto__ Object.prototype); // true这就解释了为什么函数也能有toString、call等方法。### 面试官最想听什么我个人经验是面试官听你背概念其实很反感但如果能说出下面这三点基本稳了1. **属性查找机制**先找自身再沿__proto__链向上直到null。2. **修改原型影响全局**如果改Array.prototype所有数组都会受影响虽然不推荐这么做。3. **hasOwnProperty的重要性**用来区分属性是自己的还是继承来的。平时写代码我偶尔会用原型链做“方法共享”但切记不要滥用。比如把公共方法挂在prototype上而把容易变化的状态如name、age放在构造函数里用this定义。这样既省内存又不会互相影响。最后总结一句**原型链的本质就是对象之间的引用关系**。别把它想成高大上的“继承”就想象成一个孩子问爸爸要零花钱爸爸没有就去找爷爷爷爷再没有就找太爷爷。搞懂这条链子JS的面向对象就算真正入门了。希望这篇文章能帮你省下几根头发。如果面试时真的被问到了别慌画个图把prototype和__proto__指给面试官看比背十遍概念都管用。祝大家offer拿到手软
上一篇/下一篇内容由系统自动关联 返回资讯列表 →