JavaScript中这三种模块模式实现之间的区别是什么?
我已经看到以下三个代码块作为JavaScript模块模式的示例。有什么不同,为什么我会选择一种模式而不是另一种 模式1JavaScript中这三种模块模式实现之间的区别是什么?,javascript,module-pattern,Javascript,Module Pattern,我已经看到以下三个代码块作为JavaScript模块模式的示例。有什么不同,为什么我会选择一种模式而不是另一种 模式1 function Person(firstName, lastName) { var firstName = firstName; var lastName = lastName; this.fullName = function () { return firstName + ' ' + lastName; }; t
function Person(firstName, lastName) {
var firstName = firstName;
var lastName = lastName;
this.fullName = function () {
return firstName + ' ' + lastName;
};
this.changeFirstName = function (name) {
firstName = name;
};
};
var jordan = new Person('Jordan', 'Parmer');
模式2
function person (firstName, lastName) {
return {
fullName: function () {
return firstName + ' ' + lastName;
},
changeFirstName: function (name) {
firstName = name;
}
};
};
var jordan = person('Jordan', 'Parmer');
模式3
var person_factory = (function () {
var firstName = '';
var lastName = '';
var module = function() {
return {
set_firstName: function (name) {
firstName = name;
},
set_lastName: function (name) {
lastName = name;
},
fullName: function () {
return firstName + ' ' + lastName;
}
};
};
return module;
})();
var jordan = person_factory();
据我所知,JavaScript社区通常倾向于最好的模式3。它与前两个有什么不同?在我看来,这三种模式都可以用来封装变量和函数
注:实际上不回答这个问题,我不认为它是重复的。 < P>我不认为它们是模块模式,而是更多的对象实例化模式。就我个人而言,我不会推荐你的任何例子。主要是因为我认为除了方法重载之外,为其他任何东西重新分配函数参数都是不好的。让我们回过头来看看在JavaScript中创建对象的两种方法:
原型和新操作符
这是在JavaScript中创建对象的最常用方法。它与模式1密切相关,但将函数附加到对象原型,而不是每次都创建一个新原型:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
Person.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
};
Person.prototype.changeFirstName = function (name) {
this.firstName = name;
};
var jordan = new Person('Jordan', 'Parmer');
jordan.changeFirstName('John');
Object.create和factory函数
引入了ECMAScript 5,它允许以不同的方式实例化对象。使用Object.create(obj)
来设置原型,而不是使用new
操作符
var Person = {
fullName : function () {
return this.firstName + ' ' + this.lastName;
},
changeFirstName : function (name) {
this.firstName = name;
}
}
var jordan = Object.create(Person);
jordan.firstName = 'Jordan';
jordan.lastName = 'Parmer';
jordan.changeFirstName('John');
如您所见,您必须手动分配属性。这就是为什么创建一个工厂函数来为您进行初始属性分配是有意义的:
function createPerson(firstName, lastName) {
var instance = Object.create(Person);
instance.firstName = firstName;
instance.lastName = lastName;
return instance;
}
var jordan = createPerson('Jordan', 'Parmer');
像往常一样,我必须提到的是关于JavaScript面向对象编程的最好文章之一
我还想指出我自己的小库,名为,它是我在研究JavaScript中的继承机制后创建的。它提供了对象。创建语义作为更方便的包装:
var Person = Proto.extend({
init : function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
},
fullName : function () {
return this.firstName + ' ' + this.lastName;
},
changeFirstName : function (name) {
this.firstName = name;
}
});
var jordan = Person.create('Jordan', 'Parmer');
归根结底,这并不是“社区”喜欢什么,而是更多地理解语言为完成某项任务提供了什么(在您的案例中,创建新的OBEJCT)。从那里你可以更好地决定你喜欢哪种方式
模块模式
似乎模块模式和对象创建之间存在一些混淆。即使看起来相似,它也有不同的责任。由于JavaScript只有函数作用域,所以模块用于封装功能(而不是意外地创建全局变量或名称冲突等)。最常见的方法是将功能包装在一个自动执行的函数中:
(function(window, undefined) {
})(this);
因为它只是一个函数,所以您最好在最后返回一些东西(您的API)
var Person = (function(window, undefined) {
var MyPerson = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
MyPerson.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
};
MyPerson.prototype.changeFirstName = function (name) {
this.firstName = name;
};
return MyPerson;
})(this);
这就是JS中的大部分模块。它们引入了一个包装函数(相当于JavaScript中的一个新作用域)并(可选地)返回一个作为模块API的对象。“哪一个是最好的?”在这里并不是一个有效的问题。
它们都做不同的事情,有不同的权衡,提供不同的好处。
一个或另一个或所有三个(或无)的使用取决于您选择如何设计程序
模式1是JS对“类”的传统理解
它允许原型化
,这不应该与类C语言中的继承
相混淆。
原型更像是其他语言中的公共静态属性/方法。
原型方法也不能访问实例变量(即:未附加到此的变量)
模式#2创建单个对象的单个实例,没有隐式继承
var MyConstructor = function (args) {
var private_property = 123,
private_method = function () { return private_property; },
public_interface = {
public_method : function () { return private_method(); },
public_property : 456
};
return public_interface;
};
var myInstance = MyConstructor(789);
没有继承,每个实例都会获得每个函数/变量的新副本。
如果您处理的对象每页不会有数十万个实例,那么这是完全可行的
模式#3与模式#2类似,不同之处在于您正在构建一个构造函数,并且可以包含与私有静态
方法等效的方法(如果函数要返回值,而不是直接修改对象或数组,则必须100%地传入参数,必须收集返回语句,因为尽管实例构造函数可以访问所有“静态”功能。
这里的实际好处是内存占用较低,因为每个实例都有对这些函数的引用,而不是它们自己的副本
这些都在做非常不同的事情。首先,正如@Daff已经提到的,这些并不都是模块模式。让我们看看它们的区别:
模式1对模式2
你可以省略无用的行
var firstName = firstName;
var lastName = lastName;
正如您在模式2代码中看到的,函数参数已经是局部作用域变量
显然,这两个函数非常相似。它们都在这两个局部变量上创建一个闭包,只有(公开的)fullName
和changeFirstName
函数可以访问该闭包。区别在于实例化时发生的情况
- 在模式2中,您只需返回一个对象(literal),它继承自
object.prototype
- 在模式1中,您将与一个称为“构造函数”的函数一起使用(并且正确大写)。这将创建一个从
Person.prototype
继承的对象,您可以在其中放置所有实例将共享的其他方法或默认属性
还有其他的变体。它们可能倾向于对象上的[public]属性,并将所有方法放在原型上-您可以混合使用这些方法。有关模拟基于类的继承的工作原理,请参阅
什么时候使用?原型模式
var PrivateStaticConstructor = function (private_static_args) {
var private_static_props = private_static_args,
private_static_method = function (args) { return doStuff(args); },
constructor_function = function (private_args) {
var private_props = private_args,
private_method = function (args) { return private_static_method(args); },
public_prop = 123,
public_method = function (args) { return private_method(args); },
public_interface = {
public_prop : public_prop,
public_method : public_method
};
return public_interface;
};
return constructor_function;
};
var InstanceConstructor = PrivateStaticConstructor(123),
myInstance = InstanceConstructor(456);
var firstName = firstName;
var lastName = lastName;
var jordan = person_factory();
jordan.set_firstname("Jordan");
var pete = person_factory();
pete.set_firstname("Pete");
var guyFawkes = person_factory();
guyFawkes.set_lastname("Fawkes");
console.log(jordan.fullname()); // "Pete Fawkes"