如何使用默认参数并覆盖此函数中所需的参数?

时间:2017-10-22 17:35:10

标签: javascript jquery closures parameter-passing iife

我正在处理其他一些开发人员的代码,我注意到在某些JavaScript文件中使用了以下模式。

var my_team = function() {
    var handleTeam = function() {
        //do some ajax 
        // and update selected DOM element
    }
    return {
        //main function to initiate the module
        init: function() {
            handleTeam();
        }
    };
}();

jQuery(document).ready(function() {
    my_team.init();
});

我处于JS开发和学习最佳实践的初级阶段。我认为上面的方法称为闭包?这是对的吗?

我想要实现的目标是:

<select name="players" id="player">
    <option>Mark</option>
    <option>Tom</option>
</select>
<select name="coaches" id="coach">
    <option>Mark</option>
    <option>Tom</option>
</select>

我希望能够将HTML id - 属性playercoach传递给init(),以便采取一些操作来操纵DOM

我知道的一种方法是,我可以更改init - 函数以接受两个parameters并更新handleTeam以取两个等等。

init: function(param1, param2) {
    handleTeam(param1, param2);
}

这似乎不是最好的方法,因为我以后无法通过额外的parameters,除非我更改上面的代码以接受更多parameters上面的列表。

我的主要目标是让这个功能在我可以选择默认值的其他页面上重复使用,或者根据需要传递任何parameters

如何让它具有默认parameters并根据需要覆盖任何页面?

1 个答案:

答案 0 :(得分:2)

  

我认为上面的方法叫做闭包?这是对的吗?

是OPs代码段中的模式是"Closure",它也是"Immediately Invoked Function Expression (aka "IIFE")

当您询问最佳做法时,我做了一些微妙的更改来回复此问题。因此,我所执行的内容不那么重要,但更重要的是我如何执行它(请参阅内联注释)。

如果我做对了你想要达到这样的效果(为了说明目的,还在函数体中添加了一些东西):

var myTeam = (function( _sDefault, _oDefault ) { // my_team vs. myTeam? Naming convention for JS is CamelCase!

    // underscore prepended or appended to variable names is common use to show that a variable has private access
    var _handleTeam = function( sDefault, oDefault ) {
        console.log( sDefault );
        console.log( oDefault );
        // "cannot call"/"don't has access" to init() nor updatePlayer() 
    }
    return { // deploy public methods
        init: function( sDefault, oDefault ) {
            if ( !sDefault ) sDefault = _sDefault; // devs write: sDefault = _sDefault || sDefault;
            if ( !oDefault ) oDefault = _oDefault;
            _handleTeam( sDefault, oDefault );
        },
        updatePlayer: function() {
            console.log('updatePlayer');
        }
    };

})( 'default', {default: true} ); // pass values on IIFE

myTeam.init(); // initiate with default values
myTeam.init( 'custom', {default: false, custom: true} ); // initiate with custom values
myTeam.init(); // initiate again with default values
myTeam.updatePlayer();

如果符合您的需求,采用上述设计模式将是完全没问题的。但我可以在这里看到至少2个警告。

  1. 私有方法无权访问由返回值部署的公共方法。
  2. 有点难以阅读,因此难以维护。
  3. 所以这是一个我更喜欢上面的模式还关闭和IIFE

    var myTeam = (function( _sDefault, _oDefault ) {
    
        // make sure that _oDefault can not be modified from outer scope
        _oDefault = $.extend({}, _oDefault); // *
    
        // declare variables with private access
        var _oThis = this, // most devs write "that" instead of "_oThis" like I do, you can see "self" also quite often
            _oBackup = {sDefault: _sDefault, oDefault: $.extend({}, _oDefault)}; // *
        var _handleTeam = function( sDefault, oDefault ) {
            // public methods are now also availabe to private ones
            _oThis.log( sDefault );
            _oThis.log( oDefault );
            return _oThis.updatePlayer();
        }
    
        // declare properties with public access
        this.setDefaults = function( sDefault, oDefault ) {
            if ( typeof sDefault === 'string' )
                _sDefault = sDefault;
            if ( typeof sDefault === 'boolean' )
                _sDefault = _oBackup.sDefault;
            if ( typeof oDefault === 'object' )
                _oDefault = $.extend({}, oDefault); // *
            if ( typeof oDefault === 'boolean' )
                _oDefault = $.extend({}, _oBackup.oDefault); // *
            return this; // make public methods chainable
        }
    
        this.updatePlayer = function() {
            return this.log('updatePlayer'); // make public methods chainable
        }
    
        this.log = function( sLog ) {
            console.log(sLog);
            return this; // make public methods chainable
        }
    
        this.init = function( sDefault, oDefault ) {
            _handleTeam(
                sDefault || _sDefault,
                oDefault || _oDefault
            );
            return this; // make public methods chainable
        }
    
        return this; // deploy everything that has public access
    
    })( 'default', {default: true} ); // set default parameters on IIFE
    
    // our public methods are chainable now
    myTeam.init().log('initiated with default values')
          .init( 'custom', {default: false, custom: true} ).log('initiated with custom values')
          .setDefaults( false, false ).log('reseted to default values')
          .init().log('initiated reseted default values')
          .setDefaults( 'new default', {default: true, newDefault: true} ).log('set new default values')
          .init().log('initiated with new default values');
    
    // *: if you don't know why I'm using  jQuery.extend for objects, feel free to leave a comment and I can explain...
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    另一个问题?

      

    init: function(param1, param2) { handleTeam(param1, param2); }

         

    这似乎不是最好的方法,因为我以后无法传递额外的参数,除非我更改上面的代码以接受上面列表中的更多参数。

    您可以根据需要传递任意数量的参数/参数,而无需事先声明它们(使用arguments代替):

    init: function() {
       console.log(arguments);
       handleTeam(arguments[0], arguments[1], arguments[2]);
       // or you can do it like this as well:
       handleTeam.apply(this, arguments); // 
    }
    myTeam.init( 'yep', 'don't worry', 'works' )
    


    当我一遍又一遍地阅读你的问题时,我猜以下的模型应该是你的方向(或者至少应该能够说明事情如何协同工作)。工作伪代码| 关闭,但没有IIFE

    (function( $ ) { // sure this an IIFE again but thats not essentially to the question at this point
    
      var Team = function() {
        // private
        var _oThis = this,
            _oTeam = {},
            _privateHelper = function() {
              // this function can not be triggered directly from outer scope
              console.log('_privateHelper was called');
              return _oThis; // use _oThis instead of this here!!!
            },
            _get = function( sId, sIdSub ) {
              return _oTeam[sId] && _oTeam[sId][sIdSub] ? _oTeam[sId][sIdSub] : false;
            },
            _set = function( sId, sIdSub, val ) {
              _oTeam[sId][sIdSub] = val;
              return _privateHelper(); 
            };
    
        // public
        this.register = function() {
          for( var i = 0, iLen = arguments.length, sId; i < iLen; ++i ) {
            sId = arguments[i];
            _oTeam[ sId ] = {
              $: $('#' + sId), // #1 cache jQuery collection
              aPerson: [], // #2 cache names of each person
              sSelectedPerson: false // #3 cache name of selected person
            };
            _oTeam[ sId ].$.find('option').each(function( iEach ){
              _oTeam[ sId ].aPerson[ iEach ] = $(this).val(); // #2
            });
            this.updateSelectedPerson( sId ); // #3
          }
          return this; // for chaining | BTW: this === _oThis
        }
    
        this.updateSelectedPerson = function( sId ) {
          if ( _oTeam[ sId ] ) {
            _set(sId, 'sSelectedPerson', _oTeam[ sId ].$.val());
          }
          return this;
        }
    
        this.getSelectedPerson = function( sId ) {
          return _get(sId, 'sSelectedPerson');
        }
    
        this.getPersons = function( sId ) {
          return _get(sId, 'aPerson');
        }
    
        this.update = function( sId ) {
          if ( _oTeam[ sId ] ) {
            console.log(
              'old selected: ' + this.getSelectedPerson( sId ),
              'new selected: ' + this.updateSelectedPerson( sId ).getSelectedPerson( sId )
            );
          }
          return this;
        }
    
        arguments.length && this.register.apply( this, arguments );
        return this; // deploy public properties
      };
    
      $(function(){ // document ready
    
        var oTeam = new Team( 'coach', 'player' ); // would be the same as ...
        // var oTeam = new Team().register( 'coach', 'player' );
        console.log(oTeam.getPersons('coach'));
        console.log(oTeam.getPersons('player'));
        $('select').on('change.team', function(){
          oTeam.update( this.id )
        })
    
      });
    
    })( jQuery ) // pass jQuery on IIFE for making save use of "$"
    <h1 style="font-size:1em;display:inline">select coach and player: </h1>
    
    <select name="players" id="player">
        <option>player Mark</option>
        <option>player Tom</option>
    </select>
    <select name="coaches" id="coach">
        <option>coach Mark</option>
        <option selected>coach Tom</option>
    </select>
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


    处理具有type of object

    的参数

    var oO = {prop: 'save???'},
        oA = [true],
        s  = 'save!',
        i  = 0,
        b  = true,
        fn = function( oO, oA, s, i, b ) {
          // every argument will get a new value
          // lets have a look if this effects the original variable that was passed
          oO.prop = 'nope!';
          oA[0]   = 'oh oh!';
          s       = 'yep save';
          i       = 999;
          b       = false;
        };
    
    fn(oO, oA, s, i, b);
    
                            // initial   -> inner scope -> outer scope
    console.log( oO.prop ); // 'save???' -> 'nope!'     -> 'nope!'
    console.log( oA[0]   ); // true      -> 'oh oh!'    -> 'oh oh'
    console.log( s       ); // 'save!'   -> 'yep save'  -> 'save!'
    console.log( i       ); // 0         -> 999         -> 0
    console.log( b       ); // true      -> false       -> true


    以下是迄今为止我找到的最佳解释(简短,准确,可理解,信用:@newacct):

      

    “对象”不是JavaScript中的值,也不能“传递”   您正在处理的所有值都是引用(指向对象的指针)   传递或分配引用会给出另一个指向同一对象的引用。当然,您可以通过其他参考修改同一个对象。

    https://stackoverflow.com/a/16893072/3931192

    因此,如果您现在仔细查看上面的模型 - 这就是为什么我使用jQuery utility method extend来处理与外部范围有某种关系的对象(这是作为参数传递的情况 - 对吗?)。

    记住它 - 从现在开始永远不要忘记它!如果您是新手,这可以为您节省数小时的麻烦!

    那么如何规避这种行为:

    var oO = {prop: 'make it save now'},
        oA = [true],
        fn = function( oO, oA ) {
          var o = jQuery.extend({}, oO, oA);
          console.log('log#1', o);
          o[0] = 'how save is this?';
          o.prop = 'so save now :)';
          console.log('log#2', o);
        };
    
    fn( oO, oA );
    
    console.log('log#3', oO, oA);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    注意:其他库(如下划线或lodash)也提供了实现此目的的功能。