次のことを考慮してください。
var o1 = {}
var O = function () {
return this
}
var o2 = new O()
var o3 = function() {}
var o4 = [o1, o1]
var output = [
[_.isObject(o1), _.isObject(o2), _.isObject(o3), _.isObject(o4)],
[_.isPlainObject(o1), _.isPlainObject(o2), _.isPlainObject(o3), _.isPlainObject(o4)],
[typeof o1 === 'object', typeof o2 === 'object', typeof o3 === 'object', typeof o4 === 'object'],
[o1 instanceof Array, o2 instanceof Array, o3 instanceof Array, o4 instanceof Array]
]
/* outputs:
[
[true,true,true,true],
[true,false,false,false],
[true,true,false,true],
[false,false,false,true]
]
*/
明らかに、.isObject()
の間に切断があることがわかります。
これ 値がオブジェクトの言語タイプであるかどうかを確認します(例:配列、関数、オブジェクト、正規表現、新しい数値(0)、新しい文字列( '')
.isPlainObject()
:
どの 値がプレーンオブジェクト、つまり、オブジェクトコンストラクターによって作成されたオブジェクトまたはプロトタイプがnullのオブジェクトの場合
そして、私たちの古き良き信頼できる友人typeof x === 'object'
。
私は3つの質問があります:
.isObject
と.isPlainObject
の動作をネイティブの.js型チェックとは異なるものにするという意識的な設計上の決定はありましたか?is*
とまったく同じように動作するネイティブ lodash (または nderscore.js )typeof x === 'object'
関数はありますか?もちろん、typeof
を使い続けることはできますが、構文的には、場所によってはどちらか一方を使用するのは少し奇妙です。たとえば、.isObject
を使用すると、typeof x === 'object' && typeof x !== 'function'
をチェックするときに誤検知が返されます。 .isObject
がすでに存在する場合、関数に対して.isFunction
がtrueを返すことの利点は実際にはわかりません。
typeof
は、何かがオブジェクトであるかどうかとは何の関係もありません。関数、文字列、および{}
には異なるtypeof
があり、それらはすべてオブジェクトです。文字列がファーストクラスのオブジェクトであるのと同じように、関数はもちろんファーストクラスのオブジェクトです。したがって、isObject
は文字列とオブジェクトに対してtrueを返す必要があります。
記録のために、ドキュメントはこれをカバーしています:
値がオブジェクトの言語タイプであるかどうかを確認します。 (例:配列、関数、オブジェクト、正規表現、新しい数値(0)、新しい文字列( ''))
うわー!これは、便利なisObject
メソッドがなくてもテストすることが本当にたくさんあります。公平を期すために、ほとんどの場合typeof
をobject
として返しますが、特にlodashのようなライブラリでの高レベルのメソッドのポイントは、プログラマーがそのナンセンスを忘れることができるようにすることです。
typeof
引数が気になる場合は、typeof
を使用してください。関数ではないオブジェクトを気にする場合は、いくつかのオプションがあります。typeof
andチェック文字列を特別に使用するか、isObject && !isFunction
を使用できます。私は後者を好むでしょう。後者はたまたまあなたが伝えようとしていることを正確に言っているので、それは本当に正しいコーディングです。 「オブジェクト」と言うときに暗黙のうちに関数を意味しないと思う場合は、関数がファーストクラスのオブジェクトであるとは思わないか、コードをそうでない言語にもっと近づけたいと考えています。しかし、関数がファーストクラスのオブジェクトであるという事実を広範に使用して、関数がファーストクラスのオブジェクトである言語をより表現力豊かにするライブラリであることをlodashのせいにすることはできません。
それがあなたの質問の大部分だったと思います。 isPlainObject
の使用例は、「これは単なるデータですか?」という質問に答えることだと思います。または「このコードですか?」したがって、疑似クラス(何かのnew
)として作成されたオブジェクトはカウントされません。
コードが言葉よりも雄弁になることがあります。 lodashからのソースは次のとおりです。
function isObject(value) {
var type = typeof value;
return !!value && (type == 'object' || type == 'function');
}
function isPlainObject(value) {
var Ctor;
if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value) && !isArguments(value)) ||
(!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
return false;
}
var result;
if (lodash.support.ownLast) {
baseForIn(value, function(subValue, key, object) {
result = hasOwnProperty.call(object, key);
return false;
});
return result !== false;
}
baseForIn(value, function(subValue, key) {
result = key;
});
return result === undefined || hasOwnProperty.call(value, result);
}
Lodashのドキュメントによると:
値がオブジェクトの 言語タイプ であるかどうかを確認します。 (例:配列、関数、オブジェクト、正規表現、新しい数値(0)、新しい文字列( ''))
値がプレーンオブジェクト、つまりObjectコンストラクターによって作成されたオブジェクトであるか[[Prototype]]がnullのオブジェクトであるかを確認します。
おそらく、JavaScriptで非常に多くのものがオブジェクトのように機能する場合、isObject
関数の存在は本質的に少し混乱します。重要なアイデアは、JavaScriptの一部の値はプリミティブと見なされ、他の値は本格的なオブジェクトと見なされるということです。文字列、数値、ブール値は、オブジェクトリテラルまたはコンストラクター関数で作成されたオブジェクトとは内部レベルで異なる方法で処理されます(プリミティブは必要に応じて自動的にオブジェクトにキャストされるため、これは実用的な意味を持ちません)。
事実 typeof null === 'object'
おそらく、オブジェクトは参照型であり、オブジェクトの代わりに関数からnullが返されることが多いという事実に起因します。 (CおよびC++でのポインターとNULLPTRの概念に耳を傾ける可能性のある癖。タイプvoid *
には多くの値がありますが、NULLPTRは依然としてポインター型の有効な値と見なされます。)
isObject
とisPlainObject
は余分です。それらは効用関数です。彼らは彼らがタイプとは異なることを知っていたと確信しています。それが彼らが何人かの人々に役立つかもしれない理由です。彼らはおそらく、ネイティブのtypeof
のパフォーマンス、一貫性、および構文は、同じことを行う.typeOf
の作成を保証しないと考えていました。.isObject
がtrueを返すことにした理由は、JSの関数がオブジェクトであるためです。ネイティブのtypeof
がオブジェクトではなく関数を返す場合、これは一種の欺瞞です。 typeof []
は配列を返さず、代わりにオブジェクトを返すという事実によってさらに複雑になります。なぜ、関数のオブジェクトではなく関数を返す必要があるのでしょうか。typeof
と同じ関数を持っているようには見えません。アンダースコアには_.isObject
があり、これはlodash .isObject
と同じです。 lodashの.isFunction
と.isObject
を使用して、ネイティブのtypeof
と同じものを返す独自のtypeof関数を作成できます。