一、什么是原型
原型prototype是函数的一个属性,这个属性是一个指针,指向一个对象(原型对象),这个原型对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
函数也是一种对象。它也是属性的集合,你也可以对函数进行自定义属性。
javascript就默认的给函数一个属性——prototype。所以,每个函数都有一个属性叫做prototype。
这个prototype的属性值是一个对象(属性的集合!),默认的只有一个叫做constructor的属性,指向这个函数本身。
原型既然作为对象,属性的集合,肯定可以自定义的增加许多属性。例如Object的prototype里面,就有好几个其他属性。
那么看着上图想象一下:
1、我们先创建了一个Person函数备用。
var Person = function(name,age){
this.name = name;
this.age = age;
}
2、Person函数本身也是一个对象,它具有一个属性prototype。
这个属性prototype也是一个对象,它具有一些属性集合。其中有一个属性constructor是一个指针,指向Person函数对象。
那么,此时在内存中,Person及其属性prototype都是唯一一份的单实例。
3、然后,我们利用Person函数new两个对象p1、p2。
这时候,对象p1、p2除了name和age之外,还会默认携带一个隐藏属性__proto__,它是一个指针,指向全局的Person的prototype属性。
二、原型有什么用处
仍然考虑上面的Person函数,现在需要添加一个函数:
var Person = function(name,age){
this.name = name;
this.age = age;
this.isAdult = function(){
if(this.age >= 18){
return true;
}else{
return false;
}
}
}
如果按照上述写法,每一次new Person的时候都会创建一个isAdult的实例。
打印表达式(p1.isAdult===p2.isAdult)的结果,显示为false。显然,这不是我们想要的。
可以这样做:
var Person = function(name,age){
this.name = name;
this.age = age;
}
Person.prototype.isAdult = function(){
if(this.age >= 18){
return true;
}else{
return false;
}
}
由于所有对象的__proto__属性,都是指向全局唯一的Person的属性prototype。
所以,所有由Person创建出来的对象都会共用一个isAdult()方法的实例,类似于Java中的static类型的方法(类方法)。
由此可见,通过这种方式,可以向已定义的对象追加方法。
所以有如下结论:
1、把方法写在prototype中比写在构造函数中消耗的内存更小,因为在内存中一个类的原型只有一个,写在原型中的行为可以被所有实例共享,
实例化的时候并不会在实例的内存中再复制一份 而写在类中的方法,实例化的时候会在每个实例中再复制一份,所以消耗的内存更高
所以没有特殊原因,我们一般把属性写到类中,而行为写到原型中。
2、构造函数中定义的属性和方法要比原型中定义的属性和方法的优先级高,如果定义了同名称的属性和方法,构造函数中的将会覆盖原型中的。