15 分钟掌握 JavaScript 对象






4.54/5 (16投票s)
如何处理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()
指令创建一个空对象,最后指定所需的属性和方法。
测试方法
在此测试中,我创建了两个名为employee1
和employee2
的对象实例,它们都有name
和department
。
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
)以及子级的新方法(WriteCode
和MakeBackup
)。
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日