试图理解 Javascript 中的 DRY 原则
Trying to understand DRY principles in Javascript
我目前正在努力提高我的重构技能,我写了一段代码,其中有两个非常相似的方法,我正在努力简化我臃肿的代码,任何建议不客气。
如您所见,这两种方法非常相似,唯一真正的区别是 URL 和 POST。
authenticateA : function( e ) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : '/A_authentications/update/',
data : { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() },
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
},
authenticateB : function( e ) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : '/B_authentications/',
data : { otp : this.$el.find( '#B-input' ).val() },
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status )
}
},
dataType : 'json'
});
}
我在事件块中调用这些方法作为点击函数:
'click [data-fn="authenticate-A"]' : 'authenticateA',
'click [data-fn="authenticate-B"]' : 'authenticateB'
我认为这些可以重构为一种方法或两种更精简的方法,我只是不确定从哪里开始,再次提前感谢。
尝试(未经测试的代码):
authenticate: function(t, e) { // t = 'A' || 'B'
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this
, url
, data;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
// conditionnaly set up your variables
if(t == 'A') {
data = { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() };
url = '/A_authentications/update/';
} else if(t == 'B') {
url = '/B_authentications/';
data = { otp : this.$el.find( '#B-input' ).val() };
}
$.ajax({
type : 'POST',
url : url, // use them
data : data, // use them
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
},
您可以拥有一个生成这些函数的函数:
generateAuthFunction : function( authDetails) {
return function (e) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : authDetails.url,
data : authDetails.dataFunc(this),
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
};
}
然后你生成它:
var authDetailsA = {
url : '/A_authentications/update/',
dataFunc : function (this) {
return { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() };
}
};
var authDetailsB = {
url : '/B_authentications/',
dataFunc : function (this) {
return { otp : this.$el.find( '#B-input' ).val() };
};
authenticateA : generateAuthFunction(authDetailsA);
authenticateB : generateAuthFunction(authDetailsB);
你可以像以前那样称呼它:
'click [data-fn="authenticate-A"]' : 'authenticateA',
'click [data-fn="authenticate-B"]' : 'authenticateB'
我认为这甚至可能引入不必要的复杂性,但它更干。
你可以在authenticate函数中检查data-fn属性。
authenticate: function (e) {
var $this = $(e.target).closest('[data-fn]'),
text = $this.text(),
that = this;
$this.text('Authenticating...').addClass("auth-button-disable");
var fn = $this.data("fn");
switch (fn) {
case "authenticate-A":
data = {
_method: 'PUT',
sms_token: this.$el.find('#sms-input').val()
};
url = '/A_authentications/update/';
break;
case "authenticate-B":
data = {
otp: this.$el.find('#B-input').val()
};
url = '/B_authentications/update/';
break;
}
$.ajax({
type: 'POST',
url: url,
data: data,
complete: function (xhr) {
if (xhr.status === 200) that.relocate();
else {
$this.text(text).removeClass("auth-button-disable");
that.handleError(xhr.status);
}
},
dataType: 'json'
});
}
提取您的请求。将您的应用程序逻辑混合到您的视图中只会混淆画面。让我们创建一个 Authentication
模块:
var Authentication = (function(Backbone, _) {
function whoGoesThere(opts) {
opts = _.extend({}, opts, {
type : 'POST',
dataType: 'json'
});
return Backbone.$.ajax(opts);
}
return {
A: function(data) {
data = _.extend({}, data, {
_method : 'PUT'
});
return whoGoesThere({
url : '/A_authentications/update/',
data: data
});
},
B: function(data) {
return whoGoesThere({
url : '/B_authentications/',
data: data
});
}
};
})(Backbone, _);
将您的视图配置为使用函数而不是函数名称来处理事件,将您的值传递给前一个模块上的适当方法,然后将返回的承诺委托给通用处理程序:
events: {
'click [data-fn="authenticate-A"]': function(e) {
var promise = Authentication.A({
sms_token : this.$el.find( '#sms-input' ).val()
});
this.onAuthentication(e, promise);
},
'click [data-fn="authenticate-B"]': function(e) {
var promise = Authentication.B({
otp : this.$el.find( '#B-input' ).val()
});
this.onAuthentication(e, promise);
}
}
处理承诺(这里是 Ajax 对象,但它可以是任何东西)
onAuthentication: function(e, promise) {
var $this = $(e.target).closest('[data-fn]')
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable");
promise.done(function() {
that.relocate();
});
promise.fail(function(xhr) {
that.handleError(xhr.status);
});
promise.always(function() {
$this.text(text).removeClass("auth-button-disable");
});
}
我目前正在努力提高我的重构技能,我写了一段代码,其中有两个非常相似的方法,我正在努力简化我臃肿的代码,任何建议不客气。
如您所见,这两种方法非常相似,唯一真正的区别是 URL 和 POST。
authenticateA : function( e ) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : '/A_authentications/update/',
data : { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() },
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
},
authenticateB : function( e ) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : '/B_authentications/',
data : { otp : this.$el.find( '#B-input' ).val() },
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status )
}
},
dataType : 'json'
});
}
我在事件块中调用这些方法作为点击函数:
'click [data-fn="authenticate-A"]' : 'authenticateA',
'click [data-fn="authenticate-B"]' : 'authenticateB'
我认为这些可以重构为一种方法或两种更精简的方法,我只是不确定从哪里开始,再次提前感谢。
尝试(未经测试的代码):
authenticate: function(t, e) { // t = 'A' || 'B'
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this
, url
, data;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
// conditionnaly set up your variables
if(t == 'A') {
data = { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() };
url = '/A_authentications/update/';
} else if(t == 'B') {
url = '/B_authentications/';
data = { otp : this.$el.find( '#B-input' ).val() };
}
$.ajax({
type : 'POST',
url : url, // use them
data : data, // use them
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
},
您可以拥有一个生成这些函数的函数:
generateAuthFunction : function( authDetails) {
return function (e) {
var $this = $( e.target ).closest( '[data-fn]' )
, text = $this.text()
, that = this;
$this.text( 'Authenticating...' ).addClass("auth-button-disable")
$.ajax({
type : 'POST',
url : authDetails.url,
data : authDetails.dataFunc(this),
complete: function( xhr ) {
if ( xhr.status === 200 )
that.relocate();
else {
$this.text( text ).removeClass("auth-button-disable");
that.handleError( xhr.status );
}
},
dataType : 'json'
});
};
}
然后你生成它:
var authDetailsA = {
url : '/A_authentications/update/',
dataFunc : function (this) {
return { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() };
}
};
var authDetailsB = {
url : '/B_authentications/',
dataFunc : function (this) {
return { otp : this.$el.find( '#B-input' ).val() };
};
authenticateA : generateAuthFunction(authDetailsA);
authenticateB : generateAuthFunction(authDetailsB);
你可以像以前那样称呼它:
'click [data-fn="authenticate-A"]' : 'authenticateA',
'click [data-fn="authenticate-B"]' : 'authenticateB'
我认为这甚至可能引入不必要的复杂性,但它更干。
你可以在authenticate函数中检查data-fn属性。
authenticate: function (e) {
var $this = $(e.target).closest('[data-fn]'),
text = $this.text(),
that = this;
$this.text('Authenticating...').addClass("auth-button-disable");
var fn = $this.data("fn");
switch (fn) {
case "authenticate-A":
data = {
_method: 'PUT',
sms_token: this.$el.find('#sms-input').val()
};
url = '/A_authentications/update/';
break;
case "authenticate-B":
data = {
otp: this.$el.find('#B-input').val()
};
url = '/B_authentications/update/';
break;
}
$.ajax({
type: 'POST',
url: url,
data: data,
complete: function (xhr) {
if (xhr.status === 200) that.relocate();
else {
$this.text(text).removeClass("auth-button-disable");
that.handleError(xhr.status);
}
},
dataType: 'json'
});
}
提取您的请求。将您的应用程序逻辑混合到您的视图中只会混淆画面。让我们创建一个
Authentication
模块:var Authentication = (function(Backbone, _) { function whoGoesThere(opts) { opts = _.extend({}, opts, { type : 'POST', dataType: 'json' }); return Backbone.$.ajax(opts); } return { A: function(data) { data = _.extend({}, data, { _method : 'PUT' }); return whoGoesThere({ url : '/A_authentications/update/', data: data }); }, B: function(data) { return whoGoesThere({ url : '/B_authentications/', data: data }); } }; })(Backbone, _);
将您的视图配置为使用函数而不是函数名称来处理事件,将您的值传递给前一个模块上的适当方法,然后将返回的承诺委托给通用处理程序:
events: { 'click [data-fn="authenticate-A"]': function(e) { var promise = Authentication.A({ sms_token : this.$el.find( '#sms-input' ).val() }); this.onAuthentication(e, promise); }, 'click [data-fn="authenticate-B"]': function(e) { var promise = Authentication.B({ otp : this.$el.find( '#B-input' ).val() }); this.onAuthentication(e, promise); } }
处理承诺(这里是 Ajax 对象,但它可以是任何东西)
onAuthentication: function(e, promise) { var $this = $(e.target).closest('[data-fn]') , text = $this.text() , that = this; $this.text( 'Authenticating...' ).addClass("auth-button-disable"); promise.done(function() { that.relocate(); }); promise.fail(function(xhr) { that.handleError(xhr.status); }); promise.always(function() { $this.text(text).removeClass("auth-button-disable"); }); }