Javascriptパノプティコン

私がたまたまJavascriptで書いている間に、jsとその仕様は秘密の底を持つ箱であるというイメージを得ました。 突然魔法があなたの家をノックすると、秘密はないように見えることがあります。箱が開き、悪魔が飛び出し、家でブルースを演奏し、すぐに箱の中に隠れます。 後で、理由がわかります。テーブルが導かれ、ボックスが5度傾いて、悪魔を引き起こしました。 それ以来、これがボックスの機能なのか、それとも強力なテープで巻き上げた方が良いのかわかりません。 次回まで、ボックスが新しいストーリーを提供するまで続きます。







そして、あなたがそのような物語をそれぞれ書き留めれば、あなたが共有したい小さな記事を手に入れるかもしれません。







「ボイドの合計」



.join()



メソッドを使用して配列を文字列にマージすると、空、未定義、長さゼロの配列が空の文字列に変換されます。 そして、これは、配列に配置されている場合にのみ当てはまります。







 [void 0, null, []].join("") == false // => true [void 0, null, []].join("") === "" // => true //      . void 0 + "" // => "undefined" null + "" // => "null" [] + "" // => ""
      
      





実際には、この動作を使用して、真に空のデータを除外できます。







 var isEmpty = (a, b, c) => { return ![a, b, c].join(""); } var isEmpty = (...rest) => { return !rest.join(""); } isEmpty(void 0, [], null) // => true isEmpty(void 0, [], null, 0) // => false isEmpty(void 0, [], null, {}) // => false.        //  ,      var isEmpty = (arg) => { return !([arg] + ""); } isEmpty(null) // => true isEmpty(void 0) // => true isEmpty(0) // => false
      
      





「奇妙な数字」



typeof



演算子を使用してNaN



およびInfinity



タイプを決定しようとすると、結果として「number」が返されます







 typeof NaN // => "number" typeof Infinity// => "number" !isNaN(Infinity) // => true
      
      





ユーモアは、NaNが "Not-A-Number"の略であり、 Infinity



を数字と呼ぶことはほとんどできないということです。







次に、数字を決定する方法は? それらの有限性をチェックしてください!







 function isNumber(n) { return isFinite(n); } isNumber(parseFloat("mr. Number")) // => false isNumber(0) // => true isNumber("1.2") // => true isNumber("abc") // => false isNumber(1/0) // => false
      
      





「足を撮影するには、オブジェクトを取ります」



javascriptの場合、 Object



は非常に最初のデータ構造の1つであり、同時に、私の意見では複雑さの王でもあります。







たとえば、ループでハッシュテーブルとして使用されるオブジェクトをバイパスする場合、反復可能なプロパティが独自のものであることを確認することをお勧めします。







そうしないと、プロトタイプ拡張からのプロパティが反復に含まれる場合があります。







 Object.prototype.theThief = " "; Object.prototype.herLover = ""; var obj = { theCook: " ", hisWife: "" }; for (var prop in obj) { obj[prop]; //  : " ", "", " ", "" if (!obj.hasOwnProperty(prop)) continue; obj[prop]; //  : " ", "" }
      
      





一方、 Object



はプロトタイプ継承なしで作成できます。







 //      var obj = Object.create(null); obj.key_a = "value_a"; obj.hasOwnProperty("key_a") // =>  .
      
      





「ちょっとキャップ、なぜこれが必要なの?」







このようなハッシュでは、継承されたキーはなく、独自のキーのみが使用されます(仮想メモリの節約)。 そのため、ユーザーが独自のデータコレクションを転送できるライブラリ用のAPIを設計するとき、それを簡単に忘れてしまいます。







また、この場合、入力データを制御できないため、オブジェクト内の独自のキーをチェックする普遍的な方法が必要です。







最初の方法。 すべてのキーを取得できます。 ループ内でindexOf



を実行する場合は最適ではありません。配列の余分な走査です。







 Object.keys(obj); // => ["key_a"]
      
      





二番目の方法 。 変更されたコンテキストでhasOwnProperty



メソッドを呼び出します







 Object.prototype.hasOwnProperty.call(obj, "key_a") // => true
      
      





ここが完璧な方法のようです。 しかし、Internet Explorer。







 //   IE //     var obj = Object.create(null); obj[0] = "a"; obj[1] = "b"; obj[2] = "c"; Object.prototype.hasOwnProperty.call(obj, 1); // => false Object.prototype.hasOwnProperty.call(obj, "1"); // => false Object.keys(obj); // => ["0", "1", "2"] obj.a = 1; Object.prototype.hasOwnProperty.call(obj, 1); // => true Object.prototype.hasOwnProperty.call(obj, "1"); // => true //        Object obj = Object.create(Object.prototype); obj["2"] = 2; obj.hasOwnProperty("2"); // => false obj.a = "a"; obj.hasOwnProperty("2"); // => true delete obj.a; obj.hasOwnProperty("2"); // => false
      
      





そうは思わなかったObject.create()



、少なくとも1つの小文字が現れるまで、 Object.create()



で作成されたオブジェクトのデジタルキーのチェックを実際に拒否します。







そして、この事実は休日全体を台無しにします。







UPD:

ドミトリー・コロブキンが提案した解決策







UPD:

bingo347は 、「恐竜」用のスクリプトを作成しない場合、 Object.keys(obj)



およびObject.getOwnPropertyNames(obj)



を使用して独自のプロパティを列挙する方が適切であると正しく指摘しました。







ただし、 getOwnPropertyNames



がすべての独自のキー(読み取り不可能なキーも含む)を返すというニュアンスに留意する必要があります。







 Object.keys([1, 2, 3]); // => ["0", "1", "2"] Object.getOwnPropertyNames([1, 2, 3]); // => ["0", "1", "2", "length"]
      
      





「False undefined」



多くの場合、開発者は直接比較により未定義の変数をチェックします







 ((arg) => { return arg === undefined; // => true })();
      
      





同様に、割り当て付き







 (() => { return { "undefined": undefined } })();
      
      





「待ち伏せ」は、未定義を再定義できるという事実にあります







 ((arg) => { var undefined = "Happy debugging m[\D]+s!"; return { "undefined": undefined, "arg": arg, "arg === undefined": arg === undefined, // => false }; })();
      
      





この知識は睡眠を奪います:クロージャー内で未定義をオーバーライドするだけでプロジェクト全体を破壊できることがわかります。







しかし、未定義を比較または割り当てるには、いくつかの信頼できる方法がありvoid



演算子を使用するか、空の変数を宣言することです







 ((arg) => { var undefined = "Happy debugging!"; return { "void 0": void 0, "arg": arg, "arg === void 0": arg === void 0 // => true }; })(); ((arg) => { var undef, undefined = "Happy!"; return { "undef": undef, "arg": arg, "arg === undef": arg === undef // => true }; })();
      
      





「シュレーディンガーの比較」



かつて、同僚は私と興味深い異常を共有しました。







 0 < null; // false 0 > null; // false 0 == null; // false 0 <= null; // true 0 >= null // true
      
      





これは、より少ない比較が数値比較であり、式の両方の部分が数値に変換されるためです。







nullと数値が等しい場合は常にfalseを返します。







数値になった後にnullが+0になることを考慮すると、コンパイラ内部では比較は次のようになります。







 0 < 0; // false 0 > 0; // false 0 == null; // false 0 <= 0; // true 0 >= 0 // true
      
      





数値とブール値の比較



 -1 == false; // => false -1 == true; // => false
      
      





javascriptでは、 Number



Boolean



比較する場合、後者は数値に変換され、その後、Number == Numberが比較されます。







また、 false



が+0にキャストされ、 true



+1にキャストされるため、コンパイラー内では比較は次の形式になります。







 -1 == 0 // => false -1 == 1 // => false
      
      





しかし。







 if (-1) "true"; // => "true" if (0) "false"; // => undefined if (1) "true"; // => "true" if (NaN) "false"; // => undefined if (Infinity) "true" // => "true"
      
      





0とNaNは常にfalseにキャストされるため、他のすべてはtrueです。







配列チェック



JSでは、 Array



Object



を継承し、本質的には数値キーを持つオブジェクトです







 typeof {a: 1}; // => "object" typeof [1, 2, 3]; // => "object" Array.isArray([1, 2, 3]); // => true
      
      





問題は、IE9以降ではArray.isArray()



のみが機能することです。







しかし、別の方法があります







 Object.prototype.toString.call([1, 2, 3]); // => "[object Array]" //  function isArray(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } isArray([1, 2, 3]) // => true
      
      





一般に、 Object.prototype.toString.call(something)



を使用すると、他の多くの型を取得できます。







UPD:

私の意見では、 boldyrev_geneは良い質問をしました:なぜinstanceof



使用しないのですか?







フレームおよび他のウィンドウ内に作成された配列インスタンスには、異なるコンストラクターインスタンスがあります。







 var iframe = document.querySelector("iframe"), IframeArray = iframe.contentWindow.Array; new IframeArray() instanceof Array; // => false Array.isArray(new IframeArray()); // => true Object.prototype.toString.call(new IframeArray()); // => "[object Array]"
      
      





引数は配列ではありません



あまりにも頻繁にそれを忘れてしまい、書き出すことにしました。







 (function fn() { return [ typeof arguments, // => "object" Array.isArray(arguments), // => false Object.prototype.toString.call(arguments) // => "[object Arguments]"; ]; })(1, 2, 3);
      
      





引数は配列ではないため、通常のメソッド.push()



.concat()



などは使用できません。引数をコレクションとして使用する必要がある場合は、解決策があります。







 (function fn() { arguments = Array.prototype.slice.call(arguments, 0); //    return [ typeof arguments, // => "object" Array.isArray(arguments), // => true Object.prototype.toString.call(arguments) // => "[object Array]"; ]; })(1, 2, 3);
      
      





そしてここに...残りは配列です



 (function fn(...rest) { return Array.isArray(rest) // => true. Oh, wait... })(1, 2, 3);
      
      





グローバルにキャッチ。 または、スクリプトランタイムを定義します



たとえば、Webpackを通じて収集された多数のライブラリから同形ライブラリを構築する場合、遅かれ早かれ、スクリプトが実行されている環境を特定する必要が生じます。







また、JSは標準ライブラリのレベルでランタイム環境を決定するメカニズムを提供しないため、laxモードの匿名関数内でポインター動作の機能を使用してフェイントを作成できます。







無名関数では、 this



ポインターはグローバルオブジェクトを参照します。







 function getEnv() { return (function() { var type = Object.prototype.toString.call(this); if (type == "[object Window]") return "browser"; if (type == "[object global]") return "nodejs"; })(); };
      
      





ただし、厳格モードでは、 this



は未定義であり、 this



が道を壊します。 このメソッドは、 global



またはwindow



手動でグローバルに宣言されている場合に関連しwindow



-「トリッキー」ライブラリに対する保護。










ご清聴ありがとうございました! 誰かがこれらのメモが有用で役に立つと思うことを願っています。








All Articles