如何拥有默认参数并根据此功能的需要覆盖这些参数?
How to have default params and override those as required in this function?
我正在处理其他一些开发人员的代码,我注意到某些 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
属性 player
和 coach
传递给 init()
以采取一些操作来操纵 DOM
.
我知道的一种方法是我可以更改 init
函数以接受两个 parameters
并更新 handleTeam
以接受两个,依此类推。
init: function(param1, param2) {
handleTeam(param1, param2);
}
这似乎不是最好的方法,因为我以后将无法传递额外的 parameters
,除非我更改上面的代码以接受上面列表中的更多 parameters
.
我的主要目标是使此功能可在其他页面上重复使用,我可以在这些页面上选择默认值或根据需要传递任何 parameters
。
我怎样才能使它具有默认值 parameters
并根据需要覆盖任何页面的默认值?
I think above method is called Closures? Is that correct?
是的,OP 片段中的模式是 "Closure" and its also an "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 个注意事项。
- 私有方法无法访问由 return 值部署的 public 方法。
- 有点难读,因此更难维护。
所以这是我比上面那个更喜欢的模式 |还有 闭包和 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); }
This doesn't seem to be the best method as I won't be able to pass additional params later on unless I change code above to accept more params in the list above.
您可以传递任意数量的 parameters/arguments,而无需事先声明它们(改为使用 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):
"Objects" are not values in JavaScript, and cannot be "passed".
All the values that you are dealing with are references (pointers to objects).
Passing or assigning a reference gives another reference that points to the same object. Of course you can modify the same object through that other reference.
因此,如果您现在仔细查看上面的模型 - 这就是为什么我使用 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>
注意: underscore 或 lodash 等其他库也提供实现此目的的函数。
我正在处理其他一些开发人员的代码,我注意到某些 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
属性 player
和 coach
传递给 init()
以采取一些操作来操纵 DOM
.
我知道的一种方法是我可以更改 init
函数以接受两个 parameters
并更新 handleTeam
以接受两个,依此类推。
init: function(param1, param2) {
handleTeam(param1, param2);
}
这似乎不是最好的方法,因为我以后将无法传递额外的 parameters
,除非我更改上面的代码以接受上面列表中的更多 parameters
.
我的主要目标是使此功能可在其他页面上重复使用,我可以在这些页面上选择默认值或根据需要传递任何 parameters
。
我怎样才能使它具有默认值 parameters
并根据需要覆盖任何页面的默认值?
I think above method is called Closures? Is that correct?
是的,OP 片段中的模式是 "Closure" and its also an "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 个注意事项。
- 私有方法无法访问由 return 值部署的 public 方法。
- 有点难读,因此更难维护。
所以这是我比上面那个更喜欢的模式 |还有 闭包和 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); }
This doesn't seem to be the best method as I won't be able to pass additional params later on unless I change code above to accept more params in the list above.
您可以传递任意数量的 parameters/arguments,而无需事先声明它们(改为使用 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):
"Objects" are not values in JavaScript, and cannot be "passed".
All the values that you are dealing with are references (pointers to objects).
Passing or assigning a reference gives another reference that points to the same object. Of course you can modify the same object through that other reference.
因此,如果您现在仔细查看上面的模型 - 这就是为什么我使用 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>
注意: underscore 或 lodash 等其他库也提供实现此目的的函数。