关于在 Javascript 函数上创建良好接口的问题
Issues about creating good interfaces on Javascript functions
如何在某些 Javascript 代码上实现 high-quality routines (mentioned by Steve McConnell, on Code Complete, chapter 7)?例如,在这种情况下:
$('#variable').on('click', function(){
//do some stuff
});
这是一个非常常见的片段,但它是将一个函数作为参数传递给另一个函数。在我看来,它们不是自我记录的(不可读),也不像书上所说的那样维护程序抽象;但是很常见。
您可以将要传递的函数分配给局部变量,这样至少您可以给它起一个名字:
var onClickCallback = function() {
//do some stuff
};
$('#variable').on('click', onClickCallback);
假设 DOM 节点 #variable
是调用服务的 "widget" UI 元素的根并且具有 CSS class 'clicked' 单击时添加到其中。
以下代码展示了单一职责原则、模型-视图-控制器、依赖注入、有意义的命名、体面的文档、无全局变量、数据封装、高内聚、低耦合等:
/**
* Responsible for performing some
* significant action(s) related to
* widgets.
*/
function WidgetService() {}
WidgetService.prototype.doSomething = function() {
//do some stuff
};
/**
* Responsible for wiring up the object
* graph backing the widget.
*/
function WidgetController($, widgetService) {
this._service = widgetService;
this._model = new WidgetModel({
renderCb: renderCb.bind(this)
});
this._view = new WidgetView($, this._model);
this.onClick = this._onClick.bind(this);
function renderCb() {
this._view.render();
}
}
WidgetController.prototype._onClick = function() {
this._service.doSomething();
this._model.isClicked = true;
};
/**
* Responsible for encapsulating the
* state of the UI element.
*/
function WidgetModel(options) {
options = options || {
renderCb: noop
};
this._renderCb = options.renderCb;
}
WidgetModel.prototype = {
_isClicked: false,
get isClicked() {
return this._isClicked;
},
set isClicked(value) {
this._isClicked = value;
this._renderCb(this._isClicked);
}
};
/**
* Responsible for interacting with the DOM.
*/
function WidgetView($, model) {
this._$ = $;
this._model = model;
this.render = this._render.bind(this);
}
WidgetView.prototype.el = '#variable';
WidgetView.prototype._render = function() {
this._$(this.el).addClass(this._model.isClicked ? 'clicked' : '');
};
/**
* Responsible for linking the DOM
* event with the controller.
*/
function WidgetRouter($, controller) {
$(WidgetView.prototype.el).on('click', controller.onClick);
}
function noop() {}
$(function() {
// Go...
var s, c, r;
s = new WidgetService();
c = new WidgetController($, s);
r = new WidgetRouter($, c);
// Now clicking on the element with ID '#variable' will add a class of clicked to it.
});
如何在某些 Javascript 代码上实现 high-quality routines (mentioned by Steve McConnell, on Code Complete, chapter 7)?例如,在这种情况下:
$('#variable').on('click', function(){
//do some stuff
});
这是一个非常常见的片段,但它是将一个函数作为参数传递给另一个函数。在我看来,它们不是自我记录的(不可读),也不像书上所说的那样维护程序抽象;但是很常见。
您可以将要传递的函数分配给局部变量,这样至少您可以给它起一个名字:
var onClickCallback = function() {
//do some stuff
};
$('#variable').on('click', onClickCallback);
假设 DOM 节点 #variable
是调用服务的 "widget" UI 元素的根并且具有 CSS class 'clicked' 单击时添加到其中。
以下代码展示了单一职责原则、模型-视图-控制器、依赖注入、有意义的命名、体面的文档、无全局变量、数据封装、高内聚、低耦合等:
/**
* Responsible for performing some
* significant action(s) related to
* widgets.
*/
function WidgetService() {}
WidgetService.prototype.doSomething = function() {
//do some stuff
};
/**
* Responsible for wiring up the object
* graph backing the widget.
*/
function WidgetController($, widgetService) {
this._service = widgetService;
this._model = new WidgetModel({
renderCb: renderCb.bind(this)
});
this._view = new WidgetView($, this._model);
this.onClick = this._onClick.bind(this);
function renderCb() {
this._view.render();
}
}
WidgetController.prototype._onClick = function() {
this._service.doSomething();
this._model.isClicked = true;
};
/**
* Responsible for encapsulating the
* state of the UI element.
*/
function WidgetModel(options) {
options = options || {
renderCb: noop
};
this._renderCb = options.renderCb;
}
WidgetModel.prototype = {
_isClicked: false,
get isClicked() {
return this._isClicked;
},
set isClicked(value) {
this._isClicked = value;
this._renderCb(this._isClicked);
}
};
/**
* Responsible for interacting with the DOM.
*/
function WidgetView($, model) {
this._$ = $;
this._model = model;
this.render = this._render.bind(this);
}
WidgetView.prototype.el = '#variable';
WidgetView.prototype._render = function() {
this._$(this.el).addClass(this._model.isClicked ? 'clicked' : '');
};
/**
* Responsible for linking the DOM
* event with the controller.
*/
function WidgetRouter($, controller) {
$(WidgetView.prototype.el).on('click', controller.onClick);
}
function noop() {}
$(function() {
// Go...
var s, c, r;
s = new WidgetService();
c = new WidgetController($, s);
r = new WidgetRouter($, c);
// Now clicking on the element with ID '#variable' will add a class of clicked to it.
});