機能過負荷Javascript-最良の実践
-
19-08-2019 - |
質問
何のために偽の機能過負荷Javascript?
しかできない過負荷Javascriptの機能とその他の言語を学んでいます。すれば必要な機能と用途 foo(x)
や foo(x,y,z)
最高のスペック/優先出方法:
- 異なる名前の
- 使用オプション引数のように
y = y || 'default'
- 利用数の引数
- 引数の型をチェック
- またはいかがですか?
解決
パラメータを使用して関数のオーバーロードを行う最良の方法は、引数の長さや型を確認しないことです。型をチェックするだけでコードが遅くなり、配列、null、オブジェクトなどを楽しむことができます。
ほとんどの開発者は、メソッドの最後の引数としてオブジェクトを追加します。このオブジェクトは何でも保持できます。
function foo(a, b, opts) {
// ...
if (opts['test']) { } //if test param exists, do something..
}
foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});
その後、メソッドで必要に応じて処理できます。 [スイッチ、if-elseなど]
他のヒント
私はよくこうします:
C#:
public string CatStrings(string p1) {return p1;}
public string CatStrings(string p1, int p2) {return p1+p2.ToString();}
public string CatStrings(string p1, int p2, bool p3) {return p1+p2.ToString()+p3.ToString();}
CatStrings("one"); // result = one
CatStrings("one",2); // result = one2
CatStrings("one",2,true); // result = one2true
同等のJavaScript:
function CatStrings(p1, p2, p3)
{
var s = p1;
if(typeof p2 !== "undefined") {s += p2;}
if(typeof p3 !== "undefined") {s += p3;}
return s;
};
CatStrings("one"); // result = one
CatStrings("one",2); // result = one2
CatStrings("one",2,true); // result = one2true
この特定の例は、実際にはC#よりもJavaScriptの方がエレガントです。指定されていないパラメーターはjavascriptで「未定義」であり、ifステートメントでfalseと評価されます。ただし、関数定義は、p2とp3がオプションであるという情報を伝えません。大量のオーバーロードが必要な場合、jQueryは、jQuery.ajax(options)などのオブジェクトをパラメーターとして使用することを決定しました。これはオーバーロードに対する最も強力で明確に文書化可能なアプローチであることに同意しますが、1つまたは2つ以上のクイックオプションパラメーターが必要になることはほとんどありません。
編集:Ianの提案に従ってIFテストを変更しました
JavaScriptでは、任意のタイプのパラメーターをいくつでも渡すことができるため、実際の関数のオーバーロードはありません。関数内で引数が渡された回数とその内容を確認する必要があります入力してください。
正解は、JAVASCRIPTにオーバーロードがないことです。
関数内のチェック/切り替えはオーバーロードではありません。
オーバーロードの概念: 一部のプログラミング言語では、関数のオーバーロードまたはメソッドのオーバーロードは、異なる実装で同じ名前の複数のメソッドを作成する機能です。オーバーロードされた関数の呼び出しは、呼び出しのコンテキストに適したその関数の特定の実装を実行し、1つの関数呼び出しがコンテキストに応じて異なるタスクを実行できるようにします。
たとえば、doTask()およびdoTask(object O)はオーバーロードされたメソッドです。後者を呼び出すには、オブジェクトをパラメーターとして渡す必要がありますが、前者はパラメーターを必要とせず、空のパラメーターフィールドで呼び出されます。よくあるエラーは、2番目のメソッドでオブジェクトにデフォルト値を割り当てることです。これは、コンパイラが2つのメソッドのどちらを使用するかわからないため、あいまいな呼び出しエラーになります。
https://en.wikipedia.org/wiki/Function_overloading
推奨される実装はすべて素晴らしいですが、実はJavaScriptのネイティブ実装はありません。
この方法を改善する方法は2つあります:
-
多くの柔軟性を残したい場合は、辞書(連想配列)を渡します
-
オブジェクトを引数として使用し、プロトタイプベースの継承を使用して柔軟性を追加します。
以下に示すように、パラメータタイプを使用して実際のメソッドのオーバーロードを可能にするアプローチがあります。
Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);
編集(2018):2011年に作成されて以来、直接メソッド呼び出しの速度は大幅に向上しましたが、オーバーロードされたメソッドの速度は向上していません。
これは私が推奨するアプローチではありませんが、これらのタイプの問題をどのように解決できるかを考えることは価値のある思考運動です。
さまざまなアプローチのベンチマーク- https://jsperf.com/function-overloading。 Googleでは、 ChromeのV8 では、 16.0(beta)の時点で、関数のオーバーロード(型を考慮)が 13倍遅くになることが示されています。
オブジェクト({x: 0, y: 0}
)を渡すだけでなく、必要に応じてCのアプローチを取り、それに応じてメソッドに名前を付けることもできます。たとえば、Vector.AddVector(vector)、Vector.AddIntegers(x、y、z、...)およびVector.AddArray(integerArray)。インスピレーションの命名については、OpenGLなどのCライブラリをご覧ください。
編集:オブジェクトを渡し、'param' in arg
とarg.hasOwnProperty('param')
の両方を使用してオブジェクトをテストするためのベンチマークを追加しました。関数のオーバーロードは、オブジェクトを渡してチェックするよりもはるかに高速ですプロパティ(少なくともこのベンチマークでは)。
設計の観点から、関数のオーバーロードは、オーバーロードされたパラメーターが同じアクションに対応する場合にのみ有効または論理的です。そのため、特定の詳細にのみ関係する基本的な方法があるべきであるという理由があります。そうでなければ、不適切な設計選択を示す可能性があります。したがって、データをそれぞれのオブジェクトに変換することにより、関数のオーバーロードの使用を解決することもできます。もちろん、名前を印刷するだけの場合は手の込んだデザインを作成する必要はありませんが、フレームワークやライブラリのデザインにはそのような考えが正当化されるため、問題の範囲を考慮する必要があります。
私の例はRectangleの実装に由来しています。したがって、DimensionとPointに言及しています。おそらく、RectangleはGetRectangle()
メソッドをDimension
およびPoint
プロトタイプに追加でき、関数のオーバーロードの問題はソートされます。そして、プリミティブはどうですか?さて、オブジェクトには<=>メソッドがあるため、引数の長さは有効なテストになりました。
function Dimension() {}
function Point() {}
var Util = {};
Util.Redirect = function (args, func) {
'use strict';
var REDIRECT_ARGUMENT_COUNT = 2;
if(arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
return null;
}
for(var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
var argsIndex = i-REDIRECT_ARGUMENT_COUNT;
var currentArgument = args[argsIndex];
var currentType = arguments[i];
if(typeof(currentType) === 'object') {
currentType = currentType.constructor;
}
if(typeof(currentType) === 'number') {
currentType = 'number';
}
if(typeof(currentType) === 'string' && currentType === '') {
currentType = 'string';
}
if(typeof(currentType) === 'function') {
if(!(currentArgument instanceof currentType)) {
return null;
}
} else {
if(typeof(currentArgument) !== currentType) {
return null;
}
}
}
return [func.apply(this, args)];
}
function FuncPoint(point) {}
function FuncDimension(dimension) {}
function FuncDimensionPoint(dimension, point) {}
function FuncXYWidthHeight(x, y, width, height) { }
function Func() {
Util.Redirect(arguments, FuncPoint, Point);
Util.Redirect(arguments, FuncDimension, Dimension);
Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
}
Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);
最良の方法は、実際に関数と引数に依存します。さまざまな状況で、それぞれのオプションは良いアイデアです。通常、これらのいずれかが機能するまで、次の順序で試します。
-
y = yのようなオプションの引数を使用する|| 「デフォルト」。これを行うことができれば便利ですが、実際には常に機能するとは限りません。 0 / null / undefinedが有効な引数になる場合。
-
引数の数を使用。最後のオプションと同様ですが、#1が機能しない場合に機能する場合があります。
-
引数のタイプの確認。これは、引数の数が同じ場合に機能します。型を確実に判別できない場合は、異なる名前を使用する必要がある場合があります。
-
最初に別の名前を使用する。他のオプションが機能しない場合、実用的でない場合、または他の関連機能との整合性のために、これを行う必要がある場合があります。
すれば必要な機能と用途foo(x)とfoo(x,y,z)の最/優先となり得る。
の問題はJavaScriptなネイティブの支援方法の過負荷.そのため、トヨタグループをはじめ、世界の/を解析し二つ以上の機能が同じ名前とかなることを考えてみてくださの定義関数を上書きるものとする。
一つの方法と思うのは、ほとんどの場合です。
きっき方法
function foo(x)
{
}
ではなく過負荷方法 ではではできないjavascript できる新しいメソッドを定義し
fooNew(x,y,z)
{
}
その後の変更は1機能しない。
function foo(arguments)
{
if(arguments.length==2)
{
return fooNew(arguments[0], arguments[1]);
}
}
だくなどの過負荷を認識するメソッドを使用 switch
だけではな if-else
記述です。
(詳細)
PS:上記のリンクは私のコメント(スタイル用のhtmlタグを追加。
ベストプラクティスについてはわかりませんが、次のようにします。
/*
* Object Constructor
*/
var foo = function(x) {
this.x = x;
};
/*
* Object Protoype
*/
foo.prototype = {
/*
* f is the name that is going to be used to call the various overloaded versions
*/
f: function() {
/*
* Save 'this' in order to use it inside the overloaded functions
* because there 'this' has a different meaning.
*/
var that = this;
/*
* Define three overloaded functions
*/
var f1 = function(arg1) {
console.log("f1 called with " + arg1);
return arg1 + that.x;
}
var f2 = function(arg1, arg2) {
console.log("f2 called with " + arg1 + " and " + arg2);
return arg1 + arg2 + that.x;
}
var f3 = function(arg1) {
console.log("f3 called with [" + arg1[0] + ", " + arg1[1] + "]");
return arg1[0] + arg1[1];
}
/*
* Use the arguments array-like object to decide which function to execute when calling f(...)
*/
if (arguments.length === 1 && !Array.isArray(arguments[0])) {
return f1(arguments[0]);
} else if (arguments.length === 2) {
return f2(arguments[0], arguments[1]);
} else if (arguments.length === 1 && Array.isArray(arguments[0])) {
return f3(arguments[0]);
}
}
}
/*
* Instantiate an object
*/
var obj = new foo("z");
/*
* Call the overloaded functions using f(...)
*/
console.log(obj.f("x")); // executes f1, returns "xz"
console.log(obj.f("x", "y")); // executes f2, returns "xyz"
console.log(obj.f(["x", "y"])); // executes f3, returns "xy"
これを試したばかりですが、おそらくあなたのニーズに合っています。 引数の数に応じて、異なる関数にアクセスできます。最初に呼び出すときに初期化します。 そして、関数マップはクロージャーに隠されています。
TEST = {};
TEST.multiFn = function(){
// function map for our overloads
var fnMap = {};
fnMap[0] = function(){
console.log("nothing here");
return this; // support chaining
}
fnMap[1] = function(arg1){
// CODE here...
console.log("1 arg: "+arg1);
return this;
};
fnMap[2] = function(arg1, arg2){
// CODE here...
console.log("2 args: "+arg1+", "+arg2);
return this;
};
fnMap[3] = function(arg1,arg2,arg3){
// CODE here...
console.log("3 args: "+arg1+", "+arg2+", "+arg3);
return this;
};
console.log("multiFn is now initialized");
// redefine the function using the fnMap in the closure
this.multiFn = function(){
fnMap[arguments.length].apply(this, arguments);
return this;
};
// call the function since this code will only run once
this.multiFn.apply(this, arguments);
return this;
};
テストします。
TEST.multiFn("0")
.multiFn()
.multiFn("0","1","2");
JavaScriptには関数のオーバーロードオプションがないため、代わりにオブジェクトを使用できます。必要な引数が1つまたは2つある場合は、それらをオプションオブジェクトから分離しておくことをお勧めします。値がオプションオブジェクトに渡されなかった場合に、オプションオブジェクトと値をデフォルト値に使用する方法の例を次に示します。
function optionsObjectTest(x, y, opts) {
opts = opts || {}; // default to an empty options object
var stringValue = opts.stringValue || "string default value";
var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;
return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";
}
こちらは、オプションオブジェクトの使用方法の例です
javascriptで関数のオーバーロードを行う方法はありません。
そのため、typeof()
メソッドではなく、次のようにすることをお勧めします
オーバーロードを偽装する複数の関数。
function multiTypeFunc(param)
{
if(typeof param == 'string') {
alert("I got a string type parameter!!");
}else if(typeof param == 'number') {
alert("I got a number type parameter!!");
}else if(typeof param == 'boolean') {
alert("I got a boolean type parameter!!");
}else if(typeof param == 'object') {
alert("I got a object type parameter!!");
}else{
alert("error : the parameter is undefined or null!!");
}
}
がんばって!
はじめに
これまでのところ、非常に多くの回答を読むと、誰もが頭痛の種になります。概念を理解しようとする人は、次の前提条件を知る必要があります。
Function overloading Definition
、 Function Length property
、 Function argument property
Function overloading
の最も単純な形式は、渡される引数の数に基づいて関数が異なるタスクを実行することを意味します。特に、TASK1、TASK2、TASK3は以下で強調表示されており、同じ関数arguments
に渡されるfooYo
の数に基づいて実行されています。
// if we have a function defined below
function fooYo(){
// do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things
fooYo(); // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3
注 -JSは、関数のオーバーロードの組み込み機能を提供しません。
代替
John E Resig(JSの作成者)は、上記の前提条件を使用して、関数のオーバーロードを実装する機能を実現する代替手段を指摘しました。
以下のコードは、if-else
またはswitch
ステートメントを使用することにより、単純だが単純なアプローチを使用しています。
-
argument-length
プロパティを評価します。 - 値が異なると、異なる関数が呼び出されます。
var ninja = {
whatever: function() {
switch (arguments.length) {
case 0:
/* do something */
break;
case 1:
/* do something else */
break;
case 2:
/* do yet something else */
break;
//and so on ...
}
}
}
別の手法は、はるかにクリーンで動的です。この手法のハイライトは、addMethod
汎用関数です。
-
同じ名前ではあるが異なる機能を持つオブジェクトに異なる関数を追加するために使用される関数
object
を定義します。 -
の下の
name
関数は、3つのparamsオブジェクト名fn
、関数名var old
、および呼び出される関数function
を受け入れます。 - 内部
ninja.whatever(x)
定義x
は、閉鎖-保護バブルの助けによって保存されている以前の<=>への参照を保存します。
function addMethod(object, name, fn) {
var old = object[name];
object[name] = function(){
if (fn.length == arguments.length)
return fn.apply(this, arguments)
else if (typeof old == 'function')
return old.apply(this, arguments);
};
};
- デバッガを使用してコードフローを理解します。
- below the <=>は3つの関数を追加します。これらの関数は、<=>を使用して呼び出されたときに引数の数が<=>であり、空白または1つ以上が< =>関数。
var ninja = {};
debugger;
addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") });
ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(3);
これにアプローチする別の方法は、特殊変数 arguments を使用することです。これは実装です:
function sum() {
var x = 0;
for (var i = 0; i < arguments.length; ++i) {
x += arguments[i];
}
return x;
}
このコードを次のように変更できます:
function sum(){
var s = 0;
if (typeof arguments[0] !== "undefined") s += arguments[0];
.
.
.
return s;
}
これをチェックしてください。とてもかっこいいです。 http://ejohn.org/blog/javascript-method-overloading/ Javascriptをトリックして、次のような呼び出しを行えるようにします。
var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name
この投稿にはすでに多くの異なるソリューションが含まれているので、別のソリューションを投稿すると思いました。
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
function overload() {
var functions = arguments;
var nroffunctionsarguments = [arguments.length];
for (var i = 0; i < arguments.length; i++) {
nroffunctionsarguments[i] = arguments[i].length;
}
var unique = nroffunctionsarguments.filter(onlyUnique);
if (unique.length === arguments.length) {
return function () {
var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
return functions[indexoffunction].apply(this, arguments);
}
}
else throw new TypeError("There are multiple functions with the same number of parameters");
}
これは以下に示すように使用できます:
var createVector = overload(
function (length) {
return { x: length / 1.414, y: length / 1.414 };
},
function (a, b) {
return { x: a, y: b };
},
function (a, b,c) {
return { x: a, y: b, z:c};
}
);
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));
この解決策は完璧ではありませんが、どうすればそれができるかを示すだけです。
John Resigの「addMethod」を使用できます。このメソッドを使用すると、<!> quot; overload <!> quot;引数カウントに基づくメソッド。
// addMethod - By John Resig (MIT Licensed)
function addMethod(object, name, fn){
var old = object[ name ];
object[ name ] = function(){
if ( fn.length == arguments.length )
return fn.apply( this, arguments );
else if ( typeof old == 'function' )
return old.apply( this, arguments );
};
}
キャッシュを使用して関数のバリエーションを保持するこのメソッドの代替も作成しました。 相違点はこちら
// addMethod - By Stavros Ioannidis
function addMethod(obj, name, fn) {
obj[name] = obj[name] || function() {
// get the cached method with arguments.length arguments
var method = obj[name].cache[arguments.length];
// if method exists call it
if ( !! method)
return method.apply(this, arguments);
else throw new Error("Wrong number of arguments");
};
// initialize obj[name].cache
obj[name].cache = obj[name].cache || {};
// Check if a method with the same number of arguments exists
if ( !! obj[name].cache[fn.length])
throw new Error("Cannot define multiple '" + name +
"' methods with the same number of arguments!");
// cache the method with fn.length arguments
obj[name].cache[fn.length] = function() {
return fn.apply(this, arguments);
};
}
転送パターン= <!> gt; JSオーバーロードのベストプラクティス
3番目の<!> ampから名前が作成される別の関数に転送します。 4番目のポイント:
- 引数の数の使用
- 引数のタイプの確認
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)
ケースの申請:
function foo(){
return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
}
//------Assuming that `x` , `y` and `z` are String when calling `foo` .
/**-- for : foo(x)*/
function foo_1_string(){
}
/**-- for : foo(x,y,z) ---*/
function foo_3_string_string_string(){
}
その他の複雑なサンプル:
function foo(){
return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
}
/** one argument & this argument is string */
function foo_1_string(){
}
//------------
/** one argument & this argument is object */
function foo_1_object(){
}
//----------
/** two arguments & those arguments are both string */
function foo_2_string_string(){
}
//--------
/** Three arguments & those arguments are : id(number),name(string), callback(function) */
function foo_3_number_string_function(){
let args=arguments;
new Person(args[0],args[1]).onReady(args[3]);
}
//--- And so on ....
100行のJSでの動的多型による関数のオーバーロード
- VanillaJS、外部依存関係なし
- フルブラウザサポート- Array.prototype .slice 、 Object.prototype .toString
- 1114バイトuglify'd / 744バイトg-zip圧縮
これは、isFn
、isArr
などの型チェック機能を含む、より大きなコード本体からのものです。以下のVanillaJSバージョンは、すべての外部依存関係を削除するために作り直されましたが、.add()
呼び出しで使用する独自の型チェック関数を定義する必要があります。
注:これは自己実行機能であるため(クロージャー/クローズスコープを使用できます)、したがってwindow.overload
ではなくfunction overload() {...}
に割り当てられます。
window.overload = function () {
"use strict"
var a_fnOverloads = [],
_Object_prototype_toString = Object.prototype.toString
;
function isFn(f) {
return (_Object_prototype_toString.call(f) === '[object Function]');
} //# isFn
function isObj(o) {
return !!(o && o === Object(o));
} //# isObj
function isArr(a) {
return (_Object_prototype_toString.call(a) === '[object Array]');
} //# isArr
function mkArr(a) {
return Array.prototype.slice.call(a);
} //# mkArr
function fnCall(fn, vContext, vArguments) {
//# <ES5 Support for array-like objects
//# See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Browser_compatibility
vArguments = (isArr(vArguments) ? vArguments : mkArr(vArguments));
if (isFn(fn)) {
return fn.apply(vContext || this, vArguments);
}
} //# fnCall
//#
function registerAlias(fnOverload, fn, sAlias) {
//#
if (sAlias && !fnOverload[sAlias]) {
fnOverload[sAlias] = fn;
}
} //# registerAlias
//#
function overload(vOptions) {
var oData = (isFn(vOptions) ?
{ default: vOptions } :
(isObj(vOptions) ?
vOptions :
{
default: function (/*arguments*/) {
throw "Overload not found for arguments: [" + mkArr(arguments) + "]";
}
}
)
),
fnOverload = function (/*arguments*/) {
var oEntry, i, j,
a = arguments,
oArgumentTests = oData[a.length] || []
;
//# Traverse the oArgumentTests for the number of passed a(rguments), defaulting the oEntry at the beginning of each loop
for (i = 0; i < oArgumentTests.length; i++) {
oEntry = oArgumentTests[i];
//# Traverse the passed a(rguments), if a .test for the current oArgumentTests fails, reset oEntry and fall from the a(rgument)s loop
for (j = 0; j < a.length; j++) {
if (!oArgumentTests[i].tests[j](a[j])) {
oEntry = undefined;
break;
}
}
//# If all of the a(rgument)s passed the .tests we found our oEntry, so break from the oArgumentTests loop
if (oEntry) {
break;
}
}
//# If we found our oEntry above, .fn.call its .fn
if (oEntry) {
oEntry.calls++;
return fnCall(oEntry.fn, this, a);
}
//# Else we were unable to find a matching oArgumentTests oEntry, so .fn.call our .default
else {
return fnCall(oData.default, this, a);
}
} //# fnOverload
;
//#
fnOverload.add = function (fn, a_vArgumentTests, sAlias) {
var i,
bValid = isFn(fn),
iLen = (isArr(a_vArgumentTests) ? a_vArgumentTests.length : 0)
;
//#
if (bValid) {
//# Traverse the a_vArgumentTests, processinge each to ensure they are functions (or references to )
for (i = 0; i < iLen; i++) {
if (!isFn(a_vArgumentTests[i])) {
bValid = _false;
}
}
}
//# If the a_vArgumentTests are bValid, set the info into oData under the a_vArgumentTests's iLen
if (bValid) {
oData[iLen] = oData[iLen] || [];
oData[iLen].push({
fn: fn,
tests: a_vArgumentTests,
calls: 0
});
//#
registerAlias(fnOverload, fn, sAlias);
return fnOverload;
}
//# Else one of the passed arguments was not bValid, so throw the error
else {
throw "poly.overload: All tests must be functions or strings referencing `is.*`.";
}
}; //# overload*.add
//#
fnOverload.list = function (iArgumentCount) {
return (arguments.length > 0 ? oData[iArgumentCount] || [] : oData);
}; //# overload*.list
//#
a_fnOverloads.push(fnOverload);
registerAlias(fnOverload, oData.default, "default");
return fnOverload;
} //# overload
//#
overload.is = function (fnTarget) {
return (a_fnOverloads.indexOf(fnTarget) > -1);
} //# overload.is
return overload;
}();
使用法:
呼び出し元は、overload()
の戻り値に変数を割り当てることにより、オーバーロードされた関数を定義します。連鎖のおかげで、追加のオーバーロードを連続して定義できます。
var myOverloadedFn = overload(function(){ console.log("default", arguments) })
.add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
.add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;
fn
への単一のオプション引数は、<!> quot; default <!> quot;を定義します。署名を特定できない場合に呼び出す関数。 function
の引数は次のとおりです。
-
a_vArgumentTests
:Array
オーバーロードの定義; -
arguments
:true
で実行するテストを定義するsAlias
sのstring
。各myOverloadedFn.noArgs()
は単一の引数を受け入れ、引数が有効かどうかに基づいてvArgumentTests
thyを返します; -
integer
(オプション):float
オーバーロード関数に直接アクセスするエイリアスを定義します(<=>)。 <=>は、その関数を直接呼び出し、引数の動的な多態性テストを回避します。
この実装では、実際には<=>への2番目の<=>引数がカスタム型を定義するため、従来の関数のオーバーロード以上のものを実際に使用できます。したがって、タイプだけでなく、範囲、値、または値のコレクションに基づいて引数をゲートできます!
<=>の145行のコードを見ると、各署名は、渡された<=>の数で分類されていることがわかります。これは、実行するテストの数を制限するために行われます。また、コールカウントも追跡します。いくつかの追加コードを使用すると、オーバーロードされた関数の配列を並べ替えて、より一般的に呼び出される関数を最初にテストし、パフォーマンスの向上の尺度を追加することができます。
今、いくつかの注意事項があります... Javascriptが緩やかに入力されているため、<=>は<=>などとして検証される可能性があるため、<=>に注意する必要があります。
JSCompress.comバージョン(1114バイト、744バイトg-zip圧縮):
window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<v.length;s++){for(r=v[s],t=0;t<u.length;t++)if(!v[s].tests[t](u[t])){r=void 0;break}if(r)break}return r?(r.calls++,g(r.fn,this,u)):g(p.default,this,u)};return q.add=function(r,s,t){var u,v=b(r),w=d(s)?s.length:0;if(v)for(u=0;u<w;u++)b(s[u])||(v=_false);if(v)return p[w]=p[w]||[],p[w].push({fn:r,tests:s,calls:0}),h(q,r,t),q;throw'poly.overload: All tests must be functions or strings referencing `is.*`.'},q.list=function(r){return 0<arguments.length?p[r]||[]:p},l.push(q),h(q,p.default,'default'),q}var l=[],m=Object.prototype.toString;return k.is=function(n){return-1<l.indexOf(n)},k}();
ポリフィルなしでECMAScript 2018で関数のオーバーロードを実行したり、変数の長さやタイプなどをチェックしたりできます。 スプレッド構文。
function foo(var1, var2, opts){
// set default values for parameters
const defaultOpts = {
a: [1,2,3],
b: true,
c: 0.3289,
d: "str",
}
// merge default and passed-in parameters
// defaultOpts must go first!
const mergedOpts = {...defaultOpts, ...opts};
// you can now refer to parameters like b as mergedOpts.b,
// or just assign mergedOpts.b to b
console.log(mergedOpts.a);
console.log(mergedOpts.b);
console.log(mergedOpts.c);
console.log(mergedOpts.d);
}
// the parameters you passed in override the default ones
// all JS types are supported: primitives, objects, arrays, functions, etc.
let var1, var2="random var";
foo(var1, var2, {a: [1,2], d: "differentString"});
// parameter values inside foo:
//a: [1,2]
//b: true
//c: 0.3289
//d: "differentString"
スプレッド構文とは
ECMAScriptプロポーザルのレスト/スプレッドプロパティ(ステージ4)は、スプレッドプロパティをオブジェクトリテラルに追加します。提供されたオブジェクトから新しいオブジェクトに列挙可能なプロパティをコピーします。 mdnの詳細
注:オブジェクトリテラルのスプレッド構文は、EdgeおよびIEでは機能せず、実験的な機能です。 ブラウザの互換性を参照
最初のオプションは、非常に複雑なコード設定で思いついたものなので、本当に注目に値します。だから、私の答えは
- 最初に別の名前を使用する
少し重要なヒントがありますが、名前はコンピューターでは異なるように見えますが、あなたではないはずです。 func、func1、func2のようなオーバーロード関数に名前を付けます。
ことを義務付けられているものと思うもニーズの別のエントリがかりではないように思え方を読み込みます。の使用を直ちに呼び出された機能表現(IIFE)と組み合わせて使用することの閉鎖およびインライン機能するための機能過負荷.考え、以下の(逆に)例:
var foo;
// original 'foo' definition
foo = function(a) {
console.log("a: " + a);
}
// define 'foo' to accept two arguments
foo = (function() {
// store a reference to the previous definition of 'foo'
var old = foo;
// use inline function so that you can refer to it internally
return function newFoo(a,b) {
// check that the arguments.length == the number of arguments
// defined for 'newFoo'
if (arguments.length == newFoo.length) {
console.log("a: " + a);
console.log("b: " + b);
// else if 'old' is a function, apply it to the arguments
} else if (({}).toString.call(old) === '[object Function]') {
old.apply(null, arguments);
}
}
})();
foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1
このIIFEを作成し、地域の範囲を定義するのprivate変数 old
アカウントへの参照を初期定義の機能 foo
.この機能はそれを返しますインライン機能 newFoo
このログの内容の両方の引数がすでに正確に二つの引数 a
や b
または通話を old
機能の場合 arguments.length !== 2
.このパターンを繰り返すことができますので何度でも与える一変数の異なる機能defitions.
JavaScriptは型付けされていない言語であり、paramsの数に関してメソッド/関数をオーバーロードするのが理にかなっていると思います。したがって、パラメーターが定義されているかどうかを確認することをお勧めします。
myFunction = function(a, b, c) {
if (b === undefined && c === undefined ){
// do x...
}
else {
// do y...
}
};
2017年7月現在、一般的な手法は次のとおりです。関数内で型チェックも実行できることに注意してください。
function f(...rest){ // rest is an array
console.log(rest.length);
for (v of rest) if (typeof(v)=="number")console.log(v);
}
f(1,2,3); // 3 1 2 3
ユースケースでは、これはES6
で取り組む方法です(すでに2017年の終わりなので):
const foo = (x, y, z) => {
if (y && z) {
// Do your foo(x, y, z); functionality
return output;
}
// Do your foo(x); functionality
return output;
}
明らかに、これを任意の量のパラメーターで動作するように調整し、それに応じて条件ステートメントを変更することができます。
関数のオーバーロードでは、このようなことが可能です。
function addCSS(el, prop, val) {
return {
2: function() {
// when two arguments are set
// now prop is an oject
for (var i in prop) {
el.style[i] = prop[i];
}
},
3: function() {
// when three arguments are set
el.style[prop] = val;
}
}[arguments.length]();
}
// usage
var el = document.getElementById("demo");
addCSS(el, "color", "blue");
addCSS(el, {
"backgroundColor": "black",
"padding": "10px"
});
この問題を解決するために over.js を作成したのは非常にエレガントな方法です。できること:
var obj = {
/**
* Says something in the console.
*
* say(msg) - Says something once.
* say(msg, times) - Says something many times.
*/
say: Over(
function(msg$string){
console.info(msg$string);
},
function(msg$string, times$number){
for (var i = 0; i < times$number; i++) this.say(msg$string);
}
)
};
だから、javascript忍者の秘密で見つけたものを行うこの方法が本当に好きでした
function addMethod(object,name,fn){
var old = object[name];
object[name] = function(){
if (fn.length == arguments.length){
return fn.apply(this,arguments);
} else if(typeof old == 'function'){
return old.apply(this,arguments);
}
}
}
次に、addMethodを使用して、オーバーロードされた関数を任意のオブジェクトに追加します。私にとってこのコードの主な混乱は、fn.length == arguments.lengthの使用でした-fn.lengthは予想されるパラメーターの数であり、arguments.lengthは実際に呼び出されるパラメーターの数だからです。関数。無名関数に引数がないのは、javascriptで任意の数の引数を渡すことができ、言語が寛容であるためです。
これはどこでも使用できるので気に入っています。この関数を作成し、必要なコードベースでメソッドを使用するだけです。
また、複雑なコードの記述を開始すると読みにくくなる、途方もなく大きなif / switchステートメントを避けることができます(受け入れられた答えはこれになります)。
短所に関しては、最初はコードが少しあいまいだと思いますが、他のことはわかりませんか?
オーバーロードのようなアプローチの便利な例を共有したいと思います。
function Clear(control)
{
var o = typeof control !== "undefined" ? control : document.body;
var children = o.childNodes;
while (o.childNodes.length > 0)
o.removeChild(o.firstChild);
}
使用法: Clear(); //すべてのドキュメントをクリアします
Clear(myDiv); // myDivによって参照されるパネルをクリアします
@AntouanKのアプローチが好きです。私はしばしば、パラメータやタイプが異なる数の関数を提供していることに気付きます。時には彼らは命令に従わない。パラメーターのタイプを探してマッピングするために使用します:
findUDPServers: function(socketProperties, success, error) {
var fqnMap = [];
fqnMap['undefined'] = fqnMap['function'] = function(success, error) {
var socketProperties = {name:'HELLO_SERVER'};
this.searchServers(socketProperties, success, error);
};
fqnMap['object'] = function(socketProperties, success, error) {
var _socketProperties = _.merge({name:'HELLO_SERVER'}, socketProperties || {});
this.searchServers(_socketProperties, success, error);
};
fqnMap[typeof arguments[0]].apply(this, arguments);
}