javascript基础之我见(1)----深度理解原型链
写在开始之前:
早就想要好好总结下javascript的基础知识了,所以从这篇文章起,我会开始总结各种js的语法知识,作为一名以js开发为生计的前端工程师,深深的着迷于js的语言魅力,而js最吸引人最强大的地方,便在于他独特的语法,能深刻的理解js的语法,是作为一个前端工程师的基本素质,在这里,笔者在总结的同时,也希望前端朋友们给予自己的补充和见解。那么就让我们从js最最独特的语法,闭包,原型,词法作用域开始,接下来,笔者也会讨论this,正则,浏览器的能力检测,事件代理等细节问题,以及html5,css3等前沿领域,今天我先试着总结下原型链相关的知识。笔者的经验和知识也许会有很多不足之处,欢迎大家的更正与建议。
1.首先要知道的:
什么是原型链呢?
我们首先来说说继承,继承是面向对象语言的重要机制,通俗地讲就是子类可以拥有父类的方法和属性,js的原型链实际上也是一种继承,在ECMAScript标准中,只支持实现继承,而其实现实现继承就是主要依靠于原型链实现的。
那么,我们再来说说原型,原型其实就是上述所说的继承中的父类。
这样,原型链 显而易见的 可以理解为,利用原型串起一个继承链,让一个引用类型继承另一个引用类型的属性和方法,再以此类推下去。
构造函数,原型,与实例的关系:
这三者的关系用一句话概括为,每个构造函数都有一个原型,当new 一个构造函数的时候就会生成一个原型链上携带该构造函数的原型的实例。
2.原型链的生成过程:
上面基本了解了原型链的相关知识,那么原型链的生成过程是什么呢?
1 function parent(){
this.parent = "world"; 2 } 3 parent.prototype = { 4 a : function(){ 5 }, 6 b : "hello" 7 } 8 var child = new parent()
而child就是parent的一个实例,在执行构造函数的时候,js引擎创建一个空白对象,把__proto__指向parent的prototype,然后把这个对象作为this调用parent进行初始化,这样一个简单的原型链就形成了 即 child --> parent.prototye -->Object.prototype。
prototype,constructor,__proto__ :
上面多次提到这几个属性,在原型链的概念中,如果能理解这几个属性的关系,那么离胜利就不远了。下面我们逐一的谈谈这几个属性:
1.prototype
prototype是构造函数的属性,指的就是构造函数的原型,在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类,
注意,只有构造函数这个属性才有这种效果哦~如果一个构造函数没有指定该属性,那么该属性下的__proto__会默认的指向原生Object的原型对象,该属性会变成一个对象,其中constructor属性指向本身。
2.constructor
constructor,如果通俗的理解,可以理解成原型对象的构造函数,当把一个对象(对象字面量)赋给一个构造函数的原型,constructor会被复写,如果没有进行prototype的操作的话,constructor是函数声明时指定的,指定为本身:
例如:
function A() {}
则 A.prototype.constructor === A;
但是这个属性往往是不准确的:
1 function A() {} 2 function B() {} 3 B.prototype = new A(); 4 var b = new B(); 5 b.constructor; // A
上面的代码,按照constructor的含义,b的constructor应该指向B,但是确指向了A,原因如下
B.prototype = new A();之后B.prototype被复写为A的实例,
则B.prototype.constructor === A;
而b是B的实例则b.constructor === A;
如果想要实现继承,一般要进行constructor修复,即:
B.prototype = new A();
B.prototype.constructor = B;
1 function parent(){ 2 this.a = "world"; 3 this.b = "world"; 4 } 5 parent.prototype = { 6 a : "aaa", 7 b : "bbb", 8 c : "!" 9 } 10 function temp(){ 11 this.a = "hello"; 12 } 13 temp.prototype = new parent(); 14 var child = new temp(); 15 console.log(child.a +" " + child.b+child.c);//hello world!
上面的代码运行结果就为 : “hello world!”生成的原型链即是
child(temp的实例) > __proto__ > temp.prototype(parent的实例) > __proto__ >parent.prototype > __proto__ > Object.prototype >__proto__ > null
3.原型链的调用
如上所示,原型链由__proto__属性串联而成,经过上面的分析,调用的理解就变得很简单了,比如上例的原型链中,当调用child.c属性时,那么程序会按如下方式运行:
在child本层查找c,查询未果,会通过__proto__属性向上查找temp.prototype,查找未果,继续沿着__proto__向上查找parent.prototype,oyeah~终于找到了~所以child.c在这里就会是parent.prototype.c了~
怎么样,简单吧^ ^
最后的最后,呈上一个常用函数给大家:
1 function A() { 2 // do something here 3 } 4 A.prototype = { 5 a : 'aaa', 6 b : function() { 7 } 8 }; 9 function B() { 10 } 11 extend(B, A); 12 function extend(child, parent) { 13 function temp() { 14 this.constructor = child; 15 } 16 temp.prototype = parent.prototype; 17 child.prototype = new temp(); 18 }
extend函数 两个参数都为构造函数,目的是让一个构造函数(子类)继承另一个构造函数(父类)的原型,并且不调用父类的构造函数(有时候构造函数会需要一些参数或者做一些事情破坏了原型链),这个方法可以用来生成想要的原型链哦。
好了,今天就总结这么多,以后继续总结其他的js语法知识