私はオープンソースプロジェクトから拾い上げた大きなjavascriptドキュメントをリファクタリングしています。多くの関数は一貫性のないreturnステートメントを使用します。ここに私が言っていることの簡単な例を示します:
var func = function(param) {
if (!param) {
return;
}
// do stuff
return true;
}
関数がブール値を返すこともあれば、文字列などを返すこともあります。通常、それらは、条件内の単純なreturn;
ステートメントと一貫性なくペアになっています。
問題は、コードが複雑であることです。多数の一意のRegExマッチを使用し、DOMノードをオンザフライで作成および破棄するパーサーです。上記の例では、return;
ステートメントをreturn false;
に変更できることが予備テストで示されていますが、かなり後になるまで、スクリプトにマイナスの影響(つまり、一部の機能が動作しなくなった)があったことを理解できないかもしれません。
私の質問:空白のreturnステートメントを使用する利点はありますか?これは意図的にこのようにコーディングされているのでしょうか、それとも単に怠け者だったのでしょうか?それらをすべてreturn false;
またはreturn null;
に変更できますか、またはすべての呼び出しを調べて、それらの関数の結果で何をしているかを調べる必要がありますか?
値なしでreturn
を使用すると、値undefined
が返されます。
値がブール値として評価される場合、undefined
はfalse
として機能しますが、たとえば値をfalse
と比較すると、異なる動作が得られます。
var x; // x is undefined
alert(x); // shows "undefined"
alert(!x); // shows "true"
alert(x==false); // shows "false"
したがって、コードは論理的にtrue
またはfalse
ではなくtrue
またはundefined
を返す必要がありますが、単にreturn;
からreturn false;
戻り値の使用方法をチェックせずに。
「ブランクreturn」ステートメントを使用して、呼び出し元の関数に制御を戻す(または何らかの理由で関数の実行を停止する-例:検証など)ことができます。ほとんどの場合、空のreturnステートメントを使用するのは、何らかの検証を行うときです。ただし、関数の実行が停止された理由について何らかのインジケータを設定することは重要です。たとえば、エラーメッセージでDIV要素の「innerText」プロパティを設定します。
上記のコードでは、検証のように見えます。すべてがうまくいった場合、関数は「true」を返します。呼び出し元の関数は戻り値を解析し、それが「true」の場合、ステートメントの次のステップ(呼び出し元の関数内)が実行されるようです。
上記の例では、空の戻り値の代わりに「false」を返すことをお勧めします。そうすれば、他のプログラマーがすべてを統一し、生活を楽にすることができます。
このような不整合を修正できます。ただし、すべての変更を徹底的にテストしてください。コードに加えた各変更をテストすることをお勧めしますが、それがどんなに小さくてもかまいません。
ここで失われる可能性があるのは(例では直接ではありません)、トライステートオブジェクトを持つことができるということです:
var myfunc = function(testparam) {
if (typeof testparam === 'undefined') return;
if (testparam) {
return true;
}
else {
return false;
}
};
var thefirst = myfunc(true)
var thesecond = myfunc(false);
var thelast = myfunc();
alert("type:" + typeof thefirst+" value:"+thefirst);
alert("type:" + typeof thesecond+" value:"+thesecond);
alert("type:" + typeof thelast+" value:"+thelast);
これらの戻り値:
> type:boolean:true
> type:boolean:false
> type:undefined:undefined
注:この例では、nullはfalseを返しますmyfunc(null);
関数を変更すると、return;
およびreturn false;
さまざまなデータ型を出力します。
var test = function (x) {
if (!x) {
return;
}
else {
return false;
}
};
var a = test(true), b = test(false);
console.log(typeof b); // boolean
console.log(typeof a); // undefined
return;
とreturn undefined;
にはまったく違いはありません。両方の関数を呼び出した結果、値undefined
を受け取ります。
(非常に小さいreturn
vs.コードの終わりから抜け落ちますが、コードで検出できるものは何もありません。¹実行がコードの終わりから落ちた関数を呼び出すと、値はundefined
になります。)
"use strict";
// Implicit return of `undefined`
function x() {
return;
}
// Explicit return of `undefined`
function y() {
return undefined;
}
// Execution falls off the end
function z() {
}
console.log(typeof x() === "undefined"); // true
console.log(typeof y() === "undefined"); // true
console.log(typeof z() === "undefined"); // true
Unless、もちろん、何かがundefined
を隠しています。それはまだ悲しいことに可能です(しかし、喜んで、グローバルな範囲ではありません)。そのveryエッジの効いたEdgeの場合、違いがあります:
"use strict";
(function() {
const undefined = 42;
// ^^^^^^^^^^^^^^^---- shadowing `undefined`
// Implicit return of `undefined`
function x() {
return;
}
// Explicit return of `undefined`
function y() {
return undefined;
}
// Execution falls off the end
function z() {
}
console.log(typeof x() === "undefined"); // true, `x` returns the canonical `undefined`
console.log(typeof y() === "undefined"); // false, `y` returns 42
console.log(typeof z() === "undefined"); // true, `z` (effectively) returns the canonical `undefined`
})();
¹return
を使用すると、突然の完了になり、[[Call]]は値付きの通常の完了に変換されます。コードの終わりから落ちるのは、通常の完了です( spec )([[Call]]がundefined
値)。ただし、これも仕様レベルの違いであり、コードで確認できるものではありません。