原型模式
原型模式
我们所创建的每个对象都有一个原型属性(prototype),该属性可以做到方法和属性。来看如下代码
function EDG(player,score) {
this.player = ["scout","meiko","clearlove","iboy","scout"];
this.score = score;
this.Say = function (){
alert("我们要夺冠");
}
}
var edg = new EDG();
edg.Say();
如上述代码,此时会弹出弹框,我们会夺冠,因为edg是构造函数EDG的实例,而构造函数中又存在Say方法。
如果此时来了个新的战队EDG的2队(也就是新建一个EDG的构实例),他们的目标也是夺冠,那么他们也要读Say方法。
//此时新来了一个战队EDG2
//构造函数WE
var edg2 = new EDG();
edg2.Say();//我们要夺冠
有没有发现什么问题,edg2在创建实例的时候又重新创建了一遍Say方法,重复了,edg2里的say和edg里的是不一样的,独立的。
console.log(edg2.Say==edg.Say); //false
那么此时为了不重复要怎么办呢,第一种方法就是将Say定义在全局作用域里如下
//构造函数EDG
function EDG(player,score) {
this.player = ["scout","meiko","clearlove","iboy","scout"];
this.score = score;
this.Say =Say;
}
function Say(){
alert("我们要夺冠");
}
var edg = new EDG();
edg.Say();//我们要夺冠
var edg2 = new EDG();
edg2.Say();
console.log(edg2.Say==edg.Say); //true
但是这样会造成代码污染。此时就需要用到第二种方法原型属性来共享方法和属性。如下代码
function EDG(player,score) {
this.player = ["scout","meiko","clearlove","iboy","scout"];
this.score = score;
//将Say写入EDG的原型对象里,做到共享
EDG.prototype.Say = function Say(){
alert("我们要夺冠");
}
}
var edg = new EDG();
edg.Say();//我们要夺冠
var edg2 = new EDG();
edg2.Say();
console.log(edg2.Say==edg.Say); //true
与构造函数不同的是Say方法是写在原型里共享的,是同一个函数而不是各自又重新创建的。
理解原型对象
无论什么时候,只要创建一个新函数,就会创建一个对应的prototype的原型属性,这个原型属性默认指向函数的原型对象。
默认的原型对象里自动获得一个constructor的属性,这个属性包含一个指向prototype属性所在函数的指针,也就是指向函数,还拿EDG的例子来说, EDG.prototype.constructor指向EDG。
function EDG(player,score) {
this.player = ["scout","meiko","clearlove","iboy","scout"];
this.score = score;
//将Say写入EDG的原型对象里,做到共享
EDG.prototype.Say = function Say(){
alert("我们要夺冠");
}
}
//EDG.prototype.constructor指向EDG
console.log(EDG.prototype.constructor == EDG);//true
当创建实例后,该实例内部会出现一个指向构造函数原型对象的指针,[[prototype]],虽然在js中没有标准的方式访问[[prototype]],但是在火狐,谷歌和Safari浏览器中提供了代替者,__ protp __,如果我们创建一个edg3的实例,此时
edg3.proto 指向 EDG.prototype
function EDG(player,score) {
this.player = ["scout","meiko","clearlove","iboy","scout"];
this.score = score;
//将Say写入EDG的原型对象里,做到共享
EDG.prototype.Say = function Say(){
alert("我们要夺冠");
}
}
//EDG.prototype.constructor指向EDG
console.log(EDG.prototype.constructor == EDG);//true
//创建实例
var edg3 = new EDG();
console.log(edg3.__proto__ == EDG.prototype); //true
下面我们画张图来理解构造函数,实例和原型对象之间的关系(以构造函数EDG为例)
isPrototypeOf()方法
如图所示,实例和原型对象之间通过__proto__连接,它是[[prototype]]的替代,但实际上[[prototype]]是无法访问到的,但可以通过**isPrototype()**方法来确定实例和原型对象间是否有关。当实例对象和原型对象有关联的时候返回true。
var ss = EDG.prototype.isPrototypeOf(edg3);
console.log(ss);//true,Y有关联
如果实例重新定义了属性或者方法呢?
当实例重新定义属性或方法时,会屏蔽原型中的属性和方法,并不会重写它,例如下列代码:
function EDG(player,score) {
//将Say写入EDG的原型对象里,做到共享
EDG.prototype.player = ["scout","meiko","clearlove","iboy","scout"];
EDG.prototype.Say = function Say(){
alert("我们要夺冠");
}
}
var edg4 = new EDG();
console.log(edg4.player);//"scout","meiko","clearlove","iboy","scout"
var edg5 = new EDG();
edg5.player = ["我是edg5的player"];
console.log(edg5.player);//"我是edg5的player"
在EDG的实例edg5的player是自己的,而edg4的则是原型的,这是由于变量搜索原则,先在实例中找找到了就使用,找不到就接着去原型找,edg4中没有player所以就借用了原型对象的player
那么我们怎么删除实例的属性,可以使用delete删除实例属性。
delete edg5.player;
判断一个属性是否存在于实例中——hasOwnProperty()
只有给定的对象存在于实例中才会返回true
function EDG(player,score) {
//将Say写入EDG的原型对象里,做到共享
EDG.prototype.player = ["scout","meiko","clearlove","iboy","scout"];
EDG.prototype.Say = function Say(){
alert("我们要夺冠");
}
}
var edg4 = new EDG();
var edg5 = new EDG();
edg5.player = ["我是edg5的player"];
console.log(edg4.player);//"scout","meiko","clearlove","iboy","scout"
console.log(edg5.player);//"我是edg5的player"
console.log(edg4.hasOwnProperty("player"));//fasle
console.log(edg5.hasOwnProperty("player"));//true