Javascript中的对象(二)

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 = 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
时间: 2024-10-06 00:38:42

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

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

× 目录 [1]效果 [2]HTML [3]CSS[4]JS 前面的话 简易日历作为javascript中Date对象的常见应用,用途较广泛.本文将详细说明简易日历的实现思路 效果演示 HTML说明 使用type=number的两个input分别作为年和月的输入控件,这样在高级浏览器下自带调节按钮 按照周日到周一的顺序进行星期的排列 <div class="box"> <header class='control'> <input id="con

Javascript中的对象和原型(3)

在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型对象实际上就是构造函数的一个实例对象,和普通的实例对象没有本质上的区别.可以包含特定类型的所有实例的共享属性或者方法.这样,如果我们需要修改所有实例中的属性或者方法,就只需要修改一处,就能够影响到所有实例了.因为原型中的属性和方法是共享的.我们可以看下两个图示:       构造函数方式 原型模式方

javascript中Date对象的应用

前面的话 简易日历作为javascript中Date对象的常见应用,用途较广泛.本文将详细说明简易日历的实现思路 效果演示 HTML说明 使用type=number的两个input分别作为年和月的输入控件,这样在高级浏览器下自带调节按钮 按照周日到周一的顺序进行星期的排列 <div class="box"> <header class='control'> <input id="conYear" class="con-in&q

JavaScript中的类型转换(二)

说明: 本篇主要讨论JavaScript中各运算符对运算数进行的类型转换的影响,本文中所提到的对象类型仅指JavaScript预定义的类型和程序员自己实现的对象,不包括宿主环境定义的特殊对象(比如浏览器定义的对象) 上一篇中讨论了JavaScript中原始类型到原始类型的转换,原始类型到对象类型的转换和对象类型到原始类型的转换,这里先提出一个问题 var a = undefined; if(a){ console.log('hello'); }else{ console.log('world')

JavaScript中的对象

JavaScript对象 JavaScript对象是基本数据类型之一,是复合型数据: JavaScript中几乎所有事物都是做对象: JavaScript的对象是拥有属性和方法的数据: JavaScript 中的对象可以简单理解成"名称:值"对(name:value). JavaScript中的对象分类 1.内建对象: 数据分装对象(object.object.prototype.Number.Boolean.String.Array.Function): 工具类对象(Math.Dat

JavaScript中String对象的match()、replace() 配合正则表达式使用

正则表达式由来已久,查找替换功能非常强大,但模板难记复杂. JavaScript中String对象的match().replace()这2个方法都要使用正则表达式的模板.当模板内容与字符串不相匹配时,match()返回null,replace()返回原字符串. 正则表达式的模板对象 //标准写法 regexp = new RegExp(pattern[, flag]); pattern: 模板的用法是关键,也是本章的主要内容. flag: "i"(ignore)."g&quo

【转】JavaScript中的对象复制(Object Clone)

JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b.k2 = 4; 如果只想改变b而保持a不变,就需要对对象a进行复制. 用jQuery进行对象复制 在可以使用jQuery的情况下,jQuery自带的extend方法可以用来实现对象的复制. a = {k1:1, k2:2, k3:3}; b = {}; $.extend(b,a); 自定义clone

(转)JavaScript中判断对象类型的种种方法

我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一个说明运算数类型的字符串.如:"number","string","boolean","object","function","undefined"(可用于判断变量是否存在). 但 type

Javascript中String对象的常用方法

charAt(): 返回在指定位置的字符. charCodeAt(): 返回在指定的位置的字符的 Unicode 编码. var s = "Smile forever!"; s.charAt(2); //i s.charCodeAt(2); //105 concat(): 连接字符串,参数可以是多个字符串. var s1 = "Smile forever!"; var s2 = "extend"; s1.concat(s2); //Smile f

JavaScript中String对象处理HTML标记中文本的方法

big():创建一个<big></big>标记,将这个字符串的字体变大blink():创建一个<blink></blink>标记,使字符串具有闪烁效果bold():创建一个<bold></bold>标记,使字符串加粗显示fixed():创建一个<tt></tt>标记,使字符串固定倾斜显示italics():创建一个<i></i>标记,使字符串以斜体显示small():创建一个<sm