🔥90%的开发者都搞错了!JavaScript中prototype、proto、constructor的3个致命陷阱🔥

91 阅读5分钟

文章前言:为什么你必须读完这篇(即使现在就想关掉页面)

“说不清 prototype 和 __proto__ 的区别?抱歉,下一位。”

三年前,小明自信满满地走进字节跳动终面,却被一道基础题钉在原地:
“请画出 p1 = new Person() 的完整原型链,并解释 constructor 为何可能失效。”

他张了张嘴——那些背烂的面试八股文,此刻像被拔掉网线的代码,死活连不上

别担心——90% 的开发者都掉进过同一个认知陷阱
把 prototype 当对象属性,用 __proto__ 当万能胶水,却对 constructor 的“叛变”毫无防备。

但今天,这一切终结了。

本文将用一张可保存的原型链全景图  带你亲手拆解 JavaScript 原型系统的 DNA。

现在,深吸一口气,继续朝下看。
5 分钟后,你会回来感谢自己没关掉这个页面。

一张图 & 一段代码:拆解原型链

function Person(name, age) {
 this.name = name;
 this.age = age;
}

let p1 = new Person('Skye', 18);

image.png

prototype 属性(函数专属)

  • 存在位置只有函数有 prototype 属性(如 function Person(){})。

  • 作用
    当通过 new 创建实例时,实例的 __proto__ 会指向构造函数的 prototype
    prototype 是共享资源池,所有实例共享其中的属性和方法,也就是说,由此构造函数创造的对象,都可以利用定义在prototype的方法

    例如我们定义的数组,都可以用数组原型链的方法

    const arr = [1,2,3,4,5,[6,7,8]];
    console.log(arr.flat());
    
    

屏幕截图 2025-08-06 162948.png 在下面的例子中,最具体的体现就是:经由Person构造出来的实例对象,都可以利用以下定义在prototype中的方法,如:sayHi,walk,doWork......

  • 结构
   function Person(name) {

   this.name = name; // 实例独有属性

   }

   // Person.prototype 是一个对象

   Person.prototype.sayHi = function() {

   console.log("Hello " + this.name);

   };
   
   Person.prototype.walk = function(){
   //...
   }
   Person.prototype.doWork = function(){
   //...
   }
   Person.prototype.eatMeals = function(){
   //...
   }
   Person.prototype.sleep = function(){
   //...
   }
  • Person.prototype 默认包含:

    • constructor 属性(指向 Person 函数)
    • 自定义的共享方法(如 sayHi等等)
    • PS: 如果没有自定义方法,则Person.prototype默认只包含一个constructor属性

image.png

嘿!我相信大家一定有一个疑惑:

主播主播,prototype不是只存在于函数中吗,怎么ObjectArray中也有Prototype呢?

哎呀呀!这问题地道~

在 JavaScript 中,Object 并不是一个普通的对象字面量(如 {}),而是一个内置的构造函数

你可以这样验证:

typeof Object; // "function"
Object instanceof Function; // true

👉 所以 Object 本质上是一个函数,和其他构造函数(如 ArrayStringPerson)地位相同。

函数是否有 prototype说明
function Person(){}✅ 有自定义构造函数
Array✅ 有内置构造函数:Array.prototype
String✅ 有内置构造函数:String.prototype
Object✅ 有内置构造函数:Object.prototype

 __proto__ 属性(对象通用)

image.png __proto__叫做double underscore proto,前后各有两个下划线,由于字体原因,显示不同,在这里连到一起了。

  • 存在位置所有对象都有 __proto__(包括函数,因为函数也是对象)。

  • 作用

    • 指向创建该对象的构造函数的 prototype ,是原型链的链接指针。 也就是说,__proto__会指向它们的原型对象,也就是父对象,谁创造了它们,它们的__proto__就指向谁。

    • 当访问对象属性时,若自身没有,会沿着 __proto__ 向上查找,一直找到null为止(原型链)

var obj1 = {
    a: 3
}

var obj2 = Object.create(obj1, {
    b: {
        value: 1,    // 创建变量b
        writable: true,
        enumerable: true,
        configurable: true
    }
});
var obj3 = Object.create(obj2, {
    c: {
        value: 9,   // 创建变量c
        writable: true,
        enumerable: true,
        configurable: true
    }
})
console.log(obj3.a);

在上面的例子中,我们创建了三个对象,其中关系为:obj3继承obj2,obj2继承obj1,按理说,obj3只存在c这个元素,但是我们访问obj3.a还能够访问到,在这里访问的实际上是obj1.a,引擎沿着原型链向上查找,一直查找到了原型链的顶部,找到了变量。

如果一直取到顶部都没查找到变量,就会返回undefined

  • 关键关系
   const p1 = new Person("Alice");
   p1.__proto__ === Person.prototype; // true ✅
  • 普通对象:{} 的 __proto__ 指向 Object.prototype
  • 函数对象:Person 的 __proto__ 指向 Function.prototype

constructor属性

constructor属性也是所有对象都拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,它只存在于prototype属性中。

image.png

  • prototype.constructor 的作用是:标识“我是谁的原型”

  • .constructor的意思就是:谁构造了我(一般用于实例或具体的函数)

比如:

p1.constructor就是谁构造了p1,输出为function Person(){ //.....}

再比如 Function.prototype.constructor ,意思就是Function是谁的原型,则答案自然指向Function

每个对象都有constructor属性?Why?

大家学到这里可能又有疑问了:constructor属性只存在于prototype,而prototype只存在于函数,那么为什么每个对象都有constructor属性呢?

其实,并不是所有对象都拥有constructor属性,而是所有对象都能访问constructor属性

所有对象的原型链最终都能追溯到Object.prototype,而Object.prototype.constructor值为Function Object(){},当我们访问对象的constructor属性时,引擎沿着原型链向上查找,最终查找到Object.prototype,得到了其属性,返回了值,所以我们说每个对象都有constructor 属性。

image.png

总结

所有对象都拥有__proto__以及constructor属性(实际不是拥有,只是能访问constructor)。

prototype只有函数会有,其作用是创建公用库,使得继承它的函数能够使用库里的属性和方法

__proto__属性的作用就是访问创建该对象的构造函数的prototype,并且使得引擎访问对象属性时沿着原型链向上查找一直到null

顺嘴一提:for in 循环就是查找key用的,查找属性时会沿着原型链查找

OK,这一期就是这样了,如果这篇文章对你有帮助,请动动小手点个赞吧!如果有错误,请各位大佬指出呀!