上一篇随笔讲了封装,这一篇我们说说继承,还是那上一篇猫和狗说事儿
function Dog(name,s){ this.name=name; this.sex=s; } Dog.prototype.type="犬科"; Dog.prototype.spack==function(){ alert("汪汪。。。"); } function Cat(name,s){ this.name=name; this.sex=s; } Cat.prototype.type="猫科"; Cat.prototype.spack=function(){ alert("喵喵。。。"); }
当然猫和狗都是动物,那他们就有动物的属性,比如需要吃东西,需要呼吸,喝水等等。
现在我们封装一个动物对象:
function Animal(){ this.eat="true"; this.breath="true";
this.name="动物"; } Animal.prototype.drink=function(){
alert("喝水");
};
现在我们需要让猫的对象继承于动物对象的一些属性呢,我们只需要将父对象的属性绑定到子对象上就能让子对象享受父对象的属性和方法,代码如下:
function Dog(name,s){ Animal.apply(this, arguments); this.name=name; this.sex=s; } function Cat(name,s){ Animal.apply(this, arguments); this.name=name; this.sex=s; }
当然这种方法也是最简单的,还有别的方法:我们可以用prototype来实现,代码如下
function Dog(name,s){ this.name=name; this.sex=s; } Dog.prototype = new Animal();
Dog.prototype.constructor = Dog; Dog.prototype.type="犬科"; Dog.prototype.spack==function(){ alert("汪汪。。。"); }
这样就让猫和狗都继承了动物的属性和方法了,让我们仔细剖析一下这种方式,在将new Animal()赋给Cat.prototype后,
function Cat(name,s){ this.name=name; this.sex=s; } Cat.prototype = new Animal(); console.log(Cat.prototype); Cat.prototype.type="猫科"; Cat.prototype.spack=function(){ alert("喵喵。。。"); }
控制台输出
Animal { breath:"true" eat:"true" spack:function () type:"猫科" __proto__:Object }
打开—proto—,我们看到:
__proto__:Object drink:function () constructor:function Animal() arguments:null caller:null length:0 name:"Animal" prototype:Object __proto__:function () [[FunctionLocation]]:test1.html:6 [[Scopes]]:Scopes[1] __proto__:Objec
我们在代码里输出一下:
alert(cat1.constructor == Animal); // true
我们很明显发现constructor指向的是Animal(),在程序中它本应该指向Cat();当我们加入Cat.prototype.constructor = Cat,打开constructor再看时发现
constructor:function Cat(name,s)
有人会说我没家这句话难道不行吗,我没加上述代码程序照样继承了父对象的属性和方法,当然在这种小案例中有可能会行,但是在大项目中这样写代码会在造成继承紊乱,造成不可预知的错误,所以我么需要手动调整Cat.prototype.constructor的值,所以我们在程序中如果给prototype赋值,那么下一步必须是将对象.prototype.constructor的值重新指回到原对构造函数。
当然我们也可以让子构造函数直接继承父构造函数的prototype,代码如下:
function Cat(name,s){ this.name=name; this.sex=s; } Cat.prototype = Animal.prototype; console.log(Cat.prototype); Cat.prototype.type="猫科"; Cat.prototype.spack=function(){ alert("喵喵。。。"); }
这种方法好处是高效,不需要重新在新建一个对象,当然缺点也很明显,此刻Animate.prototype和Cat.prototype指向同意数据源,不加Cat.prototype.constructor = Cat时Cat.prototype输出Animal(),加了之后Cat.prototype输出Cat(),貌似对了,可是Animal.prototype此刻居然也输出Cat(),原因是此刻Animate.prototype和Cat.prototype指向同意数据源,当Cat.prototype做出修改时事实上该的是它俩的数据源,所以,这种方法不可取。
当然我们还可以通过中间量的思路解决这个问题,代码如下:
function Bridge(){ } bridge.prototype=Animal.prototype; Cat.prototype=new Bridge(); Cat.prototype.constructor = Cat;
此刻Bridgr没有添加新的方法和属性,所以它几乎不占内存,而同时我们对prototype进行修改也不会影响其父级对象的prototype
我们将上述代码封装成一个函数,以备使用:
function brideg(p,s){ var F=function(){}; F.prototype=p.prototype; s.prototype = new F(); s.pprototype.constructor= s; s.uber= p.prototype; }
此处uper为子对象添加一个属性,这个属性直接指向父对象的prototype,这等于在子对象上打开一条通道,可以直接调用父对象的方法。
extend(Cat,Animal); var cat1 = new Cat("咪咪","雄"); alert(cat1.name); // 动物
还有一种继承思路,就是将父对象的所有属性全部拷贝给子属性,那么就相当于子对象继承于父对象了
代码如下
function extend(p,s){ var p1=p.prototype; var s1=s.prototype; for(var i in p1){ s1[i]=p1[i]; } s1.uper=p1; }
调用如下:
extend2(,Animal, Cat); var cat1 = new Cat("咪咪","雄"); alert(cat1.name); // 动物
--------------------------------------------------------我是分割线-------------------------------------------------------------
本文参考阮大神文章,地址:
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html