原型
原型,字面解释,原始的类型或模型,即它是一个最初的模板或框架,我们可以在它的基础上进行个性化修改。在JavaScript中同样适用。
1.原型模式VS构造函数
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,即通过调用构造函数而创建的那个对象的原型对象。类似于我们经常用的继承,它包含可以由特定类型的所有实例共享的属性和方法。
接下来结合构造函数对比的方式进行总结介绍。
1.1定义对象信息
构造函数
function Box(name,age){ this.name='wang'; //实例属性 this.age=23; //实例属性 this.run=function(){ //实例方法 return this.name+this.age+'fighting...'; } }
原型模式
与构造函数不同,不必在构造函数中定义对象信息,可以直接将这些信息添加到原型中
function Box1(){} //构造函数函数体内什么都没有 Box1.prototype.name='wang'; //原型属性 Box1.prototype.age=23; //原型属性 Box1.prototype.run=function(){ //原型方法 return this.name+this.age+'fighting...'; }
1.2资源共享
构造函数
不同的实例化,得到的方法地址是不一样的,是唯一的。
var box1=new Box('Lee’,100); var box2=new Box('Jack',200);
代码执行原理图(一)
原型模式
可以让所有对象实例共享它所包含的属性和方法,而且他们的地址是共享的,都一样。
var box1=new Box1() var box2=new Box1()
代码执行原理图(二)
1.3执行流程
构造函数
只有实例对象,所以不存在先后顺序
原型模式
优先访问实例对象上的属性,如果对象上没有再去访问原型对象上的属性。虽然我们可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值
var box1=new Box1(); //alert(box1.name); //wang,原型中的属性 box1.name=’zhang’; Alert(box1.name); //zhang,就近原则,先访问实例对象的属性,值为’zhang’ var box2=new Box1(); Alert(box2.name); //wang,对象实例的属性不能被共享,box2只能访问原型中的属性
2.优缺点
2.1优缺点
原型的共享性是它的优点同时也是它的缺点。共享对于函数和包含基本值的属性是可以的,如果属性包含引用类型,就会存在一定的问题:每次实例化出的数据需要保留自己的特性,而不能共享,但是引用类型的属性的修改会同步到原型,这致使很多开发者放弃使用原型。
function Box1(){} Box.prototype={ name:wang'; //原型属性 age:23; //原型属性 family:['爸爸','妈妈','哥哥']; //添加了一个数组属性,这属于引用类型 run:function(){ //原型方法 return this.name+this.age+this.family; } }; var box1=new Box1(); box1.family.push('姐姐'); //在实例中添加‘姐姐’ alert(box1.run()); //wang23爸爸妈妈哥哥姐姐 var box2=new Box1(); alert(box2.run()); //wang23爸爸妈妈哥哥姐姐,也有‘姐姐’了。
2.2解决方案
为了解决构造传参和共享问题,组合构造函数+原型模式:共有属性在构造函数中定义,共有方法在原型中定义。
function Box1(name,age){ //不共享的使用构造函数 this.name=name; this.age=age; this.family=['爸爸','妈妈','哥哥']; }; Box.prototype={ //共享的使用原型模式 constructor:Box, run:function(){ //原型方法 return this.name+this.age+this.family; } };
改进1
可以将原型封装到构造函数中,提高代码可读性
function Box1(name,age){ //不共享的使用构造函数 this.name=name; this.age=age; this.family=['爸爸','妈妈','哥哥']; Box.prototype. run=function(){ //原型方法 return this.name+this.age+this.family; } }; var box1=new Box1('Lee',100); alert(box1.run()); var box2=new Box1('Jack',200); alert(box2.run());
注意:原型被初始化了两次。
改进2
动态原型模式,保证原型初始化的唯一性
function Box1(name,age){ //不共享的使用构造函数 this.name=name; this.age=age; this.family=['爸爸','妈妈','哥哥']; //条件判断原型是否已经被初始化 if(typeof this.run!='function'){ Box.prototype. run=function(){ //原型方法 return this.name+this.age+this.family; } } }; var box1=new Box1('Lee',100); alert(box1.run()); var box2=new Box1('Jack',200); alert(box2.run());
3.总结
从编织知识网出发,原型模式类似于设计模式中的继承。原型对象相当于父类,特定类型的实例对象相当于子类,原型里面的方法和属性可以被这些特定类型的实例对象共享,而这些实例对象又可以在私有的作用域内个性化操作即重写从原型共享得到的方法或属性,但是这些修改不会影响原型对象(引用类型属性除外)。