JavaScript原型与原型链以及实例 - Go语言中文社区

JavaScript原型与原型链以及实例


谈到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 继承 a
b.prototype.foot = "fish";

function c(){
    this.computer = "MAC";
}

c.prototype = new b();   // c 继承 b , b 继承 a  

var d = new c(); 

console.log(d.apple);    //  5s
console.log(d.name);     //  a
console.log(d.foot);     //  fish
console.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 继承 b

c.prototype.c2=function(){
    console.log("c2存在");
}

var d = new c(); 

console.log(d.apple);          //  5s
console.log(d.foot);           //  fish
console.log(d.computer);       //  MAC
d.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 继承 b

c.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);          //  5s
console.log(d.foot);           //  fish
console.log(d.computer);       //  MAC
d.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;,修正之后的指向:
这里写图片描述








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

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sinat_27801935/article/details/72587901
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 22:08:34
  • 阅读 ( 922 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢