prototype我们通常叫做js的原型,你可以把prototype看作是一个指针对象,通过prototype可以获取一个对象的原型。每一个通过constructor构造函数创建的js对象都有一个内置的“__proto__”指向这个对象的prototype原型。
construtor属性返回用来创建对象的构造函数的引用。
var mm = {}; mm.constructor === Object; // true var mm = new Object; mm.constructor === Object; //true var mm= []; mm.constructor === Array; // true var mm = new Array; mm.constructor === Array;//true var mm= new Number(3); mm.constructor === Number;// true var mm=function(){} mm.constructor===Function; //true
注意函数的constructor属性指向的是创建函数对象的构造函数Function,并不是函数的字面量字符串。
我们从主观上理解了prototype是原型的意思,但这不够透彻,我们通过代码示例来深入理解prototype。
我们先创建三个方法,
function first(){} function second(){} function three(){}
然后给second函数添加prototype属性
second.prototype={sdate:"2017-1-14"}; second.price="100"; var soo=new second(); console.log(soo.sdate); //2017-1-14 console.log(soo.price); //undefined;
我们通过new操作符创建了second的实例对象soo,soo对象可以获取second原型上的属性。但是soo获取不到second的静态属性。
first.prototype=second; var foo=new first(); console.log(foo.price); //100 console.log(foo.sdate); //undefined;
我们将first函数的prototype指向了second,创建的first的实例foo可以获取second的静态属性,但foo获取不到second原型上的属性。
three.prototype=new second; var too=new three(); console.log(too.price); //undefined console.log(too.sdate); //2017-1-14
我们将three的prototype指向second的实例new second上,创建的three的实例too可以获取second原型上的属性,但是too获取不到second的静态属性。
通过上面代码的验证,你可能会疑问,first的原型明明指向了second,怎么获取不到second原型上的属性呢?这可能是刚接触原型的妹纸们经常犯的一个错误。这里的理解确实比较绕点。其实所有对象的prototype原型机制都是通过对象内部的一个“__proto__”属性指向连接的。高版本浏览器可以直接通过"__proto__"来指定一个对象的原型,IE11一下浏览器不支持访问“__proto__”属性。
我们将上first的代码用“__proto__”改造一下:
second.__proto__={sdate:"2017-1-14"}; second.price="100"; first.prototype=second; var foo=new first(); console.log(foo.price); //100 console.log(foo.sdate); //2014-1-14;
这样foo就可以访问second原型上的属性了。prototype属性也是一个对象,我们使用second.prototype来设置second的原型,其实是设置通过second创建的对象的原型,而通过“__proto__”设置second的原型,才是设置second这个对象上的原型的。
function second(){} console.log(second.constructor); //function Function(){[native code]} console.log((new second).constructor); //function second(){}
通过second.constructor和(new second).constructor我们可以看出,second的构造函数是Function,而new second创建的实例的构造函数才是second函数本身。
而second.prototype={sdate:"217-1-14"};是给second实例设置了“__proto__”的指针,并没有跟他本身设置原型指针。这时候frist.prototype=second的原型是访问second,而second本身的“__proto__”原型指针是指向“Object”对象的。所以foo访问sdate的流程是下面这样的:
1,foo本身没有sdate属性
--->2,foo的原型seond对象也没有sdate属性
--->3,second的原型Object也没有sdate属性
---->4,返回undefined
同理,foo访问price属性,就可以在第2步second对象身上找到price属性。
而three.prototype=new second; second已经给new second实例指定“__proto__”原型指针到{sdate:"2017-1-14"},所以他可以在new second的原型上找到sdate属性,而new second对象是没有price属性的,所以price为undefined。
对于prototype原型我们还可以采用静态方式访问
var sdate=second.prototype.sdate; console.log(sdate); //2017-1-14.
function first(){ } first.prototype={ say:function(){ console.log(this); }, init:function(){ this.say(); } } var foo=new first(); foo.init(); //first{}
调用原型上的say方法,在say方法中,this仍然是first创建的对象。原型链中的方法是不改变this指针的,在原型链中的方法中this指针永远指向原始调用对象。所以我们在原型上的init方法中可以通过this调用原型上的其他方法。
prototype可以封装js使其代码解耦,增强复用性,可以模拟继承。最常用在js插件的写法中。