引言
在之前对 JavaScript 原型的探讨中,我们初步了解了原型的基本概念、构造函数与原型对象的关系等核心内容。本文将在此基础上进行更深入的补充,进一步剖析原型的细节、原型链的深入理解以及实际应用中的一些要点,帮助你更全面、透彻地掌握这一关键特性。
一、回顾原型基础概念
-
获取 “小米 su7” 的类比理解:为了形象地理解原型相关概念,我们用获取 “小米 su7” 来类比。通过构造函数
Car作为蓝图,使用new关键字创建Car的实例,就如同生产出实际的小米 su7 汽车。而prototype对象上的方法和属性,就像是汽车的标准配置,所有同类型的汽车(实例)都能共享这些配置。 -
构造函数与原型对象的关键特性
- 构造函数(
constructor) :当以new方式调用构造函数时,它内部的this指向新创建的对象,这是构造新对象的过程。每个实例都拥有基于构造函数定义的自身属性。例如在Person构造函数function Person(name, age) { this.name = name; this.age = age; }中,name和age就是每个Person实例独有的属性。 - 原型对象(
prototype) :它上面设置的属性和方法能被类的所有实例共享。这体现了 JavaScript 基于原型的面向对象编程特点,与传统基于类的面向对象编程不同,它并非严格的血缘关系。每个函数都有prototype属性,指向类的原型对象,其值是一个对象,内部的属性和方法供所有实例使用。同时,每个实例都有__proto__属性,指向类的原型对象,借此实例可通过原型链访问原型对象上的属性和方法。另外,原型对象中有一个constructor属性,指向构造函数,意味着由该构造函数创建的实例能享用原型对象的内容。
- 构造函数(
二、原型相关细节深入剖析
- 原型链的本质与延伸:任何对象默认都指向
Object.prototype,除非是通过new其他构造函数生成的。这意味着所有对象在原型链上最终都能追溯到Object.prototype,它是所有对象的原型对象,而Object.prototype的原型是null,null表示没有原型对象。这就形成了一条原型链,例如:
javascript
function Person() {}
Person.prototype.species = '人类';
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
在这个例子中,person 的 __proto__ 指向 Person.prototype,Person.prototype 的 __proto__ 又指向 Object.prototype,而 Object.prototype 的 __proto__ 为 null,清晰展示了原型链的结构。2. 实例与原型对象属性的查找机制:当访问实例的某个属性时,JavaScript 首先会在实例自身查找该属性。如果找不到,就会沿着原型链向上查找,直到找到该属性或到达原型链顶端(Object.prototype 的原型为 null)。例如:
javascript
function Person() {}
Person.prototype.species = '人类';
var zzp = new Person('郑志鹏', 18);
zzp.species ='sb';
console.log(zzp.species); // sb
delete zzp.species;
console.log(zzp.species); // 人类
这里先在实例 zzp 上设置了 species 属性,此时访问 zzp.species 会返回实例上的值 sb。当删除实例上的 species 属性后,再次访问就会从原型对象上找到 species 属性的值 人类。
三、原型在实际代码中的应用与解析
Person类的完整示例:以下代码展示了一个完整的Person类的构建与使用,包括属性定义、方法添加以及原型链相关的操作。
javascript
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
speci: '人类',
sayHi: function () {
console.log(`你好我是${this.name}`);
}
}
const zzp = new Person('郑志鹏', 18);
console.log(zzp.__proto__ == Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Object.getPrototypeOf(zzp) == Person.prototype); // true
zzp.sayHi(); // 你好我是郑志鹏
在这个例子中,首先定义了 Person 构造函数,用于初始化实例的 name 和 age 属性。然后通过修改 Person.prototype 为原型对象添加了 speci 属性和 sayHi 方法。创建 zzp 实例后,验证了实例的 __proto__ 与原型对象的关系,以及通过 Object.getPrototypeOf 方法获取实例的原型对象,并成功调用了原型对象上的 sayHi 方法。2. 对象创建与原型关系:在创建对象时,无论是直接字面量创建还是通过 new Object() 创建,都与原型有着紧密联系。例如:
javascript
var obj = {
name: '舒俊',
age: 19,
};
var obj2 = new Object();
obj2.name = '舒俊';
obj2.age = 19;
console.log(obj);
console.log(obj2);
console.log(obj.__proto__.toString());
console.log(obj2.__proto__.toString());
这里通过两种方式创建了对象 obj 和 obj2,它们本质上都继承自 Object.prototype,通过 __proto__ 可以访问到 Object.prototype 的方法,如 toString 方法。3. 原型链继承示例:通过以下代码可以看到如何利用原型链实现继承。
javascript
function Animal() {}
Animal.prototype.species = '动物';
function Person() {}
Person.prototype = new Animal();
var su = new Person();
console.log(su.species, su.__proto__, su.toString());
在这个例子中,Person 构造函数的原型对象被设置为 Animal 的一个实例,这样 Person 的实例 su 就可以通过原型链访问到 Animal.prototype 上的 species 属性,实现了简单的继承关系。
四、总结
通过对 JavaScript 原型的进一步补充学习,我们对原型的理解从基础概念深入到了实际应用中的各种细节。原型链的结构、实例与原型对象属性的查找机制以及原型在继承中的应用等内容,都是掌握 JavaScript 面向对象编程的关键。希望这些深入的知识能帮助你在开发中更灵活、准确地运用原型相关特性,编写出更高效、优雅的代