深入理解JavaScript中__proto__与prototype的关系

作者:代码拾荒时间:2026-10-27 06:43:03浏览:451

本文深入解析JavaScript中__proto__与prototype的核心关系。阐述所有构造函数的__proto__均指向Function.prototype,而实例对象的__proto__指向其构造函数的prototype。通过内置对象与自定义构造器的代码示例,揭示原型链的继承机制及constructor属性的行为差异,帮助开发者清晰掌握对象创建与属性查找逻辑。

JavaScript原型链核心概念解析

本文旨在探讨JavaScript中对象的内部原型属性(__proto__)与构造函数的原型属性(prototype)之间的关联与区别。


一、所有构造函数或函数的__proto__属性均指向Function.prototype,该属性本身是一个空函数(Empty function)。


具体验证代码如下:

 
Number.__proto__ === Function.prototype // true
Boolean.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
RegExp.__proto__ === Function.prototype // true
Error.__proto__ === Function.prototype // true
Date.__proto__ === Function.prototype // true

JavaScript在ES5标准中内置了12个构造器或对象(其中包括新增的JSON)。上述代码列举了其中8个可直接访问的构造器。其余如Global对象无法直接访问,Arguments仅在函数调用时由JavaScript引擎创建,而Math和JSON以普通对象形式存在,无需使用new关键字实例化。它们的__proto__属性指向Object.prototype,验证如下:


代码示例:

 
Math.__proto__ === Object.prototype // true
JSON.__proto__ === Object.prototype // true

上述关于“所有构造函数/函数”的结论同样适用于自定义函数。示例如下:


代码示例:

 
// 函数声明形式
function Person() {}
// 函数表达式形式
var Man = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype) // true

这一现象说明了什么?


所有的构造函数均源自Function.prototype,甚至包括根构造函数Object以及Function自身。因此,所有构造函数都继承了Function.prototype上的属性和方法,例如length、call、apply以及ES5中新增的bind方法。


Function.prototype是唯一一个typeof XXX.prototype返回“function”的原型对象。其他所有构造函数的prototype属性均指向一个普通对象。验证如下:


代码示例:

 
console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype) // object
console.log(typeof Number.prototype) // object
console.log(typeof Boolean.prototype) // object
console.log(typeof String.prototype) // object
console.log(typeof Array.prototype) // object
console.log(typeof RegExp.prototype) // object
console.log(typeof Error.prototype) // object
console.log(typeof Date.prototype) // object
console.log(typeof Object.prototype) // object

前文提到Function.prototype是一个空函数,可以通过alert(Function.prototype)进行查看。


既然已知所有构造函数(包括内置和自定义)的__proto__都指向Function.prototype,那么Function.prototype本身的__proto__指向哪里呢?


常听说“JavaScript中函数是一等公民”,这一特性如何体现?如下代码所示:


代码示例:

 
console.log(Function.prototype.__proto__ === Object.prototype) // true

这说明所有的构造函数本质上也是普通的JavaScript对象,因此可以向构造函数添加或删除属性。同时,它继承了Object.prototype上的所有方法,如toString、valueOf、hasOwnProperty等。


最后,Object.prototype的__proto__指向哪里?


代码示例:

 
Object.prototype.__proto__ === null // true

原型链至此终结,指向null。


实例对象与构造函数的原型关联

二、所有对象的__proto__属性均指向其构造函数的prototype


前文测试了所有内置构造函数及自定义构造函数的__proto__属性,接下来考察这些构造函数的实例对象的__proto__指向何处。


首先查看JavaScript引擎内置构造器的实例:


代码示例:

 
var obj = {name: 'jack'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception')
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype) // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype) // true
console.log(err.__proto__ === Error.prototype) // true

接着查看自定义构造器,此处定义了一个Person构造函数:


代码示例:

 
function Person(name) {
this.name = name
} 
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true

p是Person的实例对象,其内部原型__proto__始终指向其构造函数Person的prototype属性。


每个对象都拥有一个constructor属性,用于获取其构造函数,因此以下打印结果恒等:


代码示例:

 
function Person(name) {
this.name = name
} 
var p = new Person('jack')
console.log(p.__proto__ === p.constructor.prototype) // true

上述Person构造函数未向其原型添加属性或方法,此处为其原型添加一个getName方法:


代码示例:

 
function Person(name) {
this.name = name
} 
// 修改原型对象
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true

由此可见,p.__proto__、Person.prototype以及p.constructor.prototype三者恒等,均指向同一个对象。


如果采用另一种方式设置原型,结果会有所不同:


代码示例:

 
function Person(name) {
this.name = name
} 
// 重写原型对象
Person.prototype = {
getName: function() {}
} 
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false

此处直接重写了Person.prototype(注意:上一个示例仅为修改原型对象属性)。输出结果表明,p.__proto__仍然指向Person.prototype,而非p.constructor.prototype。


这很容易理解,因为给Person.prototype赋值的是一个对象字面量{getName: function(){}}。使用对象字面量定义的对象,其constructor属性指向根构造函数Object。Object.prototype是一个空对象{},显然{}与{getName: function(){}}不相等。验证如下:


代码示例:

 
var p = {}
console.log(Object.prototype) // 为一个空的对象{}
console.log(p.constructor === Object) // 对象字面量定义的对象,其constructor为Object
console.log(p.constructor.prototype === Object.prototype) // 为true,无需解释

上述代码中使用的__proto__属性在IE6/7/8/9浏览器中均不支持。在IE9及更高版本中,可以使用ES5标准的Object.getPrototypeOf()方法获取对象的内部原型。


代码示例:

 
var p = {}
var __proto__ = Object.getPrototypeOf(p)
console.log(__proto__ === Object.prototype) // true
作者最新文章