Javascript是一种基于原型的对象语言,而不是我们比较熟悉的,像C#语言基于类的面向对象的语言。在前一篇文章中,我们已经介绍了Javascript中对象定义的创建。接下来我们来介绍一下Javascript对象的继承。我们通过一个例子来介绍对象继承实现的几种方法。

比如,现在有一个“水果”的构造函数:

function Fruit(color) {

    this.color = color;

    this.whatColor = function () {
console.log("This fruit color is " + this.color + ".");
} this.contain = function () {
console.log("Which vitamins it contains.")
}
}

另外”苹果“的构造函数:

function Apple(variety, color) {

    this.variety = variety;

    this.color = color;

    this.whatVariety = function () {
console.log("It is " + this.variety + ".");
};
}

那么在Javascript中如何让”苹果“继承”水果“呢?

  1. 构造函数的继承

    在前面的文章中,我们介绍过函数对象的方法call和apply,它们都是用来调用某个函数,并用方法中指定的对象来替换所调用方法中this。正是基于这一点,我们可以考虑在Apple的构造函数中调用函数Fruit,让Apple对象来替换Fruit中的this,这样就让Apple对象有了Fruit的contain方法,Apple本身的属性color也可以不用在本身的构造函数中定义了。

    function Apple(variety, color) {
    Fruit.call(this, color); this.variety = variety; this.whatVariety = function () {
    console.log("It is " + this.variety + ".");
    }
    } var myApple = new Apple("红富士", "red");
    myApple.whatColor(); //This fruit color is red.
    myApple.contain(); //Which vitamins it contains.
    myApple.whatVariety(); //It is 红富士.

    可以看出Apply对象除了拥有自己本省的属性外,还拥有Fruit对象的属性和方法。但是如果Fruit对象原型有一个属性type定义如下

    Fruit.prototype.type = "fruit";

    这时候如果还利用上面的方法继承,我们就会发现在myApple里面没有定义属性type

    alert(typeof(myApple.type)); // undefined
  2. 原型(prototype)模式继承

    我们知道每个对象都有一个prototype,这也是Javascript的特点。

    如果让Apple的原型指向Fruilt对象的一个实例,那么Apple对象就能有Fruit的所有属性和方法了

       function Apple(variety, color) {       
    
            Fruit.call(this, color);
    this.variety = variety; this.whatVariety = function () {
    console.log("It is " + this.variety + ".");
    }
    } Apple.prototype = new Fruit();
    Apple.prototype.constructor = Apple; var myApple = new Apple("红富士", "red");
    myApple.whatColor(); // This fruit color is red.
    myApple.contain(); //Which vitamins it contains.
    alert(myApple.type); // fruit

    每个对象原型都有一个constructor属性指向对象的构造函数,每一个对象实例也有一个constructor属性,默认调用原型的constructor。如果没有 Apple.prototype.constructor = Apple; ,那么会因为 Apple.prototype = new Fruit(); 让Apple.prototype.constructor指向了Fruit,这显然会导致继承链的紊乱,因此我们必须手动纠正,将Apple.prototype对象的constructor值改为Apple。这是很重要的一点,编程时务必要遵守。我们在Javascript中应该遵循这一点,即如果替换了prototype对象,必须要将属性constructor指回原来的构造函数(对象原本的构造函数)。

    上面这种方法看上去没有什么问题,但是有一个缺点:定义新对象时需要创建一个被继承对象的实例,有时候在一做法需要消耗一定内存,效率不高。如果直接把Fruit.prototype赋给Apple.prototype的话,那就不需要创建对象实例了,

        function Fruit(color) {
    
            this.color = color;
    
            this.whatColor = function () {
    console.log("This fruit color is " + this.color + ".");
    } this.contain = function () {
    console.log("Which vitamins it contains.")
    }
    } Fruit.prototype.type = "fruit"; function Apple(variety, color) {
    Fruit.call(this, color); this.variety = variety; this.whatVariety = function () {
    console.log("It is " + this.variety + ".");
    }
    } Apple.prototype = Fruit.prototype; // it may let the constructor of Apple be Fruit.
    Apple.prototype.constructor = Apple; // assign the constructor back to itself var myApple = new Apple("红富士", "red");
    alert(typeof (myApple.type)); // fruit

    这样Apple对象继承了父对象Fruit的所有属性和方法, 而且效率高,比较省内存。但是缺点是Apple对象的原型和Fruilt对象的原型是同一个对象,如果我们修改其中的一个,势必就会影响到另外一个。

    所以代码 Apple.prototype.constructor = Apple; ,虽然让修正了Apple构造函数的问题,但是Fruit对象的构造函数又有问题。

    alert(Fruit.prototype.constructor === Apple); // true

    结合原型模式继承的这两种方式的优缺点和问题,我们有了另外一种继承方法。

  3. 利用空对象作为中介来继承

        function Fruit(color) {
    
            this.color = color;
    
            this.whatColor = function () {
    console.log("This fruit color is " + this.color + ".");
    } this.contain = function () {
    console.log("Which vitamins it contains.")
    }
    } Fruit.prototype.type = "fruit"; function Apple(variety, color) { Fruit.call(this, color);
    this.variety = variety; this.whatVariety = function () {
    console.log("It is " + this.variety + ".");
    }
    } function Empty() {
    }
    Empty.prototype = Fruit.prototype;
    Apple.prototype = new Empty();
    Apple.prototype.constructor = Apple;

    Empty对象是空对象,几乎不占内存,而且如果修改Apple.prototype不会影响到Fruit的prototye对象。在有的javascript库通常将上面的这种方法封装成一个函数:

    function extend(child, parent) {
    var e = function () { };
    e.prototype = parent.prototype;
    child.prototype = new e();
    child.prototype.constructor = child;
    }

    大家在使用这种方式的时候千万不要忘记在子对象的构造函数中调用父对象的构造函数来初始化父对象中属性和方法,不至于每个新创建的子对象实例中父对象属性都是默认值。如果上面的例子中移除

    Fruit.call(this, color);

    之后,

        var myApple = new Apple("HFS", "red");
    myApple.whatColor(); // This fruit color is undefined.

    虽然我们在创建Apple实例的时候传了“red”值进去,但是取出来的却是undefined,为什么呢?问题就出现在 Apple.prototype = new Empty(); 上,这时候Apple里面没有定义color属性,所以即使有值传进构造函数中,获取到color属性还是未定义的。加上父对象构造函数调用的代码后,就让子对象拥有了父对象的方法和属性,并利用传入的值对它们进行初始化。

  4. 拷贝继承

    接下来我们介绍另外一种方式,纯粹采用"拷贝"方法实现继承。简单说,通过把父对象实例的所有属性和方法,拷贝进子对象来实现继承。

        function Fruit(color) {
    
            this.color = color;
    
            this.whatColor = function () {
    console.log("This fruit color is " + this.color + ".");
    } this.contain = function () {
    console.log("Which vitamins it contains.")
    }
    } Fruit.prototype.type = "fruit"; Function.prototype.extendEx = function (parent) {
    for (var p in parent) {
    this.prototype[p] = parent[p];
    }
    } function Apple(variety, color) { Fruit.call(this, color);
    this.variety = variety; this.whatVariety = function () {
    console.log("It is " + this.variety + ".");
    }
    }
    Apple.extendEx(new Fruit()); var myApple = new Apple("HFS", "red");
    myApple.whatColor(); // This fruit color is red.
    myApple.contain(); //Which vitamins it contains.
    alert(myApple.type); // fruit

Javascript中的对象(二)的更多相关文章

  1. javascript中的对象,原型,原型链和面向对象

    一.javascript中的属性.方法 1.首先,关于javascript中的函数/“方法”,说明两点: 1)如果访问的对象属性是一个函数,有些开发者容易认为该函数属于这个对象,因此把“属性访问”叫做 ...

  2. javascript中Date对象的应用——简易日历的实现

    × 目录 [1]效果 [2]HTML [3]CSS[4]JS 前面的话 简易日历作为javascript中Date对象的常见应用,用途较广泛.本文将详细说明简易日历的实现思路 效果演示 HTML说明 ...

  3. Javascript中的对象和原型(3)

    在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型 ...

  4. javascript中Date对象的应用

    前面的话 简易日历作为javascript中Date对象的常见应用,用途较广泛.本文将详细说明简易日历的实现思路 效果演示 HTML说明 使用type=number的两个input分别作为年和月的输入 ...

  5. Javascript中的对象和原型(三)(转载)

    在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型 ...

  6. Javascript中的对象和原型(一)(转载)

    面向对象的语言(如Java)中有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,JavaScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同. 要了解面向对象,首 ...

  7. JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)

    一.对象 对象可以通过两种形式定义:声明(文字)形式和构造形式.即: var myObj = { key: value // ... }; 或: var myObj = new Object(); m ...

  8. Javascript 中判断对象为空

    发现了一个巧妙的实现: 需要检查一个对象(Object)是否为空,即不包含任何元素.Javascript 中的对象就是一个字典,其中包含了一系列的键值对(Key Value Pair).检查一个对象是 ...

  9. JavaScript 中的对象

    JavaScript 中的对象 在 JavaScript 中,对象是数据(变量),拥有属性和方法. JavaScript 中的所有事物都是对象:字符串.数字.数组.日期,等等.   访问对象的属性 访 ...

  10. JavaScript中判断对象类型方法大全1

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

随机推荐

  1. java的装饰设计模式

    类似python中的装饰器. 示例: public class Test5 { public static void main(String[] args) { Worker w = new Work ...

  2. Java中的原子操作类

    转载: <ava并发编程的艺术>第7章 当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可 ...

  3. 八:Zookeeper开源客户端Curator的api测试

    curator是Netflix公司开源的一套ZooKeeper客户端,Curator解决了很多ZooKeeper客户端非常底层的细节开发工作.包括连接重连,反复注册Watcher等.实现了Fluent ...

  4. Centos查找大文件的办法

    find / -size +100M -exec ls -lh {} \; # 查看整体磁盘占用df -h #切换到这块磁盘检查一下这块磁盘的哪个文件夹占用高,再逐层去查找 du -h --max-d ...

  5. Centos7下安装破解confluence6.3

    confluence是一个专业的企业知识管理与协同软件,可以用于构建企业wiki.通过它可以实现团队成员之间的协作和知识共享.现在大多数公司都会部署一套confluence与jira的结合,用作内部w ...

  6. KMP算法-->深入浅出

    说明: 在网上查了各种资料,终于对KMP算法有了透彻的了解,都说KMP特简单,我咋没有察觉呢?难道是智商不在线?或许都是骗纸? 还是进入正题吧,整理整理大佬的blog KMP算法简介: KMP算法是一 ...

  7. 【JavaScript】setinterval和setTimeout的区别

    计时器setTimeout()与setInterval()是原生JS很重要且用处很多的两个方法, 但很多人一直误以为是相同的功能: 间隔时间重复执行传入的句柄函数. 但实际上, 并非如此, 既然JS给 ...

  8. SpringBoot整合SpringSecurity简单实现登入登出从零搭建

    技术栈 : SpringBoot + SpringSecurity + jpa + freemark ,完整项目地址 : https://github.com/EalenXie/spring-secu ...

  9. 洛谷—— P2562 [AHOI2002]Kitty猫基因编码

    P2562 [AHOI2002]Kitty猫基因编码 题目描述 小可可选修了基础生物基因学.教授告诉大家 Super Samuel 星球上 Kitty猫的基因的长度都是 2 的正整数次幂 ), 全是由 ...

  10. Linux添加或修改ssh端口

    1)  查看ssh服务是否安装齐全 这里使用”rpm –qa|grep ssh”命令查看. [root@xuexi ~]# rpm -qa | grep ssh libssh2-1.4.3-10.el ...