深入理解JavaScript原型与原型链:从基础概念到核心方法

作者:木棉

时间:2026-10-29

浏览:209

本文系统梳理JavaScript面向对象编程中的原型与原型链机制。详细解析构造函数、原型对象及实例间的指针关系,阐述原型链查询、属性屏蔽与动态性原理。同时介绍ES5新增的Object.getPrototypeOf、hasOwnProperty、in操作符及Object.keys等核心方法,帮助开发者深入理解JS继承模型与属性检测技巧。

文章目录

JavaScript原型与原型链基础

首先通过一张示意图来梳理整体脉络。

JavaScript原型与原型链基础

核心概念:原型链与原型对象

一、核心概念解析 【原型链】每个构造函数都拥有一个对应的对象,即原型对象。该原型对象内部包含一个指向构造函数的指针。同时,每个实例对象内部也包含一个指向其原型对象的内部指针。若将原型对象设置为另一个原型的实例,则新原型对象会包含指向另一个原型的指针,而另一个原型中则包含指向另一个构造函数的指针。若这种层级关系继续延伸,便形成了实例与原型之间层层递进的链条,即原型链。 【原型对象】该对象包含可由特定类型所有实例共享的属性和方法。所有引用类型默认继承自Object,这一继承过程同样通过原型链实现。所有函数的默认原型均为Object的实例,因此默认原型包含一个内部指针指向Object.prototype。这也是所有自定义类型能够继承toString()和valueOf()方法的原因。 【构造函数】构造函数与普通函数的主要区别在于调用方式。通常,通过new操作符调用的函数可作为构造函数;若不通过new调用,其与普通函数无异。 [注意]用户自定义函数与JavaScript内置构造函数均可作为构造函数使用。 【构造函数的命名规范】构造函数名称首字母应大写,而非构造函数首字母小写。此规范借鉴自其他面向对象语言,旨在与ECMAScript中的其他函数区分。构造函数本质仍是函数,仅用于创建对象。 【构造函数的三种调用场景】 [a]作为构造函数使用


var person = new Person("Nicholas",29,"software Engineer");
person.sayName();

[b]作为普通函数直接调用


Person("greg",27,"doctor");//添加到window
window.sayName();//"Greg"

[c]在另一个对象的作用域中调用


var o = new Object();
Person.call(o,"Kristen",25,"Nurse");
o.sayName();//"Kristen"

构造函数与原型属性详解

【prototype属性】每当创建新函数时,系统会按特定规则为该函数创建prototype属性,该属性指向函数的原型对象。 [注意]仅函数拥有prototype属性,Object对象没有此属性。 【constructor属性】默认情况下,所有原型对象自动获得一个constructor(构造函数)属性,该属性包含一个指向prototype属性所在函数的指针。 [注意]自定义构造函数创建后,其原型对象默认仅拥有constructor属性,其他方法均从Object继承而来。 【_proto_与[[prototype]]】调用构造函数创建新实例后,实例内部包含一个指向构造函数原型对象的指针(内部属性)。ECMA-262第5版将此指针称为[[prototype]]。虽然标准访问方式并非直接脚本访问,但Firefox、Safari和Chrome等浏览器在每个对象上支持_proto_属性,而其他实现中该属性对脚本不可见。此连接存在于实例与构造函数的原型对象之间,而非实例与构造函数之间。 二、基本操作机制 【原型链属性查询】读取对象属性时,系统执行一次搜索,目标为指定名称的属性。搜索首先从实例本身开始,若找到同名属性则返回值;若未找到,则沿指针指向原型对象继续搜索,若在原型中找到则返回该属性值。 【添加实例属性】为对象实例添加属性时,该属性会屏蔽原型对象中保存的同名属性。添加属性仅阻止访问原型中的同名属性,不会修改原型属性,即使将实例属性设为null,也仅在实例中设置该属性,不恢复指向原型的连接。使用delete操作符可完全删除实例属性,从而重新访问原型中的属性。 【原型的动态性】由于原型查找是一次搜索过程,对原型对象所做的任何修改都会立即反映在实例上,即使先创建实例后修改原型也是如此。 [注意]不建议在产品化程序中修改原生对象的原型。


function Person(){};
var friend = new Person();
Person.prototype.sayHi = function(){
  alert('hi');
}
friend.sayHi();//"hi"  

原型操作:查询、屏蔽与动态性

【重写原型】调用构造函数时,实例会添加一个指向最初原型的[[prototype]]指针。若将原型修改为另一个对象,则切断了构造函数与最初原型的联系。此时,实例中的指针仅指向原型,而不指向构造函数。 三、常用方法详解 [1]isPrototypeOf():用于判断实例对象和原型对象是否存在于同一原型链中。若原型出现在原型链中,则该原型可视为该原型链所派生实例的原型。


function Person(){};
var person1 = new Person();
var person2 = new Object();
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Object.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//false
console.log(Object.prototype.isPrototypeOf(person2));//true

[2]ES5新增方法Object.getPrototypeOf():返回[[Prototype]]的值。


function Person(){};
var person1 = new Person();
var person2 = new Object();
console.log(Object.getPrototypeOf(person1)); //Person{}
console.log(Object.getPrototypeOf(person1) === Person.prototype); //true
console.log(Object.getPrototypeOf(person1) === Object.prototype); //false
console.log(Object.getPrototypeOf(person2)); //Object{}  

[3]hasOwnProperty():检测属性是否存在于实例中。


function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
//不存在实例中,但存在原型中
console.log(person1.hasOwnProperty("name"));//false
//不存在实例中,也不存在原型中
console.log(person1.hasOwnProperty("no"));//false
person1.name = 'Greg';
console.log(person1.name);//'Greg'
console.log(person1.hasOwnProperty('name'));//true
delete person1.name;
console.log(person1.name);//"Nicholas"
console.log(person1.hasOwnProperty('name'));//false  

[4]ES5的Object.getOwnPropertyDescriptor():仅用于获取实例属性的描述符。若要获取原型属性的描述符,需直接在原型对象上调用Object.getOwnPropertyDescription()方法。


function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
person1.name = 'cook';
console.log(Object.getOwnPropertyDescriptor(person1,"name"));//Object {value: "cook", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(Person.prototype,"name"));//Object {value: "Nicholas", writable: true, enumerable: true, configurable: true}

[5]in操作符:当通过对象能访问给定属性时返回true,无论该属性存在于实例还是原型中。


function Person(){}
var person1 = new Person();
person1.name = 'cook';
console.log("name" in person1);//true
console.log("name" in Person.prototype);//false
var person2 = new Person();
Person.prototype.name = 'cook';
console.log("name" in person2);//true
console.log("name" in Person.prototype);//true

[6]结合hasOwnProperty()与in操作符:用于确定属性是否存在于实例中。


//hasOwnProperty()返回false,且in操作符返回true,则函数返回true,判定是原型中的属性
function hasPrototypeProperty(object,name){
  return !object.hasOwnProperty(name) && (name in object);
}
function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
console.log(hasPrototypeProperty(person1,'name'));//true
person1.name = 'cook';
console.log(hasPrototypeProperty(person1,'name'));//false
delete person1.name;
console.log(hasPrototypeProperty(person1,'name'));//true
delete Person.prototype.name;
console.log(hasPrototypeProperty(person1,'name'));//false

[7]ES5的Object.keys()方法:接收对象参数,返回包含所有可枚举属性的字符串数组。 [注意]务必先通过new创建实例对象再使用此方法,否则返回结果为空。


function Person(){
  Person.prototype.name = 'Nicholas';
  Person.prototype.age = 29;
  Person.prototype.job = 'Software Engineer';
  Person.prototype.sayName = function(){
    alert(this.name);
  }  
};
var keys = Object.keys(Person.prototype);
console.log(keys);//[]
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var keys = Object.keys(Person.prototype);
console.log(keys);//["name","age","job","sayName"]
var p1Keys = Object.keys(p1);
console.log(p1Keys);//["name","age"]

[8]ES5的Object.getOwnPropertyNames()方法:接收对象参数,返回包含所有属性的字符串数组。 [注意]务必先通过new创建实例对象再使用此方法,否则仅返回constructor。


function Person(){
  Person.prototype.name = 'Nicholas';
  Person.prototype.age = 29;
  Person.prototype.job = 'Software Engineer';
  Person.prototype.sayName = function(){
    alert(this.name);
  }  
};
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);//["constructor"]
var p1 = new Person();
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);//["constructor", "name", "age", "job", "sayName"]

ES5核心方法实战:原型检测与属性获取

希望本文所述内容能对大家学习JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章