新手理解的JS原型链

樊岐
• 阅读 1004

一直以来对于JavaScript 的原型链的概念,始终有些东西有一种模糊感,最近刚好有时间就塌下心认真的把《JavaScript高级程序设计》中相关内容认真读了一遍,也查看了很多网上很多资料,以前很多不明白的地方也渐渐明白了起来。
写一篇文章记录一下最近学习的感悟。

字面量创建对象

我们通常创建一个对象无非就两种方式:

 1. var obj= new Object();//new 一个Object的实例
 2. var obj= {};//对象字面量

使用对象字面量 和使用new的方式是一样的。
为了简便,一般推荐使用使用字面量:var o= {};

构造函数创建对象

当我们想要创建自定义的对象时,需要用到构造函数。
构造函数和普通函数有两个区别:

1. 便于和普通函数区分,函数名首字母大写。    
2. 使用 `new` 操作符调用,返回一个实例对象。    

除此之外和普通函数一摸一样。
我们使用构造函数Person来创建两个实例对象:

function Person(name){
    this.name = name;
    this.sayName= function (){ alert(this.name) }
 }
var person1 = new Person('小明');
var person2 = new Person('小红');
console.log(person1);//{name: "小明", sayName: fun}
console.log(person2);//{name: "小红", sayName: fun}

上面的例子不难理解,虽然这两个实例对象都有sayName方法,而且他们两个的作用也是一样的,但却是两个方法,只是名字和作用一样。

画个图表示一下:
新手理解的JS原型链

如果还不明白,我在打个比喻:
就像A街上有一间麦当劳,在B街上也开了一间麦当劳,它们都叫麦当劳,作用也是一样的。但是你总不能说他们是一间麦当劳吧?

person1.sayName === person2.sayName;//false

如果这样的话,我们每构造出来一个对象,都要单独为这个对象创建出一个专属于它自己使用的sayName,这是很占用内存的。

那我们能不能让所有的实例对象都共同使用一个sayName方法,来节省内存,提升效率呢?这需要我们先理解原型对象的概念。

原型对象

我们先了解原型对象的概念。

每个对象都有原型对象(null除外),我们用__proto__表示,每个函数都有prototype属性,指向实例的原型对象。

对照这句话,按照我们上面的例子,也就是说Person.prototype指向person1的原型对象(__proto__),

Person.prototype === person1.__proto__; // true

为了便于理解,来看一张图。

新手理解的JS原型链

恩~他们的关系大概就是这样。

原型链

原型链简单用一句话概括就是:

原型链就是 对象的__proto__所连接的链状结构

为了方便我们理解原型链,举一个简单的例子:

function F(){
    this.a = 1;
    this.b = 2;
}
F.prototype.b = 3;
F.prototype.c = 4;
var o = new F();// {a: 1, b: 2}
//原型链:
//o --> o.__proto__ --> o.__proto__.__proto__ --> null
// 其中的 --> 就表示 __proto__ 也就是原型链
console.log(o.a); // 1
// o上有a这个属性吗?有的,该属性的值为1

console.log(o.b); // 2
// o上有b这个属性吗?有的,该属性的值为2
// 原型上也有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 "

console.log(o.c); // 4
// o上有c这个属性吗?没有,那看看原型上有没有
// o.__proto__上有c这个属性吗?有的,该属性的值为4

console.log(o.d); // undefined
// o上有d这个属性吗?没有,那看看原型上有没有
// o.__proto__ 上有d这个属性吗?没有,那看看它的原型上有没有
// o.__proto__.__proto__ 为 null,停止搜索
// 没有找到d属性,返回undefined。

我们画张图来表示:

新手理解的JS原型链

图中这条红色的线就是原型链。
由此可见,实例对象可访问自己原型对象上的属性和方法,额..准确来说是:

  1. 当一个对象 查找属性或方法时,自己有,停止查找,返回结果。
  2. 自己没有,顺着__proto__一直向上查找,如找到,停止查找,返回结果。
  3. 如果一直找到了原型链的最顶端(null),还没有找到,返回undefined

我们先回顾一下那个sayName的问题:
怎么让所有的实例对象都是用一个sayName方法呢
现在我们可以使用原型对象来解决这个问题了。
我们把sayName方法放到实例的原型对象上面,也就是Person.prototype上面来供所有实例使用:

function Person(name){
    this.name = name;
 }
Person.prototype.sayName=function (){
    alert(this.name);
}
var person1 = new Person('小明');
var person2 = new Person('小红');
person1.sayName === person2.sayName;//true

用图表示:
新手理解的JS原型链

补充

constructor
说一下我的经历,一开始理解原型链时,一直在prototype__proto__constructor在这个三个属性中绕来绕去。

为了便于理解,我把constructor放在最后了。
constructor字面意思就很容易理解,构造函数的意思。
一句话解释:

每个原型对象都有一个 constructor 属性指向 关联的构造函数。

还是上面那个例子:

console.log(Person.prototype.constructor);//Person(){ fun }

需要注意的一点是,实例对象上没有constructor属性。
但是:

console.log(person1.constructor) ;//Person(){ fun }

得出这个结果很简单:
实例上查找不到constructor属性 --> 顺着__proto__在原型对象上找 --> 找到并返回。

Object.prototype
刚才我们说了创建对象的两种方式:字面量创建对象和使用new操作符创建对象。
这两种方式创建出来的对象都会继承Object.prototyoe上的方法。
比如,我们使用字面量新创建一个对象o:

var o = {value: 1};
o.toString();//"[object Object]"
//查找过程: o --> o.__proto__ 找到返回
o.__proto__ === Object.prototype;//true

o这个的对象本身并没有toString这个方法,但它却可以使用toString方法。
因为它继承了Object.prototyoe上的toString的方法。

null
既然对象都会继承自Object.prototype上面的方法,那它自己的原型又是什么呢。答案是null

Object.prototype.__prototype__ === null;//true

以上仅自己学习所得,如有不当之处 望指出。

点赞
收藏
评论区
推荐文章
晴空闲云 晴空闲云
4年前
JavaScript中isPrototypeOf函数详解
有时看一些框架源码的时候,会碰到isPrototypeOf()这个函数,那么这个函数有什么作用呢?isPrototypeOf()isPrototypeOf()是Object函数(类)的下的一个方法,用于判断当前对象是否为另外一个对象的原型,如果是就返回true,否则就返回false。这个函数理解的关键是在原型链上,这个据说是JavaScript
Souleigh ✨ Souleigh ✨
5年前
ES2020 骚操作:可选链 "?."
可选链"?."可选链 ?. 是一种访问嵌套对象属性的安全的方式。即使中间的属性不存在,也不会出现错误。“不存在的属性”的问题如果你才刚开始读此教程并学习JavaScript,那可能还没接触到这个问题,但它却相当常见。举个例子,假设我们有很多个 user 对象,其中存储了我们的用户数据。我们大多
Stella981 Stella981
4年前
JavaScript学习总结(十七)——Javascript原型链的原理
一、JavaScript原型链ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。在JavaScript中,用__proto__属性来表示一个对象的原型链。当查找一个对象的属性时,JavaScript会向上遍历原型
Stella981 Stella981
4年前
JavaScript的入门简介
什么是JavaScriptJavaScript,我们一般简称为JS,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。JavaScript现在已经被用到了很多非浏览器环境中,JavaScript基于原型编程、多范式的动态脚本语言,并支持面向对象、命令式和声明式风格。HTML、CSS、JavaScript三者不同的功能:
Stella981 Stella981
4年前
ES6中的super
对象方法中的super原型对于javascript来说非常重要,甚至可以说是javascript中最为重要的知识点。然而,很多人一看到原型就懵逼。ES5我们可以使用Object.getPrototypeOf()来返回原型对象,使用Object.setPrototypeOf()来设置原型对象。看下面的例子:letpe
Wesley13 Wesley13
4年前
JS原型、原型链深入理解
原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对
Stella981 Stella981
4年前
JavaScript 原型和原型链的定义和使用
目录JavaScript原型和原型链的定义和使用01原型原型定义原型实例理解书写方法1:属性单个定义书写方法2:属性多个定义02原型链原型链定义原型链实例理解最终原型问题03原型和原型链的使用应用举例Ja
Stella981 Stella981
4年前
JavaScript 基于原型链的继承
JavaScript对象是动态的属性“包”(指其自己的属性)。JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。遵循ECMAScript标准,someObject.Prototype
Stella981 Stella981
4年前
JavaScript面向对象编程的15种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”。在JavaScript中并没有类这种概念,面向对象编程不是基于类,而是基于原型去面向对象编程,JS中的函数属于一等对象,而基于JS中闭包与弱类型等特性,在实现一些设计模式的方式上与众不同。ps:本文之讲述面向对象编程的设计模式策略,JavaScript原型的基础请参考阮一峰面向
Stella981 Stella981
4年前
Javascript中,实现类与继承的方法和优缺点分析
Javascript是一种弱类型语言,不存在类的概念,但在js中可以模仿类似于JAVA中的类,实现类与继承第一种方法:利用Javascript中的原型链1//首先定义一个父类23functionAnimal(name,age){4//定义父类的属性5thi
Stella981 Stella981
4年前
JavaScript基础之原型对象和原型链
!(https://oscimg.oschina.net/oscnet/64fbb850bccf434ebbf033f599907fb3.png)原型对象!(https://oscimg.oschina.net/oscnet/9994724f3f4d47db8b0e07b93c3250e7.jpg)原型对象简单来说就是