社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
谈到JavaScript的原型链,需要思考的几个问题:
1,什么是原型链?
2,原型链有什么作用?
在JavaScript中不像其他语言有类的概念,提到JavaScript,想到的都是函数,函数无非两个用途:
1,像一般函数一样去调用它。
2,作为函数原型的构造函数去new它。
什么是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;,修正之后的指向:
描述不对的地方,多包涵。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!