理解JS原型对象与原型链(重要清晰)

2019-08-22 16:38:15

参考文章 理解JS原型对象与原型链

参考文章2 类的继承

目录:


什么是原型对象和原型链

为什么使用原型对象

原型对象的理解

“prototype"和”__proto__"区别

查找属性

原型对象操作

一、什么是原型对象和原型链

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象对应拥有一个原型,对象以其原型为模板、从原型继承方法和属性。而同时原型也是对象,它也拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。


注:因为原型同时也是对象,所以也可以称呼为原型对象。


二、为什么使用原型对象

1、Javascript 并没有类继承模型,而是使用原型对象进行原型式继承。


2、原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已。并且所有的实例是共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份。


三、原型对象的理解

1、对象__proto__属性的值就是它所对应的原型对象,即“对象.__proto__ === 对象对应的原型对象”。


2、在Javascript中,每个函数都有一个属性为prototype指向函数自身的原型。即“函数.prototype === 自身对应的原型对象”。


案例分析:


code1


function Person(){ }  //构造函数创建对象

Person.prototype.name = "Jie"; //添加属性值到原型对象上

console.log(Person.prototype);

1

2

3


原型对象内包含我们之间添加到原型对象上的name属性,还包含一个"constructor"属性,这个属性对应创建所有指向该原型的实例的构造函数。


code2



function Person(){ }  //构造函数创建对象

Person.prototype.name = "Jie"; //添加属性值到原型对象上


var person = new Person(); //创建实例对象

person.age = 23;  //创建属性值到实例对象上

console.log(person);

1

2

3

4

5

6

7



通过上面的两段代码分析,我们可以得出实例对象person的__proto__属性就是Person的prototype属性。person.__proto__与Person.prototype均指向了原型对象。


并且通过上面的结果可以看出,实例对象person的原型(person.__proto__)的原型(person.__proto__.__proto__)是Object对象的原型。


可以将上面的分析进行图解,如下所示:



prototype: 在函数身上,指向原型对象

__proto__: 在对象身上(包括函数创建的对象, 函数本身和原型对象),指向自身的原型

constructor: 在原型对象上,指向构造函数, 在多级继承的时候,指明构造函数方便在对象上扩展原型属性

Object.__protp__为null: 原型的顶端

在Javascript中,每个函数都有一个原型属性prototype指向函数自身的原型,而由这个函数创建的对象也有一个__proto__属性指向这个原型。(即person.proto === Person.prototype; //true)


而函数的原型是一个对象,所以这个对象也会有一个__proto__指向自己的原型,这样逐层深入直到Object对象的原型(null),这样就形成了原型链。


函数的原型对象的constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针指向上一层的原型对象,而上一层的原型对象的结构依然类似,这样利用__proto__一直指向Object的原型对象上,而Object的原型对象用Object.prototype.__proto__ = null表示原型链的最顶端,如此变形成了javascript的原型链继承,同时也解释了为什么所有的javascript对象都具有Object的基本方法。


四、“prototype"和”__proto__"区别

对于所有的对象,都有__proto__属性,这个属性对应该对象的原型。

对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是被设置为所有实例的__proto__属性值)


五、查找属性

当访问一个对象的属性时,Javascript 会从对象本身开始往上遍历整个原型链,直到找到对应属性为止。如果此时到达了原型链的顶部,也就是 Object.prototype,仍然未发现需要查找的属性,那么 Javascript 就会返回 undefined 值。


例如:当你访问 person 的一个属性, 浏览器首先查找 person 是否有这个属性. 如果 person 没有这个属性, 然后浏览器就会在 person 的 __proto__ 中查找这个属性(也就是 Person.prototype). 如果 person 的 __proto__ 有这个属性, 那么 person 的 __proto__ 上的这个属性就会被使用. 否则, 如果 person 的 __proto__ 没有这个属性, 浏览器就会去查找 person 的 __proto__ 的 __proto__ ,看它是否有这个属性. 默认情况下, 所有函数的原型属性的 __proto__ 就是 Object.prototype. 所以 person 的 __proto__ 的 __proto__ (也就是 Person.prototype 的 __proto__ (也就是 Object.prototype)) 会被查找是否有这个属性. 如果没有在它里面找到这个属性, 然后就会在 person 的 __proto__ 的 __proto__ 的 __proto__ 里面查找. 然而这有一个问题: person 的 __proto__ 的 __proto__ 的 __proto__ 不存在. 最后, 原型链上面的所有的 __proto__ 都被找完了, 浏览器所有已经声明了的 __proto__ 上都不存在这个属性,然后就得出结论,这个属性是 undefined.




六、原型对象操作

观察以下代码:


function Person(){

    this.name = "Jie";

}

var person = new Person();


Person.prototype.say = function(){

    console.log("success");

}

1

2

3

4

5

6

7

8

我们的代码中定义了构造器,然后用这个构造器创建了一个对象实例,此后向构造器的 prototype 添加了一个新的方法。

say() 方法仍然可用于 person 对象实例——旧有对象实例的可用功能被自动更新了。这证明了先前描述的原型链模型。这种继承模型下,上游对象(父类)的方法不会复制到下游的对象(子类)实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。


因此,我们可以将一些通用的,公用的属性或方法定义在prototype中。


例如:

1、"getInfo"方法是构造函数Person的一个成员,当通过Person构造两个实例的时候,每个实例都会包含一个"getInfo"方法。



2、原型就是为了方便实现属性的继承,所以可以将"getInfo"方法当作Person原型(Person.__proto__)的一个属性,这样所有的实例都可以通过原型继承的方式来使用"getInfo"这个方法了。并且改变getInfo方法全部的实例都会同步。


function Person(name, age){

    this.name = name;

    this.age = age;

}


Person.prototype.getInfo = function(){

    console.log(this.name + " is " + this.age + " years old");

};

1

2

3

4

5

6

7

8



参考资料:

MDN对象原型

原型对象

了解JS原型对象

__proto__和prototype

 ———————————————— 

  • 2018-11-26 17:03:59

    有些 where 条件会导致索引无效

    在查询中,WHERE 条件也是一个比较重要的因素,尽量少并且是合理的 where 条件是徆重要的,尽量在多个条件的时候,把会提取尽量少数据量的条件放在前 面,减少后一个 where 条件的查询时间。

  • 2018-11-26 17:05:47

    sql查询调优之where条件排序字段以及limit使用索引的奥秘

    看起来匪夷所思,其实搞清楚mysql查询的原理之后,其实很简单 我们来看这2条sql查询,都用到了where order by limit 当有limit存在时,查询的顺序就有可能发生变化,这时并不是从数据库中先通过where过滤再排序再limit 因为如果这样的话,从500万数据中通过where过滤就不会是5s了。

  • 2018-11-26 21:46:28

    TextView设置行间距、行高,以及字间距

    Android系统中TextView有默认行间距,但是比较窄有的时候需要我们设置每行行间距。 TextView为我们提供了相关设置属性android:lineSpacingExtra或android:lineSpacingMultiplier。

  • 2018-11-26 21:47:55

    Drawable一个有趣的属性:tileMode

    tileMode是drawable 资源文件 bitmap的一个属性, 翻译的意思是平铺模式。用法如下: 在drawable目录下新建一个资源文件 tile_mode_demo.xml

  • 2018-11-28 09:55:53

    android radiogroup样式(设置切换背景与文字颜色)

    RadioButton(单选按钮)在Android开发中应用的非常广泛,比如一些选择项的时候,会用到单选按钮。它是一种单选框双状态的按钮,可以选择或不选择。在RadioButton没有被选中时,用户能够按下或点击来选中它。

  • 2018-12-01 00:27:12

    批量kill mysql processlist进程

    如果大批量的操作能够通过一系列的select语句产生,那么理论上就能对这些结果批量处理。 但是mysql并没用提供eval这样的对结果集进行分析操作的功能。所以只能现将select结果保存到临时文件中,然后再执行临时文件中的指令。

  • 2018-12-02 10:54:14

    HTTP长连接、短连接究竟是什么?

    HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。 IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠地传递数据包,使得网络上接收端收到发送端所发出的所有包,并且顺序与发送顺序一致。TCP协议是可靠的、面向连接的。