【问题标题】:Trying to understand DRY principles in Javascript试图理解 Javascript 中的 DRY 原则
【发布时间】:2015-04-01 23:42:15
【问题描述】:

我目前正在努力提高我的重构技能,我编写了一段代码,其中有两种非常相似的方法,我正在努力简化我臃肿的代码,任何建议都是欢迎。

如您所见,这两种方法非常相似,唯一真正的区别是 POST 到的 URL。

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'

我认为这些可以重构为一种方法或两种更精简的方法,我只是不确定从哪里开始,再次提前感谢。

【问题讨论】:

  • 将两种方法重构为一种方法的关键是确定两者之间的区别是什么,这些区别可以在函数参数中指定,还是从 dom 元素中指定。作为一个起点,似乎 url 和类型是不同的,这些值可以来自
    标签本身,然后您可以使用 jquery 检索。
  • 将 ajax complete 方法与 200 一起使用是多余的。只需使用 done 方法。 complete 方法也已弃用。
  • complete参数is not deprecated的使用。 $.ajax({}).complete(),方法,就是。
  • 这个问题太笼统了。如您所见,有多种方法可以做到这一点。我认为这更适合codereview.stackexchange.com

标签: javascript jquery performance backbone.js dry


【解决方案1】:

你可以有一个函数来生成这些函数:

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'

我认为这甚至可能会引入不必要的复杂性,但它更干燥。

【讨论】:

  • 即使在主干视图中?你在哪里调用生成器函数?
  • 您可以在之前定义 authenticateA 和 authenticateB 函数的地方调用它。如果你用前两个代码块替换你的函数定义,它会产生相同的效果,authenticateA 和 authenticateB 将像以前一样定义。
  • 啊,我明白了。我想我挂断的地方是将 var authDetails 块与所有其他代码所在的 return Backbone.View.extend 块相关联。
【解决方案2】:
  1. 抽象出你的请求。将您的应用程序逻辑混合到您的视图中只会混淆图片。让我们创建一个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, _);
    
  2. 配置您的视图以使用函数而不是函数名称来处理事件,将您的值传递给前一个模块的适当方法,然后将返回的承诺委托给一个通用处理程序:

    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);
        }
    }
    
  3. 处理承诺(这里是 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");        
        });
    }
    

还有一个演示http://jsfiddle.net/s3ydy3u6/1/

【讨论】:

    【解决方案3】:

    尝试(未经测试的代码):

    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'
      });
    },
    

    【讨论】:

    • 如何调用点击事件?
    【解决方案4】:

    您可以只检查身份验证函数中的 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'
            });
    
    
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多