我正在学习如何用JavaScript制作面向对象编程。是否有接口概念(如Java的接口)?
这样我就可以创建一个监听器。
我正在学习如何用JavaScript制作面向对象编程。是否有接口概念(如Java的接口)?
这样我就可以创建一个监听器。
当前回答
没有,但是它有mixins。 您可以使用抽象子类或mixins作为替代https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#mix-ins
其他回答
JavaScript (ECMAScript edition 3)有一个保留字以备将来使用。我认为这正是为了这个目的而设计的,然而,在匆忙发布规范的过程中,他们没有时间定义如何使用它,所以,目前,浏览器除了让它呆在那里,偶尔会抱怨你试图用它来做什么之外,什么也不做。
创建自己的object. implementation (Interface)方法是可能的,而且非常简单,只要在给定的对象中没有实现特定的属性/函数集,该方法的逻辑就会中断。
我写了一篇关于面向对象的文章,其中使用了我自己的符号如下:
// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
constructor: function(name) {
Dog.superClass.call(this, name);
},
bark: function() {
alert('woof');
}
}).implement(Mammal);
有很多方法来处理这个特殊的问题,但这是我在自己的接口实现中使用的逻辑。我发现我更喜欢这种方法,而且它易于阅读和使用(如上所述)。这意味着在Function中添加一个“实现”方法。原型,有些人可能有一个问题,但我发现它工作得很漂亮。
Function.prototype.implement = function() {
// Loop through each interface passed in and then check
// that its members are implemented in the context object (this).
for(var i = 0; i < arguments.length; i++) {
// .. Check member's logic ..
}
// Remember to return the class being tested
return this;
}
JavaScript接口:
虽然JavaScript没有接口类型,但经常需要它。由于JavaScript的动态特性和原型继承的使用,很难确保跨类接口的一致性——然而,这是有可能做到的;并经常被模仿。
在这一点上,有一些特殊的方法来模拟JavaScript中的接口;方法上的差异通常满足了一些需求,而其他的则没有得到解决。通常情况下,最健壮的方法过于繁琐,阻碍了实现者(开发人员)的工作。
下面是一种接口/抽象类的方法,它不是很麻烦,是显式的,使抽象中的实现最小化,并为动态或自定义方法留下足够的空间:
function resolvePrecept(interfaceName) {
var interfaceName = interfaceName;
return function curry(value) {
/* throw new Error(interfaceName + ' requires an implementation for ...'); */
console.warn('%s requires an implementation for ...', interfaceName);
return value;
};
}
var iAbstractClass = function AbstractClass() {
var defaultTo = resolvePrecept('iAbstractClass');
this.datum1 = this.datum1 || defaultTo(new Number());
this.datum2 = this.datum2 || defaultTo(new String());
this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
this.method2 = this.method2 || defaultTo(new Function('return new Object();'));
};
var ConcreteImplementation = function ConcreteImplementation() {
this.datum1 = 1;
this.datum2 = 'str';
this.method1 = function method1() {
return true;
};
this.method2 = function method2() {
return {};
};
//Applies Interface (Implement iAbstractClass Interface)
iAbstractClass.apply(this); // .call / .apply after precept definitions
};
参与者
命令解析器
resolvePrecept函数是一个在抽象类中使用的实用工具和帮助函数。它的工作是允许对封装的戒律(数据和行为)进行定制的实现处理。它可以抛出错误或警告——并且——将默认值赋给实现者类。
iAbstractClass
iAbstractClass定义要使用的接口。它的方法需要与其实现者类达成默契。这个接口将每个戒律分配给相同的戒律名称空间——或——给戒律解析器函数返回的任何东西。但是,这个默契会分解为一个上下文——实现者的一个规定。
实现者
实现者只是“同意”一个接口(在本例中是iAbstractClass),并通过构造器劫持来应用它:iAbstractClass.apply(this)。通过定义上面的数据和行为,然后劫持接口的构造函数——将实现者的上下文传递给接口构造函数——我们可以确保将添加实现者的重写,并且该接口将解释警告和默认值。
这是一种非常简单的方法,在不同的项目过程中,对我和我的团队都非常有用。然而,它确实有一些警告和缺点。
缺点
虽然这有助于在很大程度上实现整个软件的一致性,但它并没有实现真正的接口——而是模拟它们。尽管定义、默认值、警告或错误都被解释了,但使用的解释是由开发人员强制执行和断言的(就像大多数JavaScript开发一样)。
这似乎是“JavaScript中的接口”的最佳方法,然而,我希望看到以下问题得到解决:
返回类型的断言 签名的断言 冻结删除操作中的对象 JavaScript社区的特殊性中任何其他普遍或需要的断言
也就是说,我希望这对你有帮助,就像我和我的团队一样。
通过接口,您可以实现一种多态方式。Javascript不需要接口类型来处理这个和其他接口的事情。为什么?Javascript是一种动态类型语言。以具有相同方法的类数组为例:
Circle()
Square()
Triangle()
如果你想知道多态是如何工作的,David krugman linsky的书MFC是很棒的(为c++编写的)。
在这些类中实现draw()方法,将这些类的实例推入数组中,并在迭代数组的循环中调用draw()方法。这是完全正确的。你可以说你隐式地实现了一个抽象类。它不存在于现实中,但在你的脑海中,你做到了,Javascript没有问题。真正的接口的不同之处在于,你必须实现所有的接口方法,在这种情况下,这是不需要的。
接口是一个契约。您必须实现所有的方法。只有让它是静态的,你才需要这样做。
把Javascript这样的语言从动态变成静态是有问题的。静止是不可取的。有经验的开发人员对Javascript的动态特性没有问题。
所以我不清楚使用Typescript的原因。如果你将NodeJS和Javascript结合使用,你可以建立非常高效和经济的企业网站。Javascript/NodeJS/MongoDB组合已经是伟大的赢家。
找到一个尽可能低影响的模拟接口的解决方案也困扰着我。
一种解决方案是制作一个工具:
/**
@parameter {Array|object} required : method name list or members types by their name
@constructor
*/
let Interface=function(required){
this.obj=0;
if(required instanceof Array){
this.obj={};
required.forEach(r=>this.obj[r]='function');
}else if(typeof(required)==='object'){
this.obj=required;
}else {
throw('Interface invalid parameter required = '+required);
}
};
/** check constructor instance
@parameter {object} scope : instance to check.
@parameter {boolean} [strict] : if true -> throw an error if errors ar found.
@constructor
*/
Interface.prototype.check=function(scope,strict){
let err=[],type,res={};
for(let k in this.obj){
type=typeof(scope[k]);
if(type!==this.obj[k]){
err.push({
key:k,
type:this.obj[k],
inputType:type,
msg:type==='undefined'?'missing element':'bad element type "'+type+'"'
});
}
}
res.success=!err.length;
if(err.length){
res.msg='Class bad structure :';
res.errors=err;
if(strict){
let stk = new Error().stack.split('\n');
stk.shift();
throw(['',res.msg,
res.errors.map(e=>'- {'+e.type+'} '+e.key+' : '+e.msg).join('\n'),
'','at :\n\t'+stk.join('\n\t')
].join('\n'));
}
}
return res;
};
使用实例:
// create interface tool
let dataInterface=new Interface(['toData','fromData']);
// abstract constructor
let AbstractData=function(){
dataInterface.check(this,1);// check extended element
};
// extended constructor
let DataXY=function(){
AbstractData.apply(this,[]);
this.xy=[0,0];
};
DataXY.prototype.toData=function(){
return [this.xy[0],this.xy[1]];
};
// should throw an error because 'fromData' is missing
let dx=new DataXY();
与类
class AbstractData{
constructor(){
dataInterface.check(this,1);
}
}
class DataXY extends AbstractData{
constructor(){
super();
this.xy=[0,0];
}
toData(){
return [this.xy[0],this.xy[1]];
}
}
它仍然有一点性能的提高,并且需要依赖于Interface类,但是可以用于调试或开放api。
像这样的抽象接口
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
创建一个实例:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
并使用它
let x = new MyType()
x.print()