Javascript OOP-通过接口或原型验证对象时的最佳实践
我从C#背景中学习了更高级的javascript OO策略,我想知道如何实现基于原型的验证,或者这是否是一个好主意。例如,当一个对象或函数需要它的一个参数来满足某个接口时,您可以像这样检查它的接口Javascript OOP-通过接口或原型验证对象时的最佳实践,javascript,oop,inheritance,interface,prototype-programming,Javascript,Oop,Inheritance,Interface,Prototype Programming,我从C#背景中学习了更高级的javascript OO策略,我想知道如何实现基于原型的验证,或者这是否是一个好主意。例如,当一个对象或函数需要它的一个参数来满足某个接口时,您可以像这样检查它的接口 var Interface = function Interface(i) { var satisfied = function (t, i) { for (var key in i) { if (typeof t !== 'object') {
var Interface = function Interface(i) {
var satisfied = function (t, i) {
for (var key in i) {
if (typeof t !== 'object') {
return false;
}
if (!(key in t && typeof t[key] == i[key])) {
return false;
}
}
return true;
}
this.satisfiedBy = function (t) { return satisfied(t, i); }
}
// the interface
var interfacePoint2D = new Interface({
x: 'number',
y: 'number'
});
// see if it satisfies
var satisfied = interfacePoint2D.satisfiedBy(someObject);
我提出了这个策略,只通过接口验证对象,而忽略对象的内部实现
或者,假设您正在使用基于原型的继承,您是否应该基于其原型函数验证参数?我知道您会使用原型来实现默认功能,而接口没有指定任何默认功能。有时,传递给函数的对象可能需要某些默认功能才能使该函数正常工作。只针对接口进行验证更好吗,还是应该针对原型进行验证更好?如果是,最好的方法是什么
编辑--我提供了更多的上下文来解释我为什么要问这个问题
比如说在线游戏设计(大部分用javascript编写的游戏)。在这种情况下,我对验证感兴趣有两个主要原因
1) 如果需要,提供强大的公共API来修改游戏
2) 防止(或至少极大地阻止)潜在的作弊者
这需要在可定制性和滥用性之间取得平衡。具体来说,一种情况是在设计物理引擎时,游戏中的物体会对重力产生反应。在现实系统中,用户不应该向系统中添加对重力没有反应的对象。该系统具有一个函数,该函数表示任何给定点的重力整体效应:
function getGravityAt(x, y) {
// return acceleration due to gravity at this point
}
而作出反应的对象有一种方法可以使用它来更新其加速度:
function update() {
this.acceleration = getGravity(this.position);
}
最起码要做的事情可能是确保添加到系统中的任何对象都有一个“update”方法,但您仍然不能确保update()方法确实是为了对重力做出反应。如果只允许从原型update()方法继承的对象,那么您至少在某种程度上知道系统中的所有东西都会做出真实的反应 这是一个相当主观的问题。我将传递一个问题,即用Javascript进行基于接口的验证是否是一个好主意(可能有很好的用例,但这不是语言中的标准方法)。但我要说的是,基于原型验证对象可能不是一个好主意 如果您完全是通过接口进行验证,那么您可能正在使用其他程序员创建的对象。创建对象的方法有很多种——有些依赖原型,有些不依赖原型,尽管它们都有各自的支持者,但它们都是有效且可能的方法。例如:
var Point = function(x,y) {
return {
x: function() { return x },
y: function() { return y }
};
};
var p = new Point(1,1);
对象p
符合类似于您上面的界面,除了x
和y
是函数。但无法通过检查其构造函数(即Object()
)或Point.prototype
,来验证p
是否满足此接口。您所能做的就是测试p
是否具有名为x
和y
的属性,以及它们是否属于“function”
类型—您正在执行上述操作
您可能会坚持认为p
在其原型链中有一个特定的祖先,例如AbstractPoint
,其中包括x
和y
函数-您可以使用instanceof
来检查这一点。但是您不能确定x
和y
没有在p
中重新定义:
var AbstractPoint = function() {};
AbstractPoint.prototype.x = function() {};
AbstractPoint.prototype.y = function() {};
var Point = function(x,y) {
var p = new AbstractPoint(x,y);
p.x = "foo";
return p;
}
var p = new Point(1,1);
p instanceof AbstractPoint; // true
p.x; // "foo"
也许更重要的是,这使得加入同样满足接口但不从类继承的自定义对象变得更加困难
所以我认为你现在所做的可能是你所希望的最好的。根据我的经验,Javascript程序员更可能使用即时鸭子类型,而不是尝试模仿静态类型语言的功能:
function doSomethingWithUntrustedPoint(point) {
if (!(point.x && point.y)) return false; // evasive action!
// etc.
}
我要重申,类型检查不是惯用的javascript
如果你还想进行类型检查,我推荐谷歌的实现。类型检查是静态完成的:)它有接口约定以及(原型)类型检查。就我个人而言,我根本不会验证。动态类型化语言,如JS或Python,更符合duck类型化的概念。。。如果它听起来和看起来像鸭子,那它就是鸭子。如果您的函数需要一个与某个接口兼容的参数,但没有得到它。。。然后它将抛出一个错误。正确使用你的代码是其他程序员的责任。如果你正在开发javascript游戏或API,并且想要防止框架被滥用,该怎么办?我认为清晰定义的接口是一个好主意,尤其是在API设计中。当然,接口应该清晰定义并有良好的文档记录。别误会我,这总是很重要的。但在我看来,你不应该在运行时检查它们。如果你有一个函数(x,y)需要一个点,而有人传入了一个本身已经包含属性“x”和“y”的对象,你通常会这样做:如果(x.x&&x.y){}检查这样一个对象,这样函数就可以分别接受一个点对象或每个坐标。这不是本质上的接口检查吗?我的类的要点只是以一种不太冗长的方式启用该功能(对于更复杂的对象,if语句会变得很长)。