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

avatar
作者
猴君
阅读量:0

前言

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

 🍅 个人主页:南木元元

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


目录

构造函数

什么是原型

原型对象和构造函数关系

原型链

显式与隐式原型

__proto__与[[Prototype]]

原型链的概念

Object.getPrototypeOf()方法

hasOwnProperty() 方法

结语


构造函数

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

1.对象字面量

// 对象字面量 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实例来说都一样,放在构造函数里,那每创建一个实例,就会重复创建一次相同的属性和方法,显得有些浪费。这时候,如果把这些公有的属性和方法放在原型对象里共享,就会好很多。

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()方法

__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; }  

结语

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

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!