JavaScript原型与原型链以及实例

来源:互联网 发布:网络诈骗多少钱才算 编辑:程序博客网 时间:2024/06/06 01:27

谈到JavaScript的原型链,需要思考的几个问题:

1,什么是原型链?

2,原型链有什么作用?

在JavaScript中不像其他语言有类的概念,提到JavaScript,想到的都是函数,函数无非两个用途:
1,像一般函数一样去调用它。
2,作为函数原型的构造函数去new它。

JavaScript原型

什么是JavaScript原型?
JavaScript每声明一个function,都附带着prototype原型,prototype原型是函数的一个默认属性,在函数的创建过程中由JavaScript编译器自动添加。
简言之:每当创建一个function对象的时候,就有一个原型prototype。

栗子:

这里写图片描述

那么,创建一个函数这个过程到底发生了些什么呢?

再看一个栗子:

function a(){    this.name="a";}

过程:

1、它会创建一个函数对象 也就是a 本身(一个构造函数)。
2、它会创建一个原型对象A(用A来表示)。
3、函数对象会有一个prototype指针,它指向了对应的原型对象,这里就指向了A。
4、原型对象A中有一个construtor指针,指向它的构造函数,这里就指向了函数a。

这个构造函数a有什么作用呢?或者说怎么去使用它呢?
答案就是 —– new。

比如:

function a(){      this.name = 'a';  }  var a1 = new a();  

这里,a1就是调用原型对象(通过prototype指针)里面构造函数(constructor)创建一个新的对象实例。

如果去修改原型对象中的属性,那么以它为“蒙版”实例化出来的a1又会怎么样呢?

看一个栗子:

function a(){      this.name = 'a';  }  var a1 = new a();  a.prototype.age = 18;console.log(a1.age);       // 18

为什么a1对象里明明没有定义age属性,确得到了18这个值呢?
原因是新实例化的对象a1内部有一个看不见的 _proto_ 指针,指向原型对象A。

这里写图片描述

在访问属性的时候,会先在a1对象内部中寻找,如果没有,就会顺着 _proto_ 指向的对象里面去寻找,这里会到原型对象A中寻找,找到就返回值,没有找到就返回undefined,反正就是逐级向上查找。到这里,就可以看到 原型链 的影子了。

附上另外说明:

1、一个没有继承操作的函数的 _proto_ 都会指向Object.prototype,而Object.prototype都会指向null。

2、所以,也可以很明显知道,为何null是原型链的终端。

再来看个栗子:

function a(){    this.name="a";}a.prototype.age = 18;function b(){    this.apple="5s";}b.prototype = new a();    // b 继承 ab.prototype.foot = "fish";function c(){    this.computer = "MAC";}c.prototype = new b();   // c 继承 b , b 继承 a  var d = new c(); console.log(d.apple);    //  5sconsole.log(d.name);     //  aconsole.log(d.foot);     //  fishconsole.log(d.age);      //  18

从这个栗子就能很形象的看出来“一层层往上找”的思想了吧!c继承b , b继承a ,无形之中就连成了一条“线”,这条线就是“原型链”。


代码延伸一下:

function b(){    this.apple="5s";}b.prototype.foot = "fish";function c(){    this.computer = "MAC";}c.prototype.c1=function(){    console.log("c1存在");}c.prototype = new b();   // c 继承 bc.prototype.c2=function(){    console.log("c2存在");}var d = new c(); console.log(d.apple);          //  5sconsole.log(d.foot);           //  fishconsole.log(d.computer);       //  MACd.c2();                        //  "c2存在"d.c1();             //  Uncaught TypeError: d.c1 is not a function

这里写图片描述

可以看到,构造函数c的实例d调用 c1() 报错了,很明显 c1() 已经不存在于原型对象C中了。这是为什么呢?
原因是 “c.prototype = new b(); // c 继承 b” ,这使得原型对象C的指向发生了变化,原来装有 c1() 的原型对象被抛弃了。

继续升级,探究一下c继承b之后还发生了哪些变化:

栗子:

function b(){    this.apple="5s";}b.prototype.foot = "fish";function c(){    this.computer = "MAC";}c.prototype.c1=function(){    console.log("c1存在");}c.prototype = new b();   // c 继承 bc.prototype.c2=function(){    console.log("c2存在");}console.log(c.prototype.constructor);     //  新增代码 c.prototype.constructor = c;              //  新增代码 console.log(c.prototype.constructor);     //  新增代码 var d = new c(); console.log(d.apple);          //  5sconsole.log(d.foot);           //  fishconsole.log(d.computer);       //  MACd.c2();                        //  "c2存在"d.c1();             //  Uncaught TypeError: d.c1 is not a function

这里写图片描述

可以看出,c继承了原型对象B的实例化之后,原型对象C 的构造函数指针指向了 构造函数b 。
这是不是说 var d = new c(); 在构造c的实例化d 的时候指向的是 构造函数b呢?答案是否定的。因为d.computer打印出来的值依然是”MAC”,所以仍然是调用的构造函数b进行的实例化。虽然不影响d的实例化,但是影响到了d.constructor的指向,d.constructor也指向了构造函数b。
所以,建议在c继承b之后,对原型对象C 的构造函数指针进行修正操作:c.prototype.constructor = c;,修正之后的指向:
这里写图片描述








描述不对的地方,多包涵。

原创粉丝点击