浅谈js中继承的理解和实现

一、前言

java、C#等正统面向对象语言都会提供类似extend之类的处理类的继承的方法,而javascript并没有提供专门的方法用于继承,在javascript中使用继承需要一点技巧。js中实例的属性和行为是由构造函数和原型两部分组成的,js的继承也分为这两部分。下面给大家分享一下在js中如何实现继承,讲的不对的地方望大家指正!

二、继承构造函数中的属性和行为

我们定义两个类Animal和Bird类,来实现js中类的继承。

//定义Animal类
            function Animal(name){
                this.name = name;
                this.type = ‘animal‘;
            }
            //Animal原型添加say方法
            Animal.prototype.say = function(){
                alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
            };

接下来定义Bird类

//定义Bird类
function Bird(name){
    this.name = name;
    this.type = ‘bird‘;
}

Bird.prototype = {

};

我们先让Bird类继承Animal的构造函数中的属性和行为,代码清单如下

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype = {
    say : function(){
        alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
    }
};

//定义Bird类
function Bird(name){
    this.name = name;
    this.type = ‘animal‘;
}

Bird.prototype = {

};

//实例化Bird类
var canary = new Bird(‘yinwu‘);
alert(canary.type);

程序运行正常,但看不出继承,因为在Bird类的构造函数中是将Animal的属性和行为复制了一份。同时,缺点也很明显,如果Animal类的构造函数有任何改动,Bird类也要手动的同步改动,同样一份代码,复制在程序中不同地方,违反了DRY原则,降低了代码的可维护性。需要改进,改进后的代码清单如下:

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype = {
    say : function(){
        alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
    }
};

//定义Bird类
function Bird(name){
    Animal.call(this, name);
}

Bird.prototype = {

};

//实例化Bird类
var canary = new Bird(‘yinwu‘);
alert(canary.type);//animal

我们把Bird类改为了Animal.call(this, name);,也就是通过call()方法实现让Animal内部的this指向Bird类的实例。改进后的代码就实现了构造函数的属性和行为的继承,最后alert(canary.type)弹出的信息为animal。

三、原型的继承

由继承我们知道,继承后子类拥有了父类同样的属性和行为,那么能否将Animal类的原型直接传给Bird类的原型呢?请看如下代码清单:

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype = {
    say : function(){
        alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
    }
};

//定义Bird类
function Bird(name){
    Animal.call(this, name);
}

Bird.prototype = Animal.prototype;

//实例化Bird类
var canary = new Bird(‘yinwu‘);
canary.say();//I‘m a(an) animal, my name is yinwu

通过把Animal类的原型直接传给Bird类的原型,Bird类拥有了Animal原型中的say行为。如果给Bird类的原型添加fly行为,结果会如何呢?如代码清单:

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype = {
    say : function(){
        alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
    }
};

//定义Bird类
function Bird(name){
    Animal.call(this, name);
}

Bird.prototype = Animal.prototype;
//Bird类原型添加fly方法
Bird.prototype.fly = function(){
    alert("I‘m flying more high ");
}
//实例化Bird类
var canary = new Bird(‘yinwu‘);
canary.say();//I‘m a(an) animal, my name is yinwu
canary.fly();//I‘m flying more high
var pig = new Animal(‘xiaozhu‘);
pig.fly();//I‘m flying more high

程序运行你会发现Animal类也获得了fly行为,pig对象调用fly行为弹出I‘m flying more high的提示信息,我们只给Bird类添加fly行为,为什么Animal类也获得了fly行为呢?原因在于”Bird.prototype = Animal.prototype;”这句代码,因为prototype原型本质上是hash对象,赋值时会进行传址,也就是说Bird.prototype原型和Animal.prototype原型指向同一内存地址,对Bird.prototype原型添加fly行为也就相当于为Animal.prototype原型添加了fly行为,这就是pig会fly的原因。如何对代码进行改进呢?请看如下代码清单:

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype = {
    say : function(){
        alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
    }
};

//定义Bird类
function Bird(name){
    Animal.call(this, name);
}

Bird.prototype = new Animal();
Bird.prototype.constructor = Bird;
//Bird类原型添加fly方法
Bird.prototype.fly = function(){
    alert("I‘m flying more high ");
}
//实例化Bird类
var canary = new Bird(‘yinwu‘);
canary.say();//I‘m a(an) animal, my name is yinwu
canary.fly();//I‘m flying more high
var pig = new Animal(‘xiaozhu‘);
pig.fly();//报错

与前一代码清单相比,把”Bird.prototype = Animal.prototype;”这句代码,改成了”Bird.prototype = new Animal(); Bird.prototype.constructor = Bird;”,这两句什么意思呢? ”Bird.prototype = new Animal();”意思是Bird.prototype原型作为Animal类的实例,那么Bird原型对象中包含了一个指向Animal原型对象的指针;“Bird.prototype.constructor = Bird;”意思是因为“Bird.prototype = new Animal();”时,Bird.prototype.constructor指向了Animal原型对象,将其纠正重新指向Bird。代码运行后你会发现pig.fly()会报错,此fly()行为undefined,那是因为Animal原型中没有fly行为。到此我们就实现了Bird类对Animal类的原型继承。

四、js继承的封装

前面二、三小节中分别实现了构造函数和原型的属性和行为的继承,这节中我们将js中实现继承的过程进行封装,向外提供接口调用。请看下列代码:

//js继承封装成函数并提供接口
function extend(subClass, superClass){
    var F = function(){};
    F.prototype = superClass.prototype;//F.prototype 原型对象和superClass.prototype原型对象指向同一内存地址
    subClass.prototype = new F();//subClass.prototype作为F类的实例
    subClass.prototype.constructor = subClass;//将subClass.prototype.constructor重新指向subClass
    subClass.superclass = superClass.prototype;//subClass类中定义superclass属性使其指向superClass.prototype
    if(superClass.prototype.constructor == Object.prototype.constructor){
        superClass.prototype.constructor = superClass;
    }
}

//定义Animal类
function Animal(name){
    this.name = name;
    this.type = ‘animal‘;
}
//Animal原型添加say方法
Animal.prototype.say = function(){
    alert("I‘m a(an) "+ this.type +", my name is "+ this.name);
};

//定义Bird类
function Bird(name){
    this.constructor.superclass.constructor.apply(this, arguments);//通过apply方法继承Animal类中的属性和行为
    this.type = ‘bird‘;
}

//实现对Animal继承
extend(Bird, Animal);

Bird.prototype.fly = function(){
    alert("I‘m flying more high ");
};

//实例化
var canary = new Bird(‘yinwu‘);
canary.say();//I‘m a(an) animal, my name is yinwu
canary.fly();//I‘m flying more high

通过上述方法将js中的继承封装在函数中,要用到继承时直接调用即可。

此文章发布在本人博客园changjianqiu,如需转载本文,请注明来源: http://www.cnblogs.com/changjianqiu/

时间: 2024-10-14 05:46:42

浅谈js中继承的理解和实现的相关文章

浅谈js中的this关键字

浅谈js中的this关键字 全局作用域中的this 函数作用域中的this 不同函数调用方法下的this 直接调用 作为对象的方法调用 作为构造函数调用 通过call或apply方法调用 嵌套函数作用域中的this 浅谈js中的this关键字 this是JavaScript中的关键字之一,在编写程序的时候经常会用到,正确的理解和使用关键字this尤为重要.接下来,笔者就从作用域的角度粗谈下自己对this关键字的理解,希望能给到大家一些启示,权当交流之用. 全局作用域中的this 本文将以作用域由

浅谈JS的继承

JS继承 继承是OO语言中最为人津津乐道的概念,许多OO语言都支持两种方式的继承:接口继承:实现继承. 接口继承:只继承方法签名. 实现继承:继承实际的方法. 由于ES里函数没有签名,所以在ES里面无法实现接口继承,ES只支持实现继承.                                                                                                                                    

浅谈JavaScript中继承的实现

  谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我已就在JavaScript中对象创建的方法做了一些总结,下面就其继承来道说一二:   1:原型链继承: 每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象内部的指针(默认的原型,所有默认类型都继承了Object,而这个继承也是用过原型链实现) fu

浅谈JS中的闭包

今天 大年初一,祝各位小伙伴们狗年旺旺啊,闲来也没事,只能钻研一下自己的分内之事,也就是作为一个前端码农的身份,得时刻保持学习的态度,温故而知新,每天都给自己一个小目标去完成,日积月累,所想达到的状态,都会有所见长. JS中的闭包,想必,做web开发的程序猿们都有一定的认识吧,不仅仅是js里有这种特性,而且弱语言类型的譬如python里也是有闭包这么一个强大的特性的,对于老司机们来说,闭包可真的是一个很好的东西,但是对于新入门的菜鸟们来说,闭包却是一个难以搞得清楚的特性,很多情况下,会用,但是却

浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

var num = 1;    var str = '1';    var test = 1;    test == num  //true 相同类型 相同值    test === num //true 相同类型 相同值    test !== num //false test与num类型相同,其值也相同, 非运算肯定是false    num == str  //true 把str转换为数字,检查其是否相等.    num != str  //false == 的 非运算    num ==

浅谈JS中的typeof和instanceof的区别

JS中的typeof和instanceof常用来判断一个变量是否为空,或者是什么类型. typeof typeof运算符返回一个用来表示表达式的数据类型的字符串. typeof一般返回以下几个字符串: "number", "string","boolean","object","function","undefined" 对于Array,Null等特殊对象使用typeof一律返回obje

浅谈JS中 reduce() 的用法

过去有很长一段时间,我一直很难理解 reduce() 这个方法的具体用法,平时也很少用到它.事实上,如果你能真正了解它的话,其实在很多地方我们都可以用得上,那么今天我们就来简单聊聊JS中 reduce() 的用法. 一.语法 arr.reduce(function(prev,cur,index,arr){ ... }, init); 其中, arr 表示原数组: prev 表示上一次调用回调时的返回值,或者初始值 init; cur 表示当前正在处理的数组元素: index 表示当前正在处理的数

浅谈js中arguments对象

一.先来看看到底是个啥? arguments对象在JS中应该算数比较特殊的对象.不能显示的创建,只在当前函数调用的时候才可以使用,长得有点像数组,但绝对不是Array的实例. 几点说明: 1.1.arguments实际上是当前函数的一个内置属性,在当前函数内,函数名.arguments等价于arguments,在函数外单独使用arguments是没有意义的; 1.2.arguments对象的长度由实际参数个数决定.1.3.函数中的形参和实参是可以相互影响的. 可以看出,当传入的实参个数少于形参个

浅谈js中的九个算法

排序很多时候都会用到,而在js中排序的算法有九个是人们常用的,而且使用起来可以很流畅.本文将对这九种排序算法进行详细介绍,教程尚硅谷JavaScript DOM视频教程还有详细的代码分享哦. 一.插入排序 1)算法简介插 入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描