JS 原型与原型链图解:彻底搞懂的终极指南

南木元元 2024-07-25 15:05:02 阅读 93

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元

 在JavaScript中,原型和原型链是非常重要的知识点,只有理解了它们,才能更好地理解js。


目录

构造函数

什么是原型

原型对象和构造函数关系

原型链

显式与隐式原型

__proto__与[[Prototype]]

原型链的概念

Object.getPrototypeOf()方法

hasOwnProperty() 方法

结语


构造函数

创建对象有两种方式:一种是最常见的对象字面量,一种就是通过构造函数来创建。

1.对象字面量

<code>// 对象字面量

let person = {

name: "南木元元",

age: 22,

species: "人类",

greet: function() {

console.log("Hello!");

}

};

2.构造函数

// 构造函数创建对象

function Person(name, age) {

this.name = name;

this.age = age;

this.species = '人类';

this.greet = function() {

console.log("Hello!");

};

}

let person1 = new Person("南木元元", 22);

构造函数和普通函数本质上没什么区别,只不过使用了new关键字创建对象的函数,被叫做了构造函数。构造函数的首字母一般是大写,用以区分普通函数。任何函数只要使用 new 操作符调用就是构造函数,而不使用 new 操作符调用的函数就是普通函数。

// 作为构造函数

let person1 = new Person("南木元元", 22);

person1.greet() //Hello!

// 作为函数调用

Person("yuanyuan", 18)

window.greet() //Hello!

什么是原型

 《javascript高级程序设计》中对原型的描述:

每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。

简单来讲,原型就是一个对象,可以实现对象的属性和方法的继承

原型对象和构造函数关系

在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向一个对象,这个对象就是此函数的原型对象。

当函数经过new调用时,这个函数就成为了构造函数,返回一个新的实例对象

这个实例对象有一个__proto__属性,指向构造函数的原型对象。

每个原型对象上都有个constructor属性,指向它的构造函数。

原型对象有什么用呢?最主要的作用就是用来存放实例对象的公有属性和方法。

在上面例子中,species属性和greet方法对于所有people实例来说都一样,放在构造函数里,那每创建一个实例,就会重复创建一次相同的属性和方法,显得有些浪费。这时候,如果把这些公有的属性和方法放在原型对象里共享,就会好很多。

<code>function Person(name, age) {

this.name = name;

this.age = age;

}

// 定义在原型对象

Person.prototype.species = '人类';

Person.prototype.greet = function () {

console.log("Hello");

}

let person1 = new Person('南木元元', 22);

let person2 = new Person('yuanyuan', 18);

console.log(person1.species); // 人类

console.log(person2.species); // 人类

person1.greet(); // Hello

person2.greet(); // Hello

为什么实例对象person1和person2中可以访问构造函数Person的原型对象上的方法呢?看上图可以知道,是通过__proto__ 这个属性去访问构造函数的原型对象。

有时候,我们会用person1.constructor查看实例对象的构造函数:

console.log(person1.constructor); // Person()

这个constructor是原型对象上的属性,但实例对象也可以使用,原因就是上面所说的。那如果原型对象上也没有找到想要的属性呢?这就要说到原型链了。

原型链

讲原型链之前,我们需要先了解几个概念。

显式与隐式原型

显式原型就是利用prototype属性查找原型,prototype属性是函数独有的属性。隐式原型是利用__proto__属性查找原型,这个属性是所有对象都有的属性。

隐式原型__proto__ 的属性值指向它的构造函数的显式原型prototype属性值:

console.log(person1.__proto__ === Person.prototype); // true

console.log(person2.__proto__ === Person.prototype); // true

__proto__与[[Prototype]]

[[Prototype]]用于标识对象的原型。我们来打印一下实例对象person1:

console.log(person1)

你会发现,[[Prototype]]其实就是实例对象person1的原型对象,它的值与person1.__proto__是一样的,通过__proto__可以暴露一个对象内部的原型[[Prototype]]的值。对于使用对象字面量创建的对象,该值是Object.prototype。对于使用数组字面量创建的对象,该值是Array.prototype。对于函数,该值是Function.prototype。

原型链的概念

JavaScript中所有对象都会通过 __proto__ 属性指向自己的原型对象,这个原型对象又会有自己的原型,直到指向Object对象为止,这样就形成了一个链条一样的结构,即原型链。当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型链上找这个属性,直到找到对应属性或到达原型链的末尾。

原型链的终点是什么?

原型链的终点是null。原型链上的所有原型都是对象,所有的对象最终都是由 Object 构造的。可以在控制台通过Object.prototype.__proto__ 来打印原型链的终点。

Object.getPrototypeOf()方法

<code>__proto__ 属性并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但不推荐使用该属性,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,然后再来为原型添加方法/属性。

hasOwnProperty() 方法

使用hasOwnProperty() 方法获得对象非原型链上的属性。

有时候,我们想要判断对象自身中是否具有指定的属性,而不是从原型中继承来的属性,这时可以使用hasOwnProperty() 方法。

function iterate(obj){

var res = [];

for(var key in obj){

if(obj.hasOwnProperty(key)){

res.push(key+': '+obj[key]);

}

}

return res;

}

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。