JavaScript原型从入门到精通
ps:原型里面涉及的到的各种属性单独讲解很难将通,所以一个属性经常会结合其他属性来理解(文章中如有错误,欢迎指正)。
1.prototype属性
当我们创建一个函数的时候,这个函数就会具有一个原型(prototype)属性,所以只有函数才会有prototype属性。
function Book(Name, Version){
this.Name = Name;
this.Version = version;}; //具有prototype属性
var newBook = new Book("JavaScript", "V2.0"); //不具有prototype属性
Book.prototype; //Object{constructor:Book(),__proto__:Object}
Book函数的原型对象(也就是Book.prototype)里面又有constructor属性以及通过构造函数添加的其他方法(Name、Version)。原型对象(Book.prototype)里的constructor属性又指向prototype属性所在函数的指针(即Book函数,Book函数是一个函数名,也是一个指针),我们可以用一段代码来表示
Book.prototype.constructor === Book;
通过实例化创建一个新类(newBook)的时候,newBook不会有prototype属性,但是它的内部包含一个指针,指向构造函数(Book)的原型对象,这个指针在Firefox、Safari、Chrome浏览器有具体的实现,即__proto__(两边都是两个下划线),如下图
当通过new关键字实例化newBook类时,其实就是将newBook的内部属性(proto)指向了Book.prototype,即
newBook.__proto__ === Book.prototype; //仅在Firefox、Safari、Chrome调试有效
所以newBook实例本身不会包含Book的属性和方法,使用newBook.Name调用Name属性其实就是通过查找对象的过程来实现的。
2.constructor属性
construcor属性用于返回创建该对象的函数,即构造函数。
Book的原型具有constructor属性,那么通过new实例化的对象是否也有constructor属性呢?
答案是肯定的,通过构造函数创建的对象都有两个隐藏属性,constructor属性和__proto__属性。在最开始的代码中,我们很容易就能知道newBook的constructor属性其实就是Book,因为构建newBook对象的函数就是Book函数,它的constructor当然就是Book对象了,即
var newBook = new Book("JavaScript", "V2.0");
newBook.constructor === Book //true
结合第一节(prototype属性),可以总结代码如下:
//文章中代码的结论
newBook.constructor === Book;
Book.prototype.constructor === Book;
//推论
Book.prototype.constructor === newBook.construcotr; //true
//文章中代码的结论(__proto__会在第三节详细讲)
newBook.__proto__ === Book.prototype;
//推论
newBook.__proto__.constructor === Book; //true
newBook.__proto__.constructor === newBook.construcotr; //true
或许有的人会疑惑,Book函数的构造器又是什么?
我们可以通过以下代码来测试
Book.constructor //function Function(){}
alert(Book.constructor === Function) //true
即Book的构造函数是Function,因为创建Book的函数是Function函数,底层JavaScript是通过Function创建一个函数,所以任何通过function构造的函数的constructor属性都指向它,所以如下图
上述代码中Book.constructor返回的是functino Function(){},这说明在JS源码中,Function也是由function创建的,那么也就说明Function中具有constructor属性,而这个属性指向它本身,即
Function.constructor === Function; //true
又由第一节分析,创建函数时也会创建函数的prototype属性,所以Function也有prototype属性,那么Function的原型指向的是什么呢?
Function.prototype //function(){}
这代表什么?(现在先不讲解)
3.__proto__属性
定义:当创建一个函数时,其内部将包含一个指针,指向构造函数的原型对象。
第一节已经讲过,__proto__属性在Firefox、Safari、Chrome浏览器有具体的实现,第二节也说过通过构造函数创建的对象都有两个隐藏属性,constructor属性和__proto__属性,那么我们就知道为什么第一节中Book.prototype为何会输出Object{constructor:Book(),__proto__:Object}了。
通过new关键字实例化对象时,也会创建相应的__proto__属性,且这个属性指向实例化的原型对象(即Book.prototype);
通过function关键字创建函数时,函数也会有__proto__属性,那么这个函数的__proto__属性指向什么呢?以一段测试代码来验证。
newBook.__proto__ === Book.prototype; //true
Book.__proto__; //function(){}
为什么Book.proto会输出function(){ }?
根据 定义,我们就需要找到构造Book的函数,第二节已经给出了答案Function,那么指向构造函数的原型对象就是指向Function的原型,即Function.prototype,附代码验证
Book.__proto__ === Function.prototype //第二节遗留问题
再来测试一下Function的__proto__属性
Function.__proto__; //function(){} ,那么是否与上面的Function.prototype,Book.__proto__相等呢
Function.__proto__ === Function.prototype; //true
Function.__proto__ === Book.__proto__; //true
因为Function是最顶层的函数对象,没有比Function的原型更原型的了,所以Function.__proto__ === Function.prototype。
4.Object与Function
函数是一种对象,那么对象是什么呢?对象是否有prototype属性,__proto__属性以及constructor属性?
var Class = new Object;Class.__proto__; //Object{}
Class.prototype; //undefined
Class.constructor; //function Object(){}
//对象字面量
var Grade = {};Grade.__proto__; //Object{}
Grade.prototype; //undefined
Grade.constructor; //function Object(){}
分析代码结果,对象具有__proto__属性和constructor属性。因为函数是一种对象,我们就可以猜测函数与对象之间必定存在某种关联,那么究竟是什么样的关联呢?
分析: 从上述代码的结果中,我们可以看到,Class.constructor和Grade.constructor(因为两种形式结果相同,就拿其中一种来说明)输出的结果都是function Object(){ },结果是一种函数对象!!!而Class.__proto__的结果只是对象Object{ }。Class.__proto__指向构造函数的原型对象,上述代码中Class.__proto__的构造函数是Object,所以Class.__proto__指向Object的原型对象,即Object.prototype。
Class.__proto__ === Object.protortype; //true
Object.prototype; //Object{}
再来看看Object对象
Object; //function Object() { [native code] }
可以看出Object是一种函数,那么它是否有函数应有的prototype和__proto__以及constructor属性呢
Object.prototype; //Object{}
Object.__proto__; //function(){}
Object.constructor; //function Function(){}
首先,Object的原型是它自己,说明Object是最顶层的对象。然后Object.proto指向构造函数的原型对象,从第二行代码看出Object.proto的构造函数是function的原型,而Object的构造函数是function,故
Object.__proto__ === Function.prototype; //true
Object.constructor === Function; //true
所以总结第三节,得出结论
Object.__proto__ === Function.prototype === Function.__proto__;
Object.constructor === Function ==Function.constructor;
上图就是原型与构造函数个人总结的图(如有错误,欢迎指正,一起学习)。