原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以。但如果属性包含引用类型,就存在一定的问题:
function Man() {}; Man.prototype = { constructor: Man, name: 'Jess', age: 25, family: ['a', 'b', 'c'], //添加了一个数组属性 run: function () { return this.name + this.age +this.family; } }; var obj1 = new Man(); obj1.family.push('d'); //在实例中添加 d alert(obj1.run()); var obj2 = new Man(); alert(obj2.run()); //共享带来的麻烦,也有 d 了 //数据共享的缘故,导致很多开发者放弃使用原型,因为每次实例化出的数据需要保留自己的特性,而不能共享。
组合构造函数+原型模式:
function Man(name, age) { //不共享的使用构造函数 this.name= name; this.age= age; this.family = ['a', 'b', 'c']; }; Man.prototype = { //共享的使用原型模式 constructor: Man, run: function () { return this.name + this.age +this.family; } }; //在声明一个对象时,构造函数+原型部分让人感觉很怪异
动态原型模式
function Man(name ,age) { //将所有信息封装到函数体内 this.name= name; this.age= age; if(typeof this.run != 'function') { //仅在第一次调用的初始化 Man.prototype.run = function () { returnthis.name + this.age + 'running...'; }; } } var obj = new Man('Jess', 25); alert(obj.run()); /* 当第一次调用构造函数时,run()方法发现不存在,然后初始化原型。当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会再初始化了。这样及得到了封装,又实现了原型方法共享,并且属性都保持独立。 */ if (typeof this.run != 'function') { alert('第一次初始化'); //调试用 Man.prototype.run= function () { return this.name + this.age + 'running...'; }; } var obj = new Man('Jess', 25); //第一次创建对象 alert(obj.run()); //第一次调用 alert(obj.run()); //第二次调用 var obj2 = new Man('Jack', 28); //第二次创建对象 alert(obj2.run()); alert(obj2.run()); /* 使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系。 */
寄生构造函数
function Man(name, age) { var obj = new Object(); obj.name= name; obj.age= age; obj.run= function () { return this.name + this.age + 'running...'; }; return obj; } /* 寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。 */ //String.prototype.addstring可以通过寄生构造的方式添加 function myString(string) { varstr = new String(string); str.addstring= function () { return this + ',hello!'; }; returnstr; } var str = new myString('Jess'); //比直接在引用原型添加要繁琐好多 alert(str.addstring());
稳妥构造函数 在一些安全的环境中,比如禁止使用this和new,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new。
function Man(name , age) { var obj = new Object(); obj.run= function () { return name + age + 'running...'; //直接打印参数即可 }; return obj; } var obj = Man('Jess', 25); //直接调用函数 alert(obj.run()); //稳妥构造函数和寄生类似。
继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。
function Box() { //Box构造 this.name= 'Lee'; } function Desk() { //Desk构造 this.age= 100; } Desk.prototype = new Box(); //Desc继承了Box,通过原型,形成链条 var desk = new Desk(); alert(desk.age); alert(desk.name); //得到被继承的属性 function Table() { //Table构造 this.level = 'AAAAA'; } Table.prototype = new Desk(); //继续原型链继承 var table = new Table(); alert(table.name); //继承了Box和Desk /* 以上原型链继承还缺少一环,那就是Obejct,所有的构造函数都继承自Obejct。而继承Object是自动完成的,并不需要程序员手动继承。 */ alert(table instanceof Object); //true alert(desk instanceof Table); //false,desk是table的超类 alert(table instanceof Desk); //true alert(table instanceof Box); //true
被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。继承也有之前问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。为了解决引用共享和超类型无法传参的问题,采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。
组合继承 = 原型链+借用构造函数
function Box(age) { this.name= ['Lee', 'Jack', 'Jess'] this.age= age; } Box.prototype.run = function () { return this.name + this.age; }; function Desk(age) { Box.call(this,age); //对象冒充 } Desk.prototype = new Box(); //原型链继承 var desk = new Desk(30); alert(desk.run());
原型式继承 借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function obj(o) { //传递一个字面量函数 functionF() {} //创建一个构造函数 F.prototype= o; //把字面量函数赋值给构造函数的原型 returnnew F(); //最终返回出实例化的构造函数 } var box = { //字面量对象 name: 'Lee', arr: ['a','b','c'] }; var box1 = obj(box); //传递 alert(box1.name); box1.name = 'Jack'; alert(box1.name); alert(box1.arr); box1.arr.push('d'); alert(box1.arr); var box2 = obj(box); //传递 alert(box2.name); alert(box2.arr); //引用类型共享了
寄生式继承把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。
function Box(name) { this.name= name; this.arr= ['a','b','c']; } Box.prototype.run = function () { returnthis.name; }; function Desk(name, age) { Box.call(this,name); //第二次调用Box this.age= age; } Desk.prototype = new Box(); //第一次调用Box
寄生组合继承
function obj(o) { functionF() {} F.prototype= o; returnnew F(); } function create(box, desk) { varf = obj(box.prototype); f.constructor= desk; desk.prototype= f; } function Box(name) { this.name= name; this.arr= ['a','b','c']; } Box.prototype.run = function () { returnthis.name; }; function Desk(name, age) { Box.call(this,name); this.age= age; } inPrototype(Box, Desk); //通过这里实现继承 var desk = new Desk('Jess',25); desk.arr.push('e'); alert(desk.arr); alert(desk.run()); //只共享了方法 var desk2 = new Desk('Jack', 28); alert(desk2.arr); //引用问题解决
转载于:https://blog.51cto.com/zhujingxiu/1561667