65.9K
CodeProject 正在变化。 阅读更多。
Home

15 分钟掌握 JavaScript 对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (16投票s)

2013年11月26日

CPOL

4分钟阅读

viewsIcon

29130

downloadIcon

277

如何处理JavaScript中的对象创建、封装和继承、不同的技术以及示例

引言

本文简要介绍了如何以不同方式创建对象、实现继承和封装、使用原型属性,并通过JavaScript单元测试框架QUnit (https://qunit.jqueryjs.cn/) 来测试我的代码。

背景

工厂模式工厂方法模式是一种面向对象创建型设计模式,用于实现工厂的概念,并解决创建对象(产品)而不指定将创建哪个确切的问题。- 来自Wikipedia。

基本上,它是一种通过使用方法来创建新对象实例的特定方式。

一切皆对象

在JavaScript中,一切皆对象,请始终铭记这一点。

JavaScript是一种非常灵活的语言,这既可以是优势也可以是劣势。如果您是有条理的开发者,那么您会做得很好。您可以创建对象,但它们与面向对象编程之间的关系并不直接。例如,JavaScript中没有class关键字,但您可以模拟类似的功能和封装、继承等面向对象概念。

JavaScript被称为一种基于原型的面向对象编程语言,因此您将使用原型在创建或克隆对象后才使用它们。

QUnit

我将使用Qunit来创建测试方法并断言值。

https://qunit.jqueryjs.cn下载QUnit .js.css文件。将两者添加到您的项目中。阅读入门部分。我们将使用equal函数,它基本上比较两个值并将它们评估为布尔条件。

对象字面量模式

创建对象的最简单方法,只需名称、花括号,然后指定所需的属性和/或方法。

var person = {
    name: 'steve',
    age: 25,
    getInfo: function () {
        return this.name + ' ' + this.age;
    }
};

在这里,我创建了一个person对象,它有两个属性;name(字符串类型)和age(数字类型);还有一个名为getInfo的函数,它返回一个包含这两个属性值的字符串。

测试方法

在此测试方法中,我使用了已创建的person对象并调用了它的getInfo()函数。

test("Object literal test", function () {
    expect(1);

    var expected = 'steve 25';

    var actual = person.getInfo();

    equal(actual, expected, actual);
});

运行页面后,结果是

这表明“对象字面量测试”通过了,没有错误。

工厂模式

JavaScript中的每个对象都有一个原型对象,可以使用new关键字克隆该对象。

function getEmployee(name, department) {
    var newEmployee = new Object();
    newEmployee.name = name;
    newEmployee.department = department;
    newEmployee.getInfo = function () {
        return this.name + ' ' + this.department;
    }
    return newEmployee;
};

首先,我需要创建一个函数来实现工厂模式,该函数接收我需要设置的属性值的参数,在函数内部使用New Object()指令创建一个空对象,最后指定所需的属性和方法。

测试方法

在此测试中,我创建了两个名为employee1employee2的对象实例,它们都有namedepartment

test("Factory Pattern test", function () {
    expect(2);
    var employee1 = getEmployee('David', 'financial');
    var employee2 = getEmployee('Mark', 'sales');

    equal(employee1.getInfo(), 'David financial', employee1.getInfo());
    equal(employee2.getInfo(), 'Mark sales', employee2.getInfo());
});

结果

类模拟

正如我所说,JavaScript中没有class关键字;相反,我们可以使用函数和默认构造函数来模拟它。

function Computer(brand, is64, color) {
    var _brand = brand;
    var _is64 = is64;
    var _color = color;
    this.getInfo = function () {
        return _brand + ' ' + is64 + ' ' + _color;
    }
};

在这里,我创建了一个充当默认构造函数的函数,同时它允许我实现封装。使用var关键字声明每个属性(或变量)可确保它们不能被外部代码修改。

测试方法

只需创建两个computer类型的对象,并使用默认构造函数为其分配内部值。

test("Class simulation", function () {
    expect(2);
    var toshiba = new Computer('toshiba', true, 'black');
    var hp = new Computer('hp', false, 'red');

    equal(toshiba.getInfo(), 'toshiba true black', toshiba.getInfo());
    equal(hp.getInfo(), 'hp false red', hp.getInfo());
});

结果

原型属性

JavaScript中的一切皆对象,并且一切皆有原型属性,它本身就是一个对象,包含应可用于您正在处理的类型的所有实例的属性和方法。

在此示例中,我们将使用prototype属性重写Computer类的getInfo方法。我将其命名为Computer2类。

function Computer2(brand, is64, color) {
    this.brand = brand;
    this.is64 = is64;
    this.color = color;
};
Computer2.prototype.getInfo = function () {
    return this.brand + ' ' + this.is64 + ' ' + this.color;
};

区别在于getInfo的实现,它是在类定义之外使用原型属性完成的,因此我影响了所有使用getInfo函数的computer2对象的实例。

测试方法

test("Instance test using prototype, overwrite original function shared across all instances", 
      function () {
    expect(2);
    var toshiba = new Computer2('toshiba', true, 'black');
    var hp = new Computer2('hp', false, 'red');
    //overwrite the original implementation
    Computer2.prototype.getInfo = function () {
        return this.brand + ' is ' + this.is64 + ' and of color ' + this.color;
    };
    equal(toshiba.getInfo(), 'toshiba is true and of color black', toshiba.getInfo());
    equal(hp.getInfo(), 'hp is false and of color red', hp.getInfo());
});

在创建了Computer2的两个实例后,我用新的实现覆盖了getInfo函数,该新实现基本上改变了返回文本。有趣的是,我正在为所有实例(已创建的和新的)更改实现。

结果

继承

要在JavaScript中实现继承,我们必须使用一种称为IIFE(立即调用的函数表达式(或IIFE,发音为“iffy”)是一种JavaScript设计模式,它使用JavaScript的函数作用域来产生词汇作用域, - Wikipedia.org),基本上,是一段在浏览器加载后立即执行的代码。

因此,让我们按如下方式创建三个类:一个超类和两个子类。

接下来是我的超类,名为USER,它有两个属性和一个方法。

//superclass
var User = (function () {
    function User(name, isActive) {
        this.name = name;
        this.isActive = isActive;
    }
    User.prototype.getInfo = function () {
        return this.name + ' is active: ' + this.isActive;
    }
    return User;
})();

接下来是子类Developer,有几个重要部分。

在用于创建对象的内联函数中,我接收parent,它代表我正在继承的超类。使用prototype属性将对象类型更改为User。使用parent.call引用parent函数(构造函数),并将其发送以分配为子项。按需编写新方法,例如:Writecode()。在IIFE声明的末尾,在括号中指定parent类,如下所示:(user)

var Developer = (function (parent) {
    Developer.prototype = new User();
    //intheritance is accomplished by changing the prototyp object for the child

    Developer.prototype.constructor = Developer;
    function Developer(name, isActive, languajes) {
        this.languajes = languajes;
        parent.call(this, name, isActive);
    }
    Developer.prototype.WriteCode = function () {
        return 'writing code!';
    };
    return Developer;
})(User);

var Dba = (function (parent) {
    Dba.prototype = new User();
    //intheritance is accomplished by changing the prototyp object for the child

    Dba.prototype.constructor = Dba;
    function Dba(name, isActive, isSqlDba, isOracleDba) {
        this.IsSqlDba = isSqlDba;
        this.IsOracleDba = isOracleDba;
        parent.call(this, name, isActive);
    }
    Dba.prototype.MakeBackup = function () {
        return 'backup done!';
    };
    return Dba;
})(User); 

测试方法

基本上,创建两个不同的子对象实例;调用父方法(getInfo)以及子级的新方法(WriteCodeMakeBackup)。

test("Inheritance Test", function () {
    expect(4);
    var dev = new Developer('James', true, ' vb.net and c#');
    var dba = new Dba('John', true, true, false);
    
    equal(dev.getInfo(), 'James is active: true');
    equal(dba.getInfo(), 'John is active: true');
        
    equal(dev.WriteCode(), 'writing code!', dev.WriteCode());
    equal(dba.MakeBackup(), 'backup done!', dba.MakeBackup());
});

Using the Code

请查看下面的链接,获取包含我在文章中使用的所有代码的Visual Studio 2013解决方案。取消注释特定测试以查看结果。

历史

  • 版本1:2013年11月26日
© . All rights reserved.