JavaScript类
我了解基本的JavaScript伪类:JavaScript类,javascript,oop,design-patterns,Javascript,Oop,Design Patterns,我了解基本的JavaScript伪类: function Foo(bar) { this._bar = bar; } Foo.prototype.getBar = function() { return this._bar; }; var foo = new Foo('bar'); alert(foo.getBar()); // 'bar' alert(foo._bar); // 'bar' 我还了解模块模式,它可以模拟封装: var Foo = (function() {
function Foo(bar) {
this._bar = bar;
}
Foo.prototype.getBar = function() {
return this._bar;
};
var foo = new Foo('bar');
alert(foo.getBar()); // 'bar'
alert(foo._bar); // 'bar'
我还了解模块模式,它可以模拟封装:
var Foo = (function() {
var _bar;
return {
getBar: function() {
return _bar;
},
setBar: function(bar) {
_bar = bar;
}
};
})();
Foo.setBar('bar');
alert(Foo.getBar()); // 'bar'
alert(Foo._bar); // undefined
但这两种模式都有类似于OOP的属性。前者不提供封装。后者不提供实例化。这两种模式都可以修改以支持伪继承
function Foo(){
var _bar = "foo";
return {
getBar: function() {
return _bar;
},
setBar: function(bar) {
_bar = bar;
}
};
};
a = Foo();
b = Foo();
a.setBar("bar");
alert(a.getBar()); // "bar"
alert(b.getBar()); // "foo"
我想知道的是,是否有任何模式允许:
- 继承权
- 封装(支持“私有”属性/方法)
- 实例化(可以有多个“类”实例,每个实例都有自己的状态)
- Javascript当然是面向对象的。您总是有多态性,但是您必须牺牲封装或实例化,这是您遇到的问题
试试这个,让你的选择更加清晰。
还有一个我已经加入书签的老问题:
我想你要找的是“揭示原型模式” Dan Wahlin有一篇很棒的博文: 还有关于这个和其他相关JavaScript结构的更好的Pluralsight课程:这个呢:
var Foo = (function() {
// "private" variables
var _bar;
// constructor
function Foo() {};
// add the methods to the prototype so that all of the
// Foo instances can access the private static
Foo.prototype.getBar = function() {
return _bar;
};
Foo.prototype.setBar = function(bar) {
_bar = bar;
};
return Foo;
})();
现在我们有了实例化、封装和继承。但是,仍然存在一个问题。
private
变量是static
,因为它在Foo
的所有实例中共享。快速演示:
var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'b' :(
更好的方法可能是对私有变量使用约定:任何私有变量都应该以下划线开头。这种约定是众所周知且广泛使用的,因此当其他程序员使用或修改您的代码时,如果看到以下划线开头的变量,他将知道该变量是私有的,仅供内部使用,他不会修改它。以下是使用此约定进行的重写:
var Foo = (function() {
// constructor
function Foo() {
this._bar = "some value";
};
// add the methods to the prototype so that all of the
// Foo instances can access the private static
Foo.prototype.getBar = function() {
return this._bar;
};
Foo.prototype.setBar = function(bar) {
this._bar = bar;
};
return Foo;
})();
现在我们有了实例化、继承,但我们已经失去了封装,转而支持约定:
var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'a' :)
alert(b.getBar()); // alerts 'b' :)
但私人VAR是可访问的:
delete a._bar;
b._bar = null;
alert(a.getBar()); // alerts undefined :(
alert(b.getBar()); // alerts null :(
我最近一直在思考这个问题,以及各种方法的局限性。我能想出的最好的解决方案如下 它似乎解决了继承、实例化和ecapsulation的问题(至少在GoogleChromeV.24上的测试中是这样),尽管可能会以内存使用为代价
function ParentClass(instanceProperty) {
// private
var _super = Object.create(null),
privateProperty = "private " + instanceProperty;
// public
var api = Object.create(_super);
api.constructor = this.constructor;
api.publicMethod = function() {
console.log( "publicMethod on ParentClass" );
console.log( privateProperty );
};
api.publicMethod2 = function() {
console.log( "publicMethod2 on ParentClass" );
console.log( privateProperty );
};
return api;
}
function SubClass(instanceProperty) {
// private
var _super = ParentClass.call( this, instanceProperty ),
privateProperty = "private sub " + instanceProperty;
// public
var api = Object.create(_super);
api.constructor = this.constructor;
api.publicMethod = function() {
_super.publicMethod.call(this); // call method on ParentClass
console.log( "publicMethod on SubClass" );
console.log( privateProperty );
}
return api;
}
var par1 = new ParentClass(0),
par2 = new ParentClass(1),
sub1 = new SubClass(2),
sub2 = new SubClass(3);
par1.publicMethod();
par2.publicMethod();
sub1.publicMethod();
sub2.publicMethod();
par1.publicMethod2();
par2.publicMethod2();
sub1.publicMethod2();
sub2.publicMethod2();
此闭包允许实例化和封装,但不允许继承
function Foo(){
var _bar = "foo";
return {
getBar: function() {
return _bar;
},
setBar: function(bar) {
_bar = bar;
}
};
};
a = Foo();
b = Foo();
a.setBar("bar");
alert(a.getBar()); // "bar"
alert(b.getBar()); // "foo"
很多JS类的一个问题是它们没有保护它们的字段和方法,这意味着任何使用它的人都可能意外地替换一个方法。例如,代码:
function Class(){
var name="Luis";
var lName="Potter";
}
Class.prototype.changeName=function(){
this.name="BOSS";
console.log(this.name);
};
var test= new Class();
console.log(test.name);
test.name="ugly";
console.log(test.name);
test.changeName();
test.changeName=function(){
console.log("replaced");
};
test.changeName();
test.changeName();
将输出:
ugly
BOSS
replaced
replaced
正如您所看到的,changeName函数被覆盖。下面的代码将保护类方法和字段,并使用getter和setter来访问它们,从而使其成为其他语言中的“常规”类
function Class(){
var name="Luis";
var lName="Potter";
function getName(){
console.log("called getter");
return name;
};
function setName(val){
console.log("called setter");
name = val
};
function getLName(){
return lName
};
function setLName(val){
lName = val;
};
Object.defineProperties(this,{
name:{
get:getName,
set:setName,
enumerable:true,
configurable:false
},
lastName:{
get:getLName,
set:setLName,
enumerable:true,
configurable:false
}
});
}
Class.prototype.changeName=function(){
this.name="BOSS";
};
Object.defineProperty(Class.prototype, "changeName", {
writable:false,
configurable:false
});
var test= new Class();
console.log(test.name);
test.name="ugly";
console.log(test.name);
test.changeName();
test.changeName=function(){
console.log("replaced")
};
test.changeName();
test.changeName();
这将产生:
called getter
Luis
called setter
called getter
ugly
called setter
called setter
called setter
现在,您的类方法不能被随机值或函数替换,getter和setter中的代码总是在尝试读取或写入字段时运行。闭包是您的朋友 只需将以下小函数添加到顶级名称空间,就可以进行OOP,并完成以下操作
- 封装,带有静态和实例、私有和公共变量 方法
- 继承权
- 类级注入(例如针对单例服务)
- 没有约束,没有框架,只有简单的老Javascript
上面的函数只是将给定类的原型和最终的父构造函数连接起来,并返回生成的构造函数,以便实例化 现在,您可以在几行代码中最自然地声明基类(即扩展{}),包括静态、实例、公共和私有属性和方法:
MyBaseClass = clazz(function(_super) { // class closure, 'this' is the prototype
// local variables and functions declared here are private static variables and methods
// properties of 'this' declared here are public static variables and methods
return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) {
// local variables and functions declared here are private instance variables and methods
// properties of 'this' declared here are public instance variables and methods
};
});
扩展一个类?更自然的是:
MySubClass = clazz(function(_super) { // class closure, 'this' is the prototype
// local variables and functions are private static variables and methods
// properties of this are public static variables and methods
return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
// local variables and functions are private instance variables and methods
_super.apply(this, arguments); // or _super.call(this, arg1, ...)
// properties of 'this' are public instance variables and methods
};
}, MyBaseClass); // extend MyBaseClass
换句话说,将父类构造函数传递给clazz函数,并将\u super.call(this,arg1,…)
添加到子类的构造函数,该构造函数使用所需参数调用父类的构造函数。与任何标准继承方案一样,父构造函数调用必须位于子构造函数的第一位
请注意,如果需要从构造函数内部的代码简单访问构造函数,您可以使用this.constructor=function(arg1,…{…}
或this.constructor=function MyBaseClass(arg1,…{…}
显式命名构造函数,或者甚至像上面的代码一样,使用返回函数MyBaseClass(arg1,…{…}返回构造函数。你觉得最舒服的
只需像通常从构造函数中一样从此类类实例化对象:myObj=newmybaseclass()代码>
注意闭包是如何很好地封装类的所有功能的,包括它的原型和构造函数,为静态和实例、私有和公共属性和方法提供了一个自然的名称空间。类闭包中的代码完全没有约束。没有框架,没有约束,只有简单的老Javascript。关闭规则
哦,如果您想将单例依赖项(如服务)注入到您的类(即原型)中,clazz
将为您实现这一点
DependentClass = clazz([aService, function(_service, _super) { // class closure, 'this' is the prototype
// the injected _service dependency is available anywhere in this class
return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
_super.apply(this, arguments); // or _super.call(this, arg1, ...)
// the injected _service dependency is also available in the constructor
};
}], MyBaseClass); // extend MyBaseClass
正如上面的代码试图说明的那样,要将单例注入到类中,只需将类闭包作为最后一个条目放入具有所有依赖项的数组中。还将相应的参数添加到\u super
参数前面的类闭包中,顺序与数组中的顺序相同clazz
将把数组中的依赖项作为参数注入到类闭包中。然后,依赖项在类闭包中的任何位置都可用,包括构造函数
事实上,由于依赖项被注入到原型中,所以即使在从类实例化任何对象之前,静态方法也可以使用它们。这是非常强大的接线您的应用程序或单位,并结束了en