標準のJavaScriptライブラリに関する考察。 Core.js

1人の子供がすべてをJavaScriptで記述し、クライアントとサーバーは、便利で読みやすいと言った。 その後、彼はもちろんダーキーに連れて行かれました。
-インターネットから

なんで? 興味深いこと-​​JavaScript。 最新のWebおよびフロントエンドの基盤には、代替手段がありません。

JavaScriptは、とりわけ、ここで説明する標準ライブラリです。 標準ライブラリとは、プログラマー側の不必要なアクションがなく、プラットフォーム固有のAPIを含まない、任意のプラットフォーム上に存在するモジュール、コンストラクター、メソッドを意味します。 JavaScriptではなく、それにコンパイルされた言語で記述した場合でも、ほとんどの場合、その標準ライブラリを処理する必要があります。

一般に、Vanilla JavaScript標準ライブラリは優れています。 これは、現行バージョンのECMA-262言語仕様(3からドラフト6)に基づく標準ライブラリだけではありません。APIの一部は、ECMA-402国際化APIなどの個別の仕様に配置されます。 setTimeoutなど、JavaScriptが想像しがたい機能の多くは、Web標準に関連しています。 コンソールはまったく標準化されていません。事実上の標準に依存する必要があります。

しかし、それはそれほど標準的ではなく、どこでも異なります。 古いIEには、 Array#forEach, Function#bind, Object.create 、およびコンソールがなくても、90年の標準ES3ライブラリがすぐに利用できます。たとえば、多くの人がすでに将来を最大限に活用しているNode.jsがありますES6。

サーバーとブラウザの両方で、可能な限り最新であり、必要な機能を実装している、これまでのところ標準化されていない、ユニバーサルで真に標準的なライブラリが必要です。 この記事はcore.jsライブラリーに関するもので、標準的なJavaScriptライブラリーに関する私の考えを実装しています。 さらに、この記事は、最新の標準化された標準JavaScriptライブラリーに関するチートシートであり、その展望についてのメモでもあります。

コンテンツ、または出力で得られるもの:



警告します。マルチブックと最初の章は非常にありふれたものです。すべてを読みたいという欲求はありません-上記の目次、興味のあるセクションまでスクロールしてください。

アプローチ



JavaScriptでライブラリを作成するには、標準と呼ばれる3つの主なアプローチがあります。

最初のものはポリフィールのみ使用、標準化された機能のみです。 時間とともにAPIが壊れないという具体的な信念。 このようなライブラリを使用するには、その知識は必要ありません。対応するAPI言語の知識のみが必要です。 一般的に、ポリフィルは1つの標準またはその一部に限定されます。 たとえば、 es5-shimまたはes6-shim 。 このため、デフォルトで必要な機能を提供するには、複数のポリフィルを接続する必要があります。 内部コンポーネントは相互に複製することが多いため、このセットは数百キロバイトに達することがよくあります。 そして、私が持ちたいすべての機能が標準化されているわけではありません。 他のライブラリと競合する可能性は無視できますが、ライブラリを作成するときに依存関係としてそれらを使用することはありません。

2つ目は、 独自の名前空間にあるユーティリティのセットです 。 モジュラーシステムにエクスポートするか、1つのグローバルオブジェクトを作成します。 たとえば、 UndescoreまたはそのLoDashのフォーク。 通常、それは非常にコンパクトですが、可能性は一連の単純なユーティリティによって制限されます。 ネイティブオブジェクトを拡張せず、変更をロールバックするnoConflictメソッドがしばしば存在するため、他のライブラリとの競合の可能性は最小限です。他のライブラリの安全な依存関係としてここで説明した他のメソッドよりも優れています。

3つ目は、 標準化された機能だけでなく、ネイティブオブジェクト拡張です 。 たとえば、独自のメソッドをプロトタイプ配列に追加すると、通常、関数に配列を渡すよりも便利です。 今、このカテゴリでは、 Sugarルールが、 やがて-MooToolsPrototypeになります。 多くの便利な機能が追加されますが、多くの場合、メソッドは互いにほぼ完全に複製されます。 多相性はここで展開する必要があります-しかし、多相性の場合、そのようなライブラリは通常、配列のプロトタイプメソッド、 Function#bind 、および他のいくつかに制限され、ほとんどの標準を無視します。 紛争に関しては、ここではすべてが非常に悪いです。 このようなライブラリは、多くの場合、同じ名前で異なるシグネチャを持つメソッドを使用してネイティブオブジェクトを拡張します。 競合を避けるために、最終アプリケーションを開発するときは、ポリファイルをカウントせずにネイティブオブジェクトを展開する複数のライブラリを使用しないでください。また、ライブラリを記述するとき、このような依存関係は一般に受け入れられません。

1つの汎用標準ライブラリの代わりに、不必要なトラブルなしで希望する機会を提供するために、 Undescore / LoDash / Sugar + es5-shim、es6-shim、es6-symbol、setImmediate.js / asap、Moment.jsから寄せ集めを引き出す必要があります/ Intl.js、コンソールダミー ...など。

これらのアプローチのそれぞれからベストを取りましょう。 core.jsの概念は次のとおりです。


通常のビルドの場合、 core.jsの操作は非常に明白です。

 console.log(Array.from(new Set([1, 2, 3, 2, 1]))); // => [1, 2, 3] console.log('*'.repeat(10)); // => '**********' Promise.resolve(32).then(console.log); // => 32 setImmediate(console.log, 42); // => 42 

ネイティブオブジェクトを拡張しないアセンブリの場合、機能はグローバルcoreオブジェクトまたはモジュラーシステムにエクスポートされます。 たとえば、 Promiseコンストラクターはcore.Promiseとして使用でき、 Array.fromメソッドはcore.Array.fromとしてArray.from 。 ライブラリの追加ではなく、既存のプロトタイプに追加することを目的としたメソッドは、コンストラクターが静的になります。たとえば、 core.String.repeatString.prototype.repeatメソッドの静的バージョンです。

 var log = core.console.log; log(core.Array.from(new core.Set([1, 2, 3, 2, 1]))); // => [1, 2, 3] log(core.String.repeat('*', 10)); // => '**********' core.Promise.resolve(32).then(log); // => 32 core.setImmediate(log, 42); // => 42 

ポリフィルのみをそれぞれ含むアセンブリは、それらを追加するだけです。 実際、通常のアセンブリを使用した例では、ポリフィルのみが使用されています。

Node.jsへのインストール:

 npm i core-js 

アセンブリの1つを接続して、以下から選択できます。

 //  : require('core-js'); //     : var core = require('core-js/library'); // ,   : require('core-js/shim'); 

ブラウザ用のビルド


これらのアセンブリのいずれにも満足できない場合は、独自のアセンブリを作成できます。 たとえば、ネイティブオブジェクトを展開せずに、コンソールモジュールと単純な日付形式のみが必要です。 これを行うには、Node.jsをインストールします。その後、アセンブリに必要な依存関係を持つgrunt-cli、core-jsをインストールし、アセンブルします。

 npm i -g grunt-cli npm i core-js cd node_modules/core-js && npm i grunt build:date,console,library --path=custom uglify 

その結果、ファイルcustom.jscustom.min.js重量4.8kbおよびcustom.min.mapを取得します。 libraryフラグは、ネイティブオブジェクトを展開せずにアセンブリを指します。 ここで 、必要な機能が属するモジュールを確認できます (最後の列)。

パート1:松葉杖


誰かが理解していない場合、松葉杖は、記事の文脈では、ライブラリで利用可能な標準化された機能の多愛者を意味します。 それでは、行きましょう:

#ECMAScript 5



おそらく誰もがECMAScript 5が標準ライブラリに追加するものを知っています。 ES5をサポートしていないほとんどすべてのブラウザーは消滅しました。 古いIEを除きます。 これまで、顧客はIE8のサポートを、そして最も深刻な場合はIE6のサポートもしばしば要求します。 近い将来、状況が変わることを願っています。 最も人気のあるES5の親友はこのes5-shimで 、いくつかの機能はSugar、MooTools、Prototypeにありますが、一部のみです。 これは目新しさからはほど遠いので、不必要な詳細-簡単な説明と、必要に応じていくつかの実装機能なしで行うことができます。 もちろん、コードがIE8-をサポートして記述されている場合、記述子を操作することに疑問の余地はないことを覚えておくことが重要です。

配列メソッド


配列のプロトタイプメソッドから始めましょう。 これは誰にでもよく知られています:

配列#indexOfは、指定された値に等しい最初の要素のインデックスを返します。値が見つからない場合は-1返します。
配列#lastIndexOfは前のもの似ていますが、最後の要素のインデックスを返します。
配列#forEachは、配列の各要素に対して関数を呼び出します。
Array#mapは、指定された配列の各要素に対する関数呼び出しの結果を含む新しい配列を返します。
配列#フィルタは、チェック関数の条件を満たすこの配列のすべての要素を含む新しい配列を返します。
配列#everyは、配列内のすべての要素がチェック関数の条件を満たすかどうかを確認します。
配列#someは、チェック関数の条件を満たす配列の要素が少なくとも1つあるどうかを確認します。
Array#reduceは、関数を使用して左から右に配列を畳み込みます。
配列#reduceRightは、関数を使用して配列を右から左に畳み込みます。

 [1, 2, 3, 2, 1].indexOf(2); // => 1 [1, 2, 3, 2, 1].lastIndexOf(2); // => 3 [1, 2, 3].forEach(function(val, key){ console.log(val); // => 1, 2, 3 console.log(key); // => 0, 1, 2 }); [1, 2, 3].map(function(it){ return it * it; }); // => [1, 4, 9] [1, 2, 3].filter(function(it){ return it % 2; }); // => [1, 3] function isNum(it){ return typeof it == 'number'; } [1, '2', 3].every(isNum); // => false [1, '2', 3].some(isNum); // => true function add(a, b){ return a + b; } [1, 2, 3].reduce(add); // => 6 [1, 2, 3].reduceRight(add, ''); // => '321' 

これらのメソッドは基本的に実装されていますが、1つの機能があります。 配列メソッドはジェネリックであり、配列だけでなく、配列に似たオブジェクトのコンテキストで呼び出すことができます 。この詳細については、 以下で説明します 。 そのため、ES5仕様によると、文字列は配列のようなオブジェクトであり、文字列の文字はインデックスによって取得できます。たとえば、 'string'[2] // => 'r'ですが、古いIEではそうではありません。 これらのメソッドが文字列のコンテキストで適用される場合、文字列を配列にキャストします。 同じ問題を解決するには、必要に応じて、古いIEのArray#sliceArray#joinを置き換えます。

 Array.prototype.map.call('123', function(it){ return it * it; }); // => [1, 4, 9] Array.prototype.slice.call('qwe', 1); // => ['w', 'e'] Array.prototype.join.call('qwe', '|'); // => 'q|w|e' 

さて、古代の真実を忘れないでください。for for-inループで配列をバイパスしないfor-inください 。 これは遅いだけでなく、IE8-サポートが必要な場合、キーが適切であるかどうかを確認することも強制します-そうでなければ、配列の要素だけでなく、そのプロトタイプのメソッドもバイパスします:)

静的メソッドArray.isArrayは同じカテゴリに属します。 このメソッドは、オブジェクトがプロトタイプチェーンではなく、内部クラスによる配列であるかどうかをチェックします。 便利ですが、普遍的ではありません。 オブジェクトの分類については、記事の第2部である自転車で詳細に説明します

 Array.isArray([1, 2, 3]); // => true Array.isArray(Object.create(Array.prototype)); // => false 

オブジェクトAPI


ECMAScript 3に基づくすべてのECMAScript 5オブジェクトAPIメソッドの完全なエミュレーションは不可能であり、部分的です-多くの場合に可能です。 ES5は、次のカテゴリのメソッドをオブジェクトAPIに追加します。プロトタイプの作成(作成/受信)、オブジェクトキーの取得、記述子の使用。

Object.createメソッドは、プロトタイプからオブジェクトを作成します。 nullを渡すことにより、プロトタイプなしでオブジェクトを作成できますが、これはECMAScript 3に基づいて行うことは不可能です。 iframe基づく激しいゴミを使用する必要があります。 これがなぜ第2部で私たちに明らかにされるのでしょう? オプションで、 Object.definePropertiesに似た#記述子オブジェクトを受け入れます。

 function Parent(/*...*/){ /*...*/ } Parent.prototype = {constructor: Parent /*, ... */} function Child(/*...*/){ Parent.call(this /*, ...*/); // ... } //   ES3 (  inherit  extend'): function Tmp(){} Tmp.prototype = Parent.prototype; Child.prototype = new Tmp; Child.prototype.constructor = Child; //   ES5: Child.prototype = Object.create(Parent.prototype, {constructor: {value: Child}}); var dict = Object.create(null); dict.key = 42; console.log(dict instanceof Object); // => false console.log(dict.toString) // => undefined console.log(dict.key) // => 42 

Object.getPrototypeOfメソッドは、オブジェクトのプロトタイプを返します。 ECMAScript 3では、オブジェクトのプロトタイプを取得する保証された方法はありません。 オブジェクトにconstructorプロパティが含まれる場合、おそらくプロトタイプはconstructor.prototypeます。 Object.createで作成されたオブジェクトの場合、プロトタイプを含むシンボルを追加し、キーを受け取ったときにそれを無視します 。 ただし、「正しい」 constructorプロパティを指定せずにプロトタイプがオーバーライドされたコンストラクタインスタンスでは、 Object.getPrototypeOfは正しく機能しません。

 var parent = {foo: 'bar'} , child = Object.create(parent); console.log(Object.getPrototypeOf(child) === parent); // => true function F(){} console.log(Object.getPrototypeOf(new F) === F.prototype); // => true F.prototype = {constructor: F /*, ...*/}; console.log(Object.getPrototypeOf(new F) === F.prototype); // => true F.prototype = {}; console.log(Object.getPrototypeOf(new F) === F.prototype); //  IE8-    

Object.keysメソッドは、オブジェクトの独自の列挙キーの配列を返します。 Object.getOwnPropertyNamesは、オブジェクト自体のキーの配列を返します。 および非上場。 Object.keysを使用すると、すべてがシンプルに思えますObject.keys for-inを介してオブジェクトをObject.keysし、プロパティが独自のものであるかどうかを確認します。 IEの「列挙不可能な」列挙プロパティのバグではない場合。 そのため、このようなプロパティを個別に確認する必要があります。 同様に、使用可能な非表示プロパティのリストを追加チェックすると、 Object.getOwnPropertyNamesObject.getOwnPropertyNamesます。

 console.log(Object.keys({q: 1, w: 2, e: 3})); // => ['q', 'w', 'e'] console.log(Object.keys([1, 2, 3])); // => ['0', '1', '2'] console.log(Object.getOwnPropertyNames([1, 2, 3])); // => ['0', '1', '2', 'length'] 

記述子のすべてが悪いので、ECMAScript 3はそれらをサポートしていません。 ゲッター/セッターを設定する方法はありません。 Object#__define[GS]etter__持っているが、 Object.defineProperty欠落しているObject.defineProperty消滅しました。 古いIEでは、 VBScriptを使用し倒錯化することで、getter / setterを使用してオブジェクトを作成できますが、これは別のトピックです。 enumerable: falseプロパティはありますが、設定することはできませんが、 Object#propertyIsEnumerableを使用してそのようなものかどうかを確認することができます 。 IE8には記述子を操作するためのメソッドがありますが、そうでない場合はより適切です(DOMオブジェクトでのみ機能します)。 したがって、IE8-でできることはスタブだけです。 Object.definePropertyおよびObject.definePropertiesの記述子のvalueによってプロパティvalue設定し、 Object.getOwnPropertyDescriptorvalueenumerableを正直に取得しvalue

Object.freeze, Object.preventExtensions, Object.sealどうですか? スタブを除き、エミュレーションが不可能になるだけでなく、次のような観点もあります。
Object.freeze、Object.preventExtensions、Object.seal、with、eval
あなたはおそらく決して必要としないだろうクレイジーたわごと。 それから離れてください。
-フェリックス・ガイゼンデルファー
そして、私は彼女に完全に同意します。

その他


#ECMAScript 5は、 Function#bindを介してコンテキストバインディングと基本的な部分アプリケーション機能を追加しました。 この方法は素晴らしいですが、JavaScriptで部分的なアプリケーションとコンテキストバインディングの可能性を解き放つには、それだけでは十分ではありません。トピックは#対応するセクションで詳しく説明されています。

 var fn = console.log.bind(console, 42); fn(43); // => 42 43 

Date.nowメソッドは、現在の時刻を数値表現で返します。結果は+new Dateと同じです。

 Date.now(); // => 1400263401642 

String#trimメソッドは、行の先頭と末尾から空白を削除します。

 '\n  \n'.trim(); // => '' 

JSONモジュールについては、IE8でサポートされており、このライブラリのフレームワーク内では、実装する理由はありません。 あなたが絶対に先史時代のIEでそれを必要とするならば-誰も例えばこの親友を使うことを気にしません。

#ECMAScript 6



ECMAScript 5仕様は、決して受け入れられなかったECMAScript 4仕様の代わりに急いで書かれており、前世紀に採用されたECMAScript 3にはあまり拡張されていませんでした。 新機能の追加は凍結され、ECMAScript 7の提案にはすべての主要な変更が加えられ、ドラフト仕様への最近の変更のほとんどはバグ修正です。 したがって、標準ライブラリでは、主にES6に焦点を当てます。

現在のエンジンでサポートされていることは、 この表にはっきりと表れています


最も人気のあるES6の親友は、 paulmillrのes6 - shimです。

標準のECMAScript 6ライブラリの一部、たとえばProxy (これは最もおいしい機能の1つです)、ECMAScript 5を実装することはできません。さらにECMAScript 3を実装することはできませんが、ほとんどは完全ではないにしても、少なくとも部分的に「不正」に実装できます。

プリプロセッサECMAScript 6+について少し


構文については、このライブラリのフレームワーク内では、サポートを追加できません。 ただし、ECMAScript 6+の構文をECMAScript 3または5に変換するプリプロセッサがここで助けになります。それらの膨大な数があります。数個の一般的なものだけを考えてみましょう。

そのような古くて強力なプロジェクト、 Google Traceurがあります。 読み取り不能なコードを生成し、かなり重いランタイムを使用するため、使用を拒否しました。

別のプロジェクトは私にとってはるかに魅力的なようです-6to5このプロジェクトは新鮮で、驚くほど速く開発されています。読みやすいコードを生成し、ジェネレーターをコンパイルするために使用するランタイムジェネレーターを除き、独自のランタイムを使用しません。しかし、代わりに、彼は積極的に標準ES6ライブラリを使用します-例えば、Symbol.iterator。デフォルトはes6-shimおよびes6-symbolです。これらはライブラリに簡単に置き換えられるため、このプリプロセッサは理想的なペアになります。コードをECMAScript 5に変換しますが、主に標準ライブラリに関するものです。#のObject.definePropertiesようなメソッドスタブでは、ほとんどすべてが古いIEでも機能します。

標準ライブラリのプリプロセッサとポリフィールを使用して、ECMAScript 6を最大限に使い始めることができます。おそらく、いくつかのささいなことを除いて。6to5ES6をコンパイルする機能を備え

た非常にシンプルなサンドボックスと、接続されcore.jsライブラリをスケッチしました。例には、リンクがあります。ただし、このライブラリはECMAScript 6構文に一切結び付けられていないため、ほとんどの例ではECMAScript 5構文を使用します。



まあ、今では、ECMAScriptの6の新しいデザイナーのための標準ライブラリに移動し、コンセプトは、別の章で説明します#記号#コレクション#イテレータ#の約束、残りはここで検討していきます。

Object.assign


この方法は多くの人に期待されていました。Object.assign cornyは、ソースオブジェクトの独自の列挙プロパティをすべてターゲットオブジェクトにコピーします。

 var foo = {q: 1, w: 2} , bar = {e: 3, r: 4} , baz = {t: 5, y: 6}; Object.assign(foo, bar, baz); // => foo = {q: 1, w: 2, e: 3, r: 4, t: 5, y: 6} 

またObject.mixin、列挙不可能なプロパティをコピーし、記述子を考慮に入れ、キーワードを介して取得した親を再割り当てするメソッドを追加することも計画されていましたsuperしかし、彼らはその追加を延期することにしました。その類似物はライブラリの自転車部分にあります。

Object.is


JavaScriptの比較演算子は通常、かなり奇妙に動作します。==キャストのような演算子でさえ忘れてください===

 NaN === NaN // => false 0 === -0 // => true,   : 1/0 === 1/-0 // => false 

この場合のために、言語には内部SameValue比較アルゴリズムがあります。それのためにNaNあるNaNと、+0そして-0明瞭な。 ECMAScript 6では、それらを演算子isおよびとして引き出したかっisntたのですが、言語の比較演算子はすでに十分ではないことを認識しており、後方互換性のために、Object.isメソッドとして取り出されました

 Object.is(NaN, NaN); // => true Object.is(0, -0); // => false Object.is(42, 42); // => true,  '===' Object.is(42, '42'); // => false,  '===' 

また、ES6以降では、別の比較アルゴリズムが積極的に使用されます-SameValueZero、それNaNは等しくNaN、前のものと-0異なり、等しい+0です。彼らは、キーの一意提供#コレクションを経由コレクションに参加する要素チェックするとき、それが使用され、#をArray#includes

Object.setPrototypeOf


ES6では、既存のオブジェクトのプロトタイプをインストールするメソッドが表示されますObject.setPrototypeOf

 function Parent(){} function Child(){} Object.setPrototypeOf(Child.prototype, Parent.prototype); new Child instanceof Child; // => true new Child instanceof Parent; // => true function fn(){} Object.setPrototypeOf(fn, []); typeof fn == 'function'; // => true fn instanceof Array; // => true var object = {}; Object.setPrototypeOf(object, null); object instanceof Object; // => false 

言語のプロトタイプの方向、機会-少なくとも最初の例-シンプルで明白なプロトタイプの継承がECMAScript 5にないことを考えると、このように一見必要なのは奇妙です。このメソッドなしで既存のオブジェクトのプロトタイプを変更する唯一の方法は非標準のプロパティ__proto__です。現時点では、IE10-を除く現在のすべてのブラウザーで、現在の実装(プロトタイプのゲッター/セッター)でサポートされていますObject

プリミティブで、不必要なチェックやオブジェクトの戻りObject.setPrototypeOfがなければ、バージョンはシンプルに見えるでしょう-セッター__proto__引き出して、それから機能を作ります:

 var setPrototypeOf = Function.call.bind(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set); 

ただし、別の問題がここに表示されます-v8の古い、しかしどこか関連するバージョンでは、セッター__proto__を関数として使用できません。実装Object.setPrototypeOfの場合、キーで値を設定するだけです__proto__

私たちは生きています:


また、ECMAScript 6とライブラリは作業のロジックを変更していObject#toStringます。このトピックは深刻ですが、記事の後半について説明します。

配列メソッド


静的メソッドArray.fromおよびArray.of-ジェネリック、それらが以外の関数のコンテキストで実行される場合、それらはArrayそのインスタンスを作成します。これについて詳しく知りたい場合は、この記事で新しい配列メソッドについて詳しく説明しています

のECMAScript 6は、非常に有用な方法が追加されます- Array.fromこれは、反復可能で配列のようなオブジェクトへの汎用キャストです。ほとんどの場合、Array.prototype.slice.call開始位置と終了位置を指定せずに置き換えられます。さらに、このメソッドは、オプションのマップコールバックとその実行コンテキストを受け入れます。マップ・コールバックなしで反復可能な転送の場合、結果は、オペレータの使用に類似している#広がり配列リテラルで- [...iterable]

 Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] Array.from({0: 1, 1: 2, 2: 3, length: 3}); // => [1, 2, 3] Array.from('123', Number); // => [1, 2, 3] Array.from('123', function(it){ return it * it; }); // => [1, 4, 9] 

前のものとは異なり、 Array.ofメソッドは現在ほとんど実用的ではありませんサブクラスには、まずArray配列リテラルの類似物として必要です[]

 Array.of(1); // => [1] Array.of(1, 2, 3); // => [1, 2, 3] 

メソッドアレイ#の検索およびアレイ#findIndexは、コールバック呼び出して検索配列を搭載しました。

 function isOdd(val){ return val % 2; } [4, 8, 15, 16, 23, 42].find(isOdd); // => 15 [4, 8, 15, 16, 23, 42].findIndex(isOdd); // => 2 [4, 8, 15, 16, 23, 42].find(isNaN); // => undefined [4, 8, 15, 16, 23, 42].findIndex(isNaN); // => -1 

配列メソッド配列#fillは、渡された値で配列を埋めます。オプションの引数-開始位置と終了位置。

 Array(5).map(function(){ return 42; }); // => [undefined × 5],   .map  ""   Array(5).fill(42); // => [42, 42, 42, 42, 42] 

文字列メソッド


ここではすべてが簡単です。 文字列#には(最近まで- String#contains含まれていましたが、古い名前で利用できる限りArray#includes引き寄せられました)、文字列内の部分文字列をチェックします。文字列#startsWithおよび文字列#endsWithは、文字列が特定の部分文字列で開始または終了するかどうかを確認します。これら3つのメソッドは、追加の引数-開始位置を取ります。

 'foobarbaz'.includes('bar'); // => true 'foobarbaz'.includes('bar', 4); // => false 'foobarbaz'.startsWith('foo'); // => true 'foobarbaz'.startsWith('bar', 3); // => true 'foobarbaz'.endsWith('baz'); // => true 'foobarbaz'.endsWith('bar', 6); // => true 

String#repeatメソッドは、指定された回数だけ繰り返される文字列を返します。

 'string'.repeat(3); // => 'stringstringstring' 

ライブラリには、マルチバイト文字と正直な行反復子のサポートを改善するための ECMAScript 6/7メソッドがまだ追加されていません;配列反復子が文字列に使用されます。今、彼らは私が単にそれらを必要としないという理由だけではありません。近い将来それらを追加するのは良いことです。

数字を扱う


ECMAScript 6は、膨大な数の数学関数と定数を追加します。説明と例はなく、リンクのみ:Number.EPSILONNumber.parseFloatNumber.parseIntNumber.isFiniteNumber.isIntegerNumber.isNaNNumber.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER、Number.isSafeIntegerMath.acoshMath.asinhMath.atanhMath.cbrtMath.clz32Math.coshMath.expm1Math.hypotMath.imulMath.log1pMath.log10Math.log2Math.signMath.sinhMath.tanhMath.trunc

ECMAScriptの6:記号



JavaScriptでは、オブジェクトのプロパティを非表示にするのはかなり悪いです。プライベートデータはクロージャに格納できます。これにより、オブジェクトのプロトタイプではなく、コンストラクタ内でそれらを操作するメソッドを宣言する必要があります。ECMAScriptの5以降では、あなたが宣言することができますenumerable: false非表示オブジェクトのリストからプロパティことを財産for-inと上Object.keysが、それは信頼性の高い隠ぺいを提供していませんが、 -キーは一意ではないが、それはあなたがあるため、使用する必要の名前の競合を有していてもよく、ピックアップして簡単にすることができObject.defineProperty、オブジェクト記述子をこれはかなり面倒です。

ECMAScript 6では、カプセル化を簡素化するために、以前はNameと呼ばれていた新しいデータ型Symbolが導入されていますシンボルは、オブジェクトの一意のキーとして使用するためのものです。

 var Person = (function(){ var NAME = Symbol('name'); function Person(name){ this[NAME] = name; } Person.prototype.getName = function(){ return this[NAME]; }; return Person; })(); var person = new Person(''); console.log(person.getName()); // => '' console.log(person['name']); // => undefined console.log(person[Symbol('name')]); // => undefined,   Symbol    for(var key in person)console.log(key); // =>  'getName',       console.log(typeof Symbol()); // => 'symbol' 

シンボルは完全にプライベートではありません-メソッドObject.getOwnPropertySymbolsはオブジェクト自体のシンボルを返します。これにより、クローン作成などの低レベルの操作をデバッグできます。#WeakMapに基づいて、真にプライベートなデータのストレージを実装できます。私見では、この問題に対するより成功した解決策は、完全にプライベートなバージョンのキャラクターを追加することです。

現時点で、キャラクターはv38で利用可能です。Chrome38(以前のバージョン- #実験的機能のフラグ付き)から、Firefoxのナイトリービルドで33から始まります。IEで間もなく約束されます。主要な最新ブラウザ。

もちろん、ES5に基づいた本格的なポリフィリックなキャラクターは不可能ですが、基本的な機能(オブジェクトの通過for-in戻り不可Object.keysキーに関係しない一意のキーの作成)は、たとえば次のように非常に簡単に実装されます。

 window.Symbol || (function(){ var id = 0; window.Symbol = function(description){ if(this instanceof Symbol)throw new TypeError('Symbol is not a constructor'); var symbol = Object.create(Symbol.prototype) , tag = 'Symbol(' + description + ')_' + (++id + Math.random()).toString(36); symbol.tag = tag; Object.defineProperty(Object.prototype, tag, { configurable: true, set: function(it){ Object.defineProperty(this, tag, { enumerable : false, configurable: true, writable : true, value : it }); } }); return symbol; } Symbol.prototype.toString = function(){ return this.tag; } })(); 

呼び出されると、Symbolたとえば"Symbol(description)_m.y9oth2pcqaypsyvi"一意のキー文字列を生成し、このキーのObject.prototypeセッター設定します「文字」がキャストされるキー文字列によって値を設定しようとすると、セッターはenumerable: falseプロパティを現在のオブジェクトに設定します。ただし、このような「シンボル」には膨大な数のマイナスがあります。ここに一部を示します。


決定が非常に疑わしい場合、ここで何をしていますか?プロジェクトに必要な文字数が少なく、自分自身を含まないオブジェクトでそれらを使用する予定がない場合Object.prototype、このソリューションは機能します。それ以外の場合は、ライブラリにいくつかのヘルパーを追加します。Symbol.pure利用できるネイティブな文字ならば、それは文字を返し、ない-でセッターを追加することなく、独自の文字列のキーを返しObject.prototype、そしてSymbol.set、利用可能な場合、ネイティブシンボルは-ちょうどオブジェクトのキー値に敷設なし-使用しての値が設定されます。この原始的な方法で、上記の問題の半分を取り除きます。呼び出しの代わりにデータヘルパーを使用して上記で使用した例は次のようになりますObject.definePropertyenumerable: falseSymbol

 var Person = function(){ var NAME = Symbol.pure('name'); function Person(name){ Symbol.set(this, NAME, name); } Person.prototype.getName = function(){ return this[NAME]; }; return Person; }(); 

前述のとおり、メソッドObject.getOwnPropertySymbolsは追加しません。そして、文字列と文字の両方のすべてのキーをバイパスするための、標準化された、多かれ少なかれ普遍的な方法が必要です。 ECMAScriptの6は、追加のモジュールReflect最初の場所では、 -プラグのセットのためにProxy。エミュレートする能力がないため、実際にはProxyモジュールReflectは必要ありません。ただし、Reflect.ownKeysオブジェクトのすべてのキーを返すメソッドがあります-文字列と文字の両方、つまりObject.getOwnPropertyNames + Object.getOwnPropertySymbols。このメソッドを追加します。

 var O = {a: 1}; Object.defineProperty(O, 'b', {value: 2}); O[Symbol('c')] = 3; Reflect.ownKeys(O); // => ['a', 'b', Symbol(c)] 

ES6は、キャラクターのグローバルなケースのような怪しいものも追加します。それを操作するためのいくつかの方法があります -Symbol.for Symbol.keyForです。Symbol.forレジスタで検索し、キー文字列で文字を返しますが、見つかりません-新しい文字を作成し、レジスタに追加して返します。Symbol.keyFor送信された文字がレジスタ内で一致する文字列を返します。

 var symbol = Symbol.for('key'); symbol === Symbol.for('key'); // true Symbol.keyFor(symbol); // 'key' 

さらに、ライブラリはシンボルSymbol.iterator#をSymbol.toStringTag積極的に使用します

ECMAScriptの6:コレクション



ECMAScript 6には、4つの新しいタイプのコレクションがMap, Set, WeakMapありWeakSetますおよび型付き配列もありますが、今のところは配列なしで実行できます。


それで、これらのコレクションは何ですか?

Map -Key-Valueのコレクション。JavaScriptエンティティはキーとして機能できます-プリミティブとオブジェクトの両方。回避策があります。#イテレータとメソッド.forEachがあり、要素の数はプロパティを介して利用できます.size

 var a = [1]; var map = new Map([['a', 1], [42, 2]]); map.set(a, 3).set(true, 4); console.log(map.size); // => 4 console.log(map.has(a)); // => true console.log(map.has([1])); // => false console.log(map.get(a)); // => 3 map.forEach(function(val, key){ console.log(val); // => 1, 2, 3, 4 console.log(key); // => 'a', 42, [1], true }); map.delete(a); console.log(map.size); // => 3 console.log(map.get(a)); // => undefined console.log(Array.from(map)); // => [['a', 1], [42, 2], [true, 4]] 

Set-一意の値のコレクション。と同様にMap、回避策があります。

 var set = new Set(['a', 'b', 'a', 'c']); set.add('d').add('b').add('e'); console.log(set.size); // => 5 console.log(set.has('b')); // => true set.forEach(function(it){ console.log(it); // => 'a', 'b', 'c', 'd', 'e' }); set.delete('b'); console.log(set.size); // => 4 console.log(set.has('b')); // => false console.log(Array.from(set)); // => ['a', 'c', 'd', 'e'] 

WeakMap-キーと値のコレクションオブジェクトのみがキーとして機能します。弱い接続を使用します-キーオブジェクトが(ガベージコレクターによって)削除されると、コレクションのキーと値のペアも削除されます。回避する方法.forEachはありません.sizeイテレータもメソッドもプロパティもありません。これは、プライベートデータを格納するもう1つの方法であり、より「正直」ですが、#文字を使用する場合と比べてリソースを集中的に使用します。将来、抽象参照が JavaScript追加される場合、このようなプライベートフィールドに便利な構文が表示されます。

 var a = [1] , b = [2] , c = [3]; var wmap = new WeakMap([[a, 1], [b, 2]]); wmap.set(c, 3).set(b, 4); console.log(wmap.has(a)); // => true console.log(wmap.has([1])); // => false console.log(wmap.get(a)); // => 1 wmap.delete(a); console.log(wmap.get(a)); // => undefined //      var Person = (function(){ var names = new WeakMap; function Person(name){ names.set(this, name); } Person.prototype.getName = function(){ return names.get(this); }; return Person; })(); var person = new Person(''); console.log(person.getName()); // => '' for(var key in person)console.log(key); // =>  'getName' 

WeakSet-さて、ポイントを得る。比較的最近ドラフト仕様に登場したため、ブラウザのサポートはかなり弱いです。

 var a = [1] , b = [2] , c = [3]; var wset = new WeakSet([a, b, a]); wset.add(c).add(b).add(c); console.log(wset.has(b)); // => true console.log(wset.has([2])); // => false wset.delete(b); console.log(wset.has(b)); // => false 

これらのコレクションすべて、準線形検索時間提供する必要があります。キーの一意性は、#SameValueZero比較アルゴリズムによって提供されます

最新のjsエンジンに対するこれらのコレクションのサポートは何ですか?とても良い。


ほとんどすべての現在の実装収集方法.add.set返さないthis-これらのメソッドは動作しません収集チェーンを埋めるために。しかし、それは簡単に扱われます。

コレクションをイテレータで初期化するには、コンストラクターのラッパーでも十分です。これにより、コレクションが作成され、要素が追加されます。イテレータについては、次の章で説明します。

さて、コレクション自体の多相性をさらに検討してください。これらのコレクションの本格的な実装-高速でありながらクリーンで、ECMAScript 5に基づくメモリリーク(WeakMap用)はありませんが、妥当な妥協案を見つけることができます。

マップとセットの実装


ほとんどのポリフィルは実装を表しますMapか?インスタンスMap-2つの配列、キーと値。要素を受け取ると、キーの配列で一致するものを探し、結果のインデックスの値の配列から要素を返します。または、削除を最適化するための代替手段は、エントリオブジェクトのチェーンです。両方の場合に何が問題になっていますか?これは非常に遅く、要素を見つける難しさはO(n)、uniq操作の複雑さはO(n 2)です。これはどのように私たちを脅かしますか? ここに小さなテストがあります:

 var array = []; for(var i = 0; i < 100000; i++)array.push([{}, {}]); array = array.concat(array); console.time('Map test'); var map = new Map(array); console.timeEnd('Map test'); console.log('Map size: ' + map.size); 

200,000のオブジェクトのペア(将来のKey-Value)の配列を作成します。そのうち100,000は一意であり、この配列からコレクションを作成しますMapたとえば、Firefoxで

ネイティブのものをテストしましょうMap

 Map test:   Map test: 46.25 Map size: 100000 

そして今Map最も人気のある ECMAScript 6の愛好家のものです。

 Map test:   Map test: 506823.31 Map size: 100000 

約8.5分。新しい要素をそれぞれ追加しようとすると、既に追加されている100,000個まで並べ替える必要があります。このことから、このアプローチは非常に小さなコレクションにのみ適していると結論付けることができます。

ハッシュテーブルを使用して、サブリニアポリフィルの速度を実現できます。 ECMAScript 5では、Object文字列のみをキーとして使用しています。上記でテストしたポリファイルには、小さな最適化があります-コレクションアイテムを見つける平均的な複雑さをO(1)に減らす単純な関数によるキー文字列または数字の検索インデックス:

 function fastKey(key){ if(typeof key === 'string')return '$' + key; else if(typeof key === 'number')return key; return null; }; 

同様に、他のプリミティブへのクイックアクセスを実装できます。しかし、Mapプリミティブとしてのみ効果的に使用できるキーとして、なぜ必要なのでしょうか?#これに対処Object.create(null)しますオブジェクトキーの一意の識別子文字列を取得および取得することは不可能です。したがって、ルールをわずかに破る必要があります。必要に応じて、オブジェクトにキーを設定するための識別子を持つシンボルを追加しましょう。このようなもの:



 var STOREID = Symbol('storeId') , id = 0; function fastKey(it){ //    'S'      'P'    if(it !== Object(it))return (typeof it == 'string' ? 'S' : 'P') + it; //      -  if(!Object.hasOwnProperty.call(it, STOREID))it[STOREID] = ++id; //     'O' return 'O' + it[STOREID]; } 

Map2つの配列または要素オブジェクトのチェーンでObjectはなく、キーと値の2つのハッシュ実装します。追加の順序でコレクションをトラバースするために別のキー/値ストアは必要ありません:すべてのエンジンでは、オブジェクトキーは数字キーを除き、追加の順序で格納されますが、ここではすべてのキーにプレフィックス文字があるため、存在しません。合計:

 Map test:   Map test: 669.93 Map size: 100000 

もちろん、ネイティブのものよりも遅いですが、うまくいくと思います。はい、キーオブジェクトに隠しプロパティを記述します-キーとして#frozen-objectsを使用することはできませんが、許容可能な速度が得られますSet1ハッシュで同様に実装されます。

疎結合コレクションの実装


弱結合コレクションの実装はさらに簡単です。イテレータ、メソッド.forEach、プロパティはありません.sizeコレクションオブジェクトにキーと値を保存する場合、それはもはや疎結合ではなくなります-キー/値は削除されず、Setandの削除されたバージョンを取得しMapます。多かれ少なかれ合理的な解決策は長い間知られています-キーに値を保存し、その識別子のみをコレクションオブジェクトに保存することです。値はキーに保存されるため、保存されたデータのプライバシーはポリファイルで失われます。

大幅に簡素化された実装は次のようになります。
 window.WeakMap || (function(){ var id = 0 , has = Function.call.bind(Object.prototype.hasOwnProperty) , WEAKDATA = Symbol('WeakData') , ID = Symbol('ID'); window.WeakMap = function(){ if(!(this instanceof WeakMap))throw TypeError(); this[ID] = id++; } Object.assign(WeakMap.prototype, { 'delete': function(key){ return this.has(key) && delete key[WEAKDATA][this[ID]]; }, has: function(key){ return key === Object(key) && has(key, WEAKDATA) && has(key[WEAKDATA], this[ID]); }, get: function(key){ if(key === Object(key) && has(key, WEAKDATA))return key[WEAKDATA][this[ID]]; }, set: function(key, value){ if(key !== Object(key))throw TypeError(); if(!has(key, WEAKDATA))key[WEAKDATA] = {}; key[WEAKDATA][this[ID]] = value; return this; } }); })(); 

キー参照を削除するとき、それがコレクションに残っていないこと、そしてそれに応じてメモリがリークしないことを確認しましょう:

 // <-   snapshot 1 var array = []; for(var i = 0; i < 100000; i++)array[i] = {}; var wm = new WeakMap(); for(var i = 0; i < 100000; i++)wm.set(array[i], {}); // <-   snapshot 2 array = null; // <-   snapshot 3 



ただし、場合によっては、メモリリークの問題が残ることがあります。コレクションオブジェクトが削除された後、値はキーに関連付けられたままになり、キーであったオブジェクトが削除されるまでメモリリークが発生します。そのため、コレクションWeakMapがキーよりも長生きするようにシステムを設計する価値があります。誰かがメモリリークの問題を回避しようとしていますが、これは難解性のカテゴリ- まったく同じケースでのメモリリークです。この問題は

実装WeakSetに残りますが、最小化されます-重いオブジェクトになる可能性のある値の代わりに、コレクション内の存在フラグのみがキーに保存されます。

ECMAScriptの6:イテレーション



ECMAScript 6は、イテレータプロトコルを導入します。これは、コレクションを横断するための普遍的な方法などです。構文的な構造もそれに適用されるため、それらについても考慮しましょう。しかし、まず第一に、これは標準ライブラリまたは構文の一部ではなく、概念です。イテレータプロトコルには以下を含めることができます。


構文はこれの一部であるため、この章では$for、これらの構文構成の機能の一部を実装するモジュールも検討しますライブラリを#ES6 +プリプロセッサで使用する場合は、このモジュールなしで安全にビルドできます。

イテレータは、.nextフィールドを持つオブジェクトを返すメソッドを持つオブジェクトです.done-イテレータのトラバースが完了し、.value現在のステップの値です。例は、正の数の反復子を作成するメソッドです。これにより、0から指定された整数( sandboxまでのすべての整数をバイパスできます

 function NumberIterator(number){ var i = 0; return { next: function(){ return i < number ? {done: false, value: i++} : {done: true}; } } } var iter = NumberIterator(3); iter.next(); // => {done: false, value: 0} iter.next(); // => {done: false, value: 1} iter.next(); // => {done: false, value: 2} iter.next(); // => {done: true} 

反復可能オブジェクト反復可能)-Symbol.iterator反復子を返すメソッドをキーに含むオブジェクトしたがって、反復子を反復可能にSymbol.iteratorするには、を返すキーによるメソッドが必要thisです。たとえば、数字を反復可能にします( sandbox):

 Number.prototype[Symbol.iterator] = function(){ return NumberIterator(this); } Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

に注意してくださいSymbol.iterator。 Firefoxはイテレータプロトコルをサポートしていますが、安定したビルドでは#文字はまだなく、代わりにSymbol.iterator文字列が使用されます"@@iterator"。シンボルは、ナイトリービルド、およびでさえ登場しましたSymbol.iteratorが、文字列はイテレータプロトコルで引き続き使用されます"@@iterator"。 Firefoxのイテレータプロトコルを壊さないために、ライブラリ内で、キーSymbol.iterator(存在しない場合は作成)とキーの両方でイテレータを取得するメソッドを複製します"@@iterator"。 v8では、Chrome 38にイテレータプロトコルが完全にサポートされました。

Generator-一時停止できる機能。拡張イテレータインターフェイスを持つオブジェクトを返します。構文については詳しく説明しませんたとえば、この記事を参照ください。プリプロセッサの場合、おそらく最悪の部分はECMAScript 6です。ジェネレータを使用する場合の反復可能な数値の例は、非常に単純に見えます( sandbox)。

 Number.prototype[Symbol.iterator] = function*(){ for(var i = 0; i < this;)yield i++; } Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

ループはfor-of反復可能なオブジェクトをバイパスするように設計されています。反復可能な数値を使用した例では、次のように機能します( sandbox):

 for(var num of 5)console.log(num); // => 0, 1, 2, 3, 4 

でのECMAScript 6 iskaropkiを繰り返すString, Array, Map, SetArgumentsさらに、Array, MapこれらにSetはメソッドが.keys, .valuesあり.entries、それぞれキー、値、およびキーと値のペアの反復子を返します。Core.jsは、データイテレータとメソッドを追加します。ループとともに、for-of次のようになります( sandbox):

 var string = 'abc'; for(var val of string)console.log(val); // => 'a', 'b', 'c' var array = ['a', 'b', 'c']; for(var val of array)console.log(val); // => 'a', 'b', 'c'.    - .values for(var val of array.values())console.log(val); // => 'a', 'b', 'c' for(var key of array.keys())console.log(key); // => 0, 1, 2 for(var [key, val] of array.entries()){ console.log(key); // => 0, 1, 2 console.log(val); // => 'a', 'b', 'c' } var map = new Map([['a', 1], ['b', 2], ['c', 3]]); for(var [key, val] of map){ //    - .entries console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } for(var val of map.values())console.log(val); // => 1, 2, 3 for(var key of map.keys())console.log(key); // => 'a', 'b', 'c' for(var [key, val] of map.entries()){ console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } var set = new Set([1, 2, 3, 2, 1]); for(var val of set)console.log(val); // => 1, 2, 3.    - .values for(var val of set.values())console.log(val); // => 1, 2, 3 for(var key of set.keys())console.log(key); // => 1, 2, 3.  .keys  .values for(var [key, val] of set.entries()){ //  Set   .entries     console.log(key); // => 1, 2, 3 console.log(val); // => 1, 2, 3 } var list = (function(){return arguments})(1, 2, 3); for(var val of list)console.log(val); // => 1, 2, 3 


このサイクルの構文サポートではすべてが非常に悪いので、ES6 +プリプロセッサを使用しない人のために、同様の機能for-of sandboxを実装するヘルパーをライブラリに追加します

 $for(new Set([1, 2, 3, 2, 1])).of(function(it){ console.log(it); // => 1, 2, 3 }); // 2  $for -  entries -    2  $for([1, 2, 3].entries(), true).of(function(key, value){ console.log(key); // => 0, 1, 2 console.log(value); // => 1, 2, 3 }); // 2  .of -    $for('abc').of(console.log, console); // => 'a', 'b', 'c' //    ,    false $for([1, 2, 3, 4, 5]).of(function(it){ console.log(it); // => 1, 2, 3 if(it == 3)return false; }); 

オブジェクトのプロトタイプargumentsは-Object.prototypeですから、イテレータをプロトタイプに入れるメソッドを置くことはできません。ネイティブオブジェクトを拡張せずに、 core.jsをライブラリとしてビルドするオプションもありますこれらの理由から、我々は、オブジェクトが反復可能であるかどうかをテストするには、ヘルパーのカップルを実行し、イテレータオブジェクトを取得する-$for.isIterable(foo)アナログとしてSymbol.iterator in fooおよび$for.getIterator(foo)アナログとしてfoo[Symbol.iterator]()

 var list = (function(){return arguments})(1, 2, 3); console.log($for.isIterable(list)); // => true console.log($for.isIterable({})); // => false var iter = $for.getIterator(list); console.log(iter.next()); // => {value: 1, done: false} console.log(iter.next()); // => {value: 2, done: false} console.log(iter.next()); // => {value: 3, done: false} console.log(iter.next()); // => {value: undefined, done: true} 

でのECMAScript 6つのイテレータを初期化するために使用されている#コレクション Map, Set, WeakMap, WeakSet、配列、後に#をArray.from自分の予想方法Promise.all, Promise.raceで仕事に#約束

関数、コンストラクター、および配列リテラルを呼び出すときに適用されるスプレッド演算子も、反復可能なオブジェクトを必要としますが、これは標準ライブラリからはほど遠いトピックです。今すぐ使用したいという要望があります-歯の中のプリプロセッサ。

 [...new Set([1, 2, 3, 2, 1])]; // => [1, 2, 3] console.log(1, ...[2, 3, 4]); // => 1, 2, 3, 4 var map = new Map([[1, 'a'], [2, 'b']]); new Array(...map.keys(), 3); // => [1, 2, 3] 

他のすべての場合、配列/ジェネレーター内包表記配列/ジェネレーターの抽象化?イテレータープロトコルにも属します以前はECMAScript 6ドラフトに含まれていましたが、 Firefoxでは長い間サポートされていましたが、ECMAScript 7まで延期されましたこれは、フィルター処理と変換を使用して、反復可能なオブジェクトから配列またはイテレーターを生成する機能です。つまり 構文filterおよびmap任意のiteriruemuhオブジェクトについて最後の配列の破壊はサポートされていませんが、FFで動作する例を除きます。すべて6to5Traceurで機能します。

 var ar1 = [for(i of [1, 2, 3])i * i]; // => [1, 4, 9] var set = new Set([1, 2, 3, 2, 1]); var ar2 = [for(i of set)if(i % 2)i * i]; // => [1, 9] var iter = (for(i of set)if(i % 2)i * i); iter.next(); // => {value: 1, done: false} iter.next(); // => {value: 9, done: false} iter.next(); // => {value: undefined, done: true} var map1 = new Map([['a', 1], ['b', 2], ['c', 3]]); var map2 = new Map((for([k, v] of map1)if(v % 2)[k + k, v * v])); // => Map {aa: 1, cc: 9} 

私に関しては-素晴らしいことです。それも単なる構文です。

ES6プリプロセッサを使用する予定がない人のために、モジュールの一部として、同様の絶対的な自転車を追加します$for。呼び出しが$forイテレータ高度な方法を返しofた上、filter, mapおよびarray。メソッドfilterとはmap、イテレータ返します。これにより、前のイテレータの値をフィルタリングまたは変換します。この反復子は、反復子と同じメソッドによって拡張されます$for。このメソッドarrayは、現在のイテレータを配列に変換し、オプションのマップコールバックを受け入れます。これらすべてのメソッドには、2番目のオプションの引数、実行コンテキストがあります。$forフラグを受け入れる場合entries、チェーン内のすべてのコールバックは、引数のペアで起動されます。



 var ar1 = $for([1, 2, 3]).array(function(v){ return v * v; }); // => [1, 4, 9] var set = new Set([1, 2, 3, 2, 1]); var ar1 = $for(set).filter(function(v){ return v % 2; }).array(function(v){ return v * v; }); // => [1, 9] var iter = $for(set).filter(function(v){ return v % 2; }).map(function(v){ return v * v; }); iter.next(); // => {value: 1, done: false} iter.next(); // => {value: 9, done: false} iter.next(); // => {value: undefined, done: true} var map1 = new Map([['a', 1], ['b', 2], ['c', 3]]); var map2 = new Map($for(map1, true).filter(function(k, v){ return v % 2; }).map(function(k, v){ return [k + k, v * v]; })); // => Map {aa: 1, cc: 9} 

リテラルでは、ES5の関数はやや扱いにくいですが、矢印関数では、構文理解を使用するのとほぼ同じになります。

反復子に対する他の操作をモジュール$for追加することができます。これにより、反復可能なオブジェクトをトラバースおよび変換する普遍的で怠zyな(反復子プロトコルなど)方法が提供されます。しかし、将来のために延期します。または多分地獄に。

ライブラリはさらに2つのイテレータ(#times#two)を追加します。Dictコンストラクタは反復可能なオブジェクトを期待しますが、イテレータに関連付けられたすべてのバイクをこの章にドラッグしません。

ECMAScriptの6:約束



非同期性とJavaScriptは、多くの人にとってほぼ同義語です。これはECMAScript 5標準の非同期性にすぎず、まったく何もありません。などであっても、このような基本的な技術setTimeoutsetIntervalW3CとWHATWGのウェブ基準が設けられており、それらについて#話もう少し。最初のクラスのオブジェクトとして機能しない限り、コールバックの転送は便利です。これにより、コールバックhellが発生します。非同期関数の並列および順次実行を簡素化する1つの方法は、async.jsなどのライブラリを使用することです。

非同期プログラミングを簡素化する別のアプローチは、Promiseテンプレートです。非同期関数が返すことができるpromiseオブジェクトを使用して、その結果をサブスクライブできます。結果をサブスクライブできるメソッドは、新しいプロミスを返します。これは、プロミスをチェーンに配置することにより、コードをより適切に構成するのに役立ちます。また、Promiseはエラー処理の問題を解決します。非同期コードでtry-catchは機能せず、コールバック引数でエラーを渡す必要があり、コードがさらに混乱する場合があります。 promiseチェーンの最後に、メソッドrejectthrowの両方でスローされるミスが発生する可能性がありますthrowQRSVPの

ようなPromiseライブラリ一般的です。時間が経つにつれて、標準が登場しましたPromises / A +。すべてのプロミスは非同期に解決され、メソッドを使用して結果をサブスクライブできます.then(最初の引数は正常終了時に実行される関数、2番目はエラーが発生した場合)。

そのため、非同期コードでの作業を最終的に標準化するために、ECMAScript 6 はPromises / A +標準と互換性があり、ほとんどの機能を最小限に抑えながら、promise実装を追加しました。それらについては特に説明しません。詳細についてはこちら翻訳ですが、少し時代遅れです)、こちらまたはこちらをご覧ください。 ES6 Promiseはv8とFirefoxですでに利用可能です。ポリファイルがあります-es6-promisenative-promise-only

のECMAScript 6の約束の実施は、コンストラクタでPromise2コールバックを渡される関数を、受け入れる-最初は、第二に障害が発生した約束を、ことができます。さらにthen、ES6 promiseにはメソッドが含まれています。これは、最初の引数をcatch省略した場合の省略形でありthen、それを使用するとエラーをサブスクライブできます。

 var log = console.log.bind(console); function sleepRandom(time){ return new Promise(function(resolve, reject){ // resolve   , reject -   //      setTimeout(resolve, time * 1e3, 0 | Math.random() * 1e3); }); } log(''); // =>  sleepRandom(5).then(function(result){ log(result); // => 869,  5 . return sleepRandom(10); }).then(function(result){ log(result); // => 202,  10 . }).then(function(){ log('  '); // =>    throw Error('!'); }).then(function(){ log('   - '); }).catch(log); // => Error: '!' 

私は約束エンジンを標準に完全に適応させるのが面倒だったので、core.jsの promise カーネルはネイティブの約束のみのライブラリに基づいており、そこからコードはほとんど残っていません。非同期性を確保するためにprocess.nextTick、多相性#setImmediateのメソッドも使用されます。

ヘルパーのペアは、それぞれ、正常に完了した、または渡された値でエラーが発生したプロミスPromise.resolvePromise.reject返します。彼Promise.resolveが約束受け入れた場合、彼はそれを返します。また、他のthenable(たとえば、jQuery Deferred)をpromiseに変換するために使用することもできます。標準の古いバージョンでは、このための別のメソッドがありました-Promise.cast

 Promise.resolve(42).then(log); // => 42 Promise.reject(42).catch(log); // => 42 Promise.resolve($.getJSON('/data.json')); // => ES6 promise 

ヘルパーPromise.allは、渡された反復可能なコレクションのすべてのプロミスが解決されたときに解決されるプロミスを返します(v8では、配列でのみ機能するようになりました。ライブラリ内でまだ修正を開始していません。他の何かで使用する意味はありません)。約束ではないコレクションの要素は、を通して約束に導かれPromise.resolveます。

 Promise.all([ 'foo', sleepRandom(5), sleepRandom(15), sleepRandom(10) //  15   -  ]).then(log); // => ['foo', 956, 85, 382] 

ヘルパーPromise.raceは前のものと似ていますが、渡されたコレクションから少なくとも1つのプロミスが解決されると解決されるプロミスを返します。私の謙虚な意見では、とは異なりPromise.all、完全には少し違いますが、それは無意味です。その助けがなければ、約束が解決される時間を制限するのが少し簡単になります。

 function timeLimit(promise, time){ return Promise.race([promise, new Promise(function(resolve, reject){ setTimeout(reject, time * 1e3, Error('Await > ' + time + ' sec')); })]); } timeLimit(sleepRandom(5), 10).then(log); // =>  5    853 timeLimit(sleepRandom(15), 10).catch(log); // Error: Await > 10 sec 

ECMAScriptの7で提案されている加えているためであっても約束して、非同期JavaScriptは、利便性の面で改善の余地があり、非同期関数構文のキーワードを拡張async / awaitに基づいており、#発電機、発電機と約束リターンの約束:)この構文は、すでにサポートされていること Traceur、および 6to5

 var delay = time => new Promise(resolve => setTimeout(resolve, time)); async function sleepRandom(time){ await delay(time * 1e3); return 0 | Math.random() * 1e3; } async function sleepError(time, msg){ await delay(time * 1e3); throw Error(msg); } (async () => { try { log(''); // =>  log(await sleepRandom(5)); // => 936,  5 . var [a, b, c] = await Promise.all([ sleepRandom(5), sleepRandom(15), sleepRandom(10) ]); log(a, b, c); // => 210 445 71,  15 . await sleepError(5, '!'); log('  '); } catch(e){ log(e); // => Error: '!',  5 . } })(); 

やがて、Promiseは標準の非同期JavaScript APIの多くを変更します。グローバル関数既に標準化されていますpolyphilfetch- XMLHttpRequestそれを約束するシンプルで便利なラッパーオファーは、追加する、と私はシンプルを追加すると思いますが、多くの場合、必要に応じて、機能delay前の例の機能と同様に、指定した時間後に解決されるの約束を返す-さよならをsetTimeoutおそらくdelay、このライブラリに追加するといいでしょう。

MozillaのJavaScriptの:配列のメソッドの静的バージョン



上記のイテレータプロトコルを調べました。これは、JavaScriptでコレクションをクロールするための唯一の標準ではありません。よりシンプルで、より速く、より古いものがあります。これらは配列のようなオブジェクトです。

JavaScriptには配列に加えて、JavaScriptに類似した多くのエンティティがありますが、同時に配列はありません。配列のようなこれらのオブジェクトには、スキップ可能.lengthなキー(0〜.lengthによる長さと要素が含まれています。「穴」が発生します。それらはArray.prototypeそれぞれ含まれておらず、それぞれ配列メソッドもありません。これは、オブジェクトarguments、文字列(正式にはIE8 +)、型付き配列(配列、ただしそれ自体は含まれませんArray.prototype)、DOM要素のコレクション、jQueryオブジェクトなどです。

ほぼすべての配列プロトタイプメソッドはジェネリックです(仕様変換レポートとして)-「意図的に汎用」機能)。起動されたコンテキストのオブジェクトが配列である必要はありません。たぶん.concatそうではない。多くの人がそのようなデザインに精通していると思います:

 Array.prototype.slice.call(arguments, 1); //  [].slice.call(arguments, 1); 

面倒で不明瞭。

ECMAScript 6はArray.fromメソッドを追加します。これにより、反復可能な配列のようなオブジェクトを配列にキャストできます。

 Array.from(arguments).slice(1); 

それは便利に思えますが、安くはありません-最も単純な操作であっても、ほとんどの場合、イテレーターのかなり重いプロトコルを介して、オブジェクト全体を配列にキャストする必要があります。

MozillaのJavaScript バージョン1.6で、それに応じて2005年にFirefoxで、配列メソッドの静的バージョンが配列メソッドとともに追加され、後にECMAScript 5の一部になりました。彼らはECMAScriptの第5版または第6版には入りませんでしたが、長い間Strawman開発ブランチに存在していましたが、私は個人的にECMAScriptの将来のバージョンの1つに登場することを望みます。それらをライブラリに追加します-それらは基本的に実装されており、すでに防火対策が施されているため、自転車ではなく松葉杖として分類します。

 Array.slice(arguments, 1); Array.join('abcdef', '+'); // => 'a+b+c+d+e+f' var form = document.getElementsByClassName('form__input'); Array.reduce(form, function(memo, it){ memo[it.name] = it.value; return memo; }, {}); // => , {name: '', age: '42', sex: 'yes, please'} 

遅延実行:setTimeout、setInterval、setImmediate



setTimeout、setInterval


おそらく、すべての通常で始まりsetTimeoutsetInterval多くの人は、標準(W3CWHATWGに従って、これらの関数がコールバックと遅延時間に加えて、転送されたコールバックを開始する追加の引数を取ることを知りませんしかし、ここでは、いつものように、問題はIEにあります。IE9ではsetTimeoutsetInterval2つの引数のみ取り、これは複数行のラッパーで処理されます。

 // : setTimeout(log.bind(null, 42), 1000); // : setTimeout(log, 1000, 42); 

setImmediate


JavaScriptはシングルスレッドであり、時には非常に迷惑です。クライアントでの長時間の重いコンピューティングは、ユーザーインターフェイスを中断し、サーバーでは、処理を要求します。この場合、重いタスクを軽いサブタスクに分割し、非同期で実行し、その間で入出力を行うことができます。

また、JavaScriptでは(これまでのところ)末尾再帰は最適化されていません。関数が特定の回数だけ再帰的に呼び出されると、エラーが生成されRangeError: Maximum call stack size exceededます。この数は、プラットフォームに応じて、数百から数万まで変化します。非同期再帰呼び出しは、スタックオーバーフローを防ぎます。確かに、再帰呼び出しは通常、通常のループに簡単に書き換えられるため、望ましい方法です。

このような問題を解決するには、次を使用できますsetTimeout最小限の遅延で、しかし非常にゆっくりと判明します。最小setTimeout仕様遅延は4ミリ秒で、一部のプラットフォームではさらに長くなります。合計、最大、最新のブラウザーでは1秒あたり最大250回、たとえばIE8では最大64回の再帰呼び出し。効果的な遅延実行を行う方法があるため、サイクルすることが必要であり、必要です。

私がNode.jsにいた場合process.nextTick、クライアントが待たなかったところから助けが来ました。 IE10で、MicrosoftはsetImmediateメソッドを追加しました。このメソッドは、I / Oが完了した直後に実行するタスクを設定し、W3Cを標準化することを提案しました。彼は後のNode.jsに登場します。 FFとChromiumは追加を急いでいません。人気のこのポリュフェモス。

関数の効果的な遅延実行を実装するには、非常に多くの方法があります。さまざまなプラットフォームで最大のパフォーマンスを実現するには、多くのプラットフォームを使用する必要があります(上記のポリフィールのメソッドと同様)。これは:


 setImmediate(function(arg1, arg2){ console.log(arg1, arg2); // =>        }, '   ', '  '); clearImmediate(setImmediate(function(){ console.log('   '); })); 

さらにsetImmediate、より高速な代替手段-コンセプトasap(できるだけ早く、ライブラリ)-可能であれば、マイクロタスクを作成します。これは、入出力の前に実行されます。このようなグローバルメソッドを言語の標準に追加し、tc39で考えてくださいたぶんそれはライブラリに追加されるべきですか?

コンソール



コンソールは、ブラウザーとサーバーの両方で唯一の汎用出力およびデバッグツールです。同時に、コンソールはECMAScript仕様の一部ではなく、まったく標準化されていません。放棄されたアウトライン仕様があり、すべてのプラットフォームでの実際の実装は異なります。

IE7では、コンソールが完全に欠落しています。一部のブラウザでは、「ハイゼンベルグコンソール」はconsoleユーザーが視聴しているときにのみ定義されます。そして、もちろん、すべてのメソッドがすべてのプラットフォームで利用できるわけではありません。Firebug Liteのスタイルでコンソールを発明するのではなく、メソッドのスタブを作成して、可用性を確認せずにメソッドを使用できるようにします。

 // : if(window.console && console.log)console.log(42); // : console.log(42); 

Firefoxとクロム方法コンソールでは、コンテキストを使い果たすべきであるconsoleような-あなたはコールバックがネクタイ持っているようにそれらを与えたいのであれば、#をconsole.log.bind(console)IE、Firebug、およびNode.jsでは、バインドせずに渡すことができ、これははるかに便利です。したがって、メソッドをオブジェクトにバインドしますconsole

 // : setTimeout(console.log.bind(console, 42), 1000); [1, 2, 3].forEach(console.log, console); // : setTimeout(console.log, 1000, 42); [1, 2, 3].forEach(console.log); 

ただし、ここで1つの問題があります。一部のプラットフォームでは、コンソールメソッドを呼び出すときに、メソッドの呼び出し元の行も表示されます。コンソールをオーバーライドする場合は、この行は、ラインになりますsore.jsこれが重要な場合は、コンソールモジュールなしでライブラリを構築できます。

コンソールは標準化されていないため、小さなギャグを追加します。


コンソールへの出力をオフにする機能。もちろん、本番環境では、コードにコンソールメソッド呼び出しを残さずに、ペンまたはより高度なツールを使用してそれらを削除するが良いですが、これは便利であり、必ずしも必要ではありません。

 console.disable(); console.warn(' ,     .'); console.enable(); console.warn('  .'); 

コンテキストバインディングとコンソールを無効にする機能も、たとえばTheShockのこのライブラリに存在します

パート2:自転車


オプションなし、オプションなし。
私は世界の半分が欲しかった-自転車に十分。
-中毒のループ

この記事/ライブラリの文脈において、自転車はすべて標準化されていない機能です。実際、私の意見では、標準言語ライブラリに欠けているものは、たとえ現在利用可能なすべての標準に従って実装されていてもです。これには、ES7 +で提供されるものも含まれます。これは、これが繰り返しレビューされることや拒否されることからもほど遠いためです。

データ分類



ここでは、完全に平凡さから始めます。 JavaScriptではECMAScriptの5仕様のために持っている 6つのデータの種類をUndefined, Null, Boolean, String, NumberObject。 ECMAScript 6は、別のデータ型-#をSymbol追加します。データのタイプを判別するには、演算子がありますtypeof。それはまさにそれが具体的にどのように機能するかです。だから、歴史的に、というtypeof nullリターン'object'解決しようとするとハーモニーで、それは成功しませんでした。タイプのObject typeof場合'object''function'内部メソッドの可用性に応じて、またはを返します[[Call]]。合計すると、演算子typeofはプリミティブのデータ型のみを返し、すべてのプリミティブではありません。

変数がnull単に、それと比較するだけで十分です。その後Object、毎回何百ものコードを記述するか、ヘルパーを作成する必要があります。もちろん、そうすることができます- Object(foo) === fooしかし、この解決策は最速とはほど遠い -プリミティブをオブジェクトに導きます。 ES6の初期のドラフトではメソッドが存在Object.isObjectしていましたが、どうやらそれを修正する試みのためtypeof nullに削除されました。また、変数がオブジェクトかどうかを確認するには、常にする必要があります。そこで、どこにも簡単に実装されたヘルパーを追加しましょうObject.isObjectsandbox):

 Object.isObject = function(it){ return it != null && (typeof it == 'object' || typeof it == 'function'); } // ... typeof {}; // => 'object' typeof isNaN; // => 'function' typeof null; // => 'object' Object.isObject({}); // => true Object.isObject(isNaN); // => true Object.isObject(null); // => false 

しかし、オブジェクトの分類がより興味深い。オペレーターinstanceofはプロトタイプチェーンをチェックします。プロトタイプからオブジェクトを作成する場合、またはオブジェクトをプロトタイプとして設定Function.prototypeする場合、関数にはなりません。インスタンスプロパティconstructorは何も保証できませんがconstructor.name、コード圧縮ですべての意味を失うだけでなく、IEはまだサポートされていません。内部プロパティは、オブジェクトの分類に役立ちます[[Class]]。それを引き裂く唯一の方法は、多くの人になじみのある恐ろしいデザインですObject.prototype.toString.call(foo).slice(8, -1)

 Object.prototype.toString.call(1).slice(8, -1); // => 'Number' Object.prototype.toString.call([]).slice(8, -1); // => 'Array' Object.prototype.toString.call(/./).slice(8, -1); // => 'RegExp' 

内部受付クラスに基づき、ほとんどのライブラリは、型のユーティリティのセット追加#をArray.isArray:Object.is タイプ砂糖のためには、_.is タイプUndescoreなど

私たちは、異なる動作を- 1つの普遍的な方法Object.classof、データの分類のための声明と同様に、typeof!LiveScript)。

ここにObject.prototype.toString.call(foo).slice(8, -1)いくつかの問題があります:

突然、ECMAScript 6には通常、などのオブジェクトの内部プロパティがありません[[Class]]Object#toStringES6は、特別な内部プロパティをチェックすることにより、変数がUndefined, Null, Array, String, Arguments, Function, Error, Boolean, Number, DateまたはRegExp属していることを返し、残りについては#文字で ヒントを検索しSymbol.toStringTagます。メソッドがヒントを見つけ、それが組み込みの「クラス」の名前ではない場合-それを返しますが、見つかりません- Object

ロジック修正Object#toStringに起因して、利益を#1、意地の悪いけど楽しいバグ、我々は同時に壊すことなく、IE8-でそれを行うことができますfor-inもちろん、このアプローチをmethodに実装しObject.classofます。ボーナスとして、カスタムコンストラクターのインスタンスを分類する機能があります。

 var classof = Object.classof; classof(null); // => 'Null' classof(undefined); // => 'Undefined' classof(1); // => 'Number' classof(true); // => 'Boolean' classof('string'); // => 'String' classof(Symbol()); // => 'Symbol' classof(new Number(1)); // => 'Number' classof(new Boolean(true)); // => 'Boolean' classof(new String('string')); // => 'String' var fn = function(){} , list = (function(){return arguments})(1, 2, 3); classof({}); // => 'Object' classof(fn); // => 'Function' classof([]); // => 'Array' classof(list); // => 'Arguments' classof(/./); // => 'RegExp' classof(new TypeError); // => 'Error' classof(new Set); // => 'Set' classof(new Map); // => 'Map' classof(new WeakSet); // => 'WeakSet' classof(new WeakMap); // => 'WeakMap' classof(new Promise(fn)); // => 'Promise' classof([].values()); // => 'Array Iterator' classof(new Set().values()); // => 'Set Iterator' classof(new Map().values()); // => 'Map Iterator' classof(Math); // => 'Math' classof(JSON); // => 'JSON' function Example(){} Example.prototype[Symbol.toStringTag] = 'Example'; classof(new Example); // => 'Example' 

辞書



JavaScriptでは、オブジェクトと辞書(連想配列)は1つです。これには長所があります-非常に便利なようですが、JavaScriptオブジェクトシステムに基づいたデータ交換形式が非常に一般的JSONであり、マイナスであることは無駄ではありません

辞書の場合、キーによって要素を取得することとプロトタイプからメソッドを取得することに違いはありません。これは、オブジェクト(および中括弧付きの表記法で指定されたオブジェクト)の下にプロトタイプがある場合Object.prototype、辞書の基本操作を中断します。オブジェクトの場合、これらは拡張子の制限Object.prototypeです。

ECMAScript 6ではすでに述べた新しいタイプのキー値コレクション登場しましたMap。その速度は、オブジェクトの速度よりも速い場合があります(それ自体では、多相性動物には適用されません)。ほとんどの場合、それは置き換えられません。でMapオブジェクト辞書とは異なり、単純なリテラル表記はありません。プロパティへのアクセスはメソッドを介して行われます-それほど簡潔ではありません。Mapすべての人に愛されることJSONは決してなく、それほど普遍的ではありません。通常、辞書キーには文字列以外には何も必要ありません。


問題:Object.prototypeと辞書


Object.prototypeMozilla Developer Networkによって提案されているように、実装に応じて、以下があります。

 Object.prototype.constructor(); Object.prototype.hasOwnProperty(); Object.prototype.isPrototypeOf(); Object.prototype.propertyIsEnumerable(); Object.prototype.toLocaleString(); Object.prototype.toString(); Object.prototype.valueOf(); Object.prototype.__proto__; Object.prototype.__count__; Object.prototype.__parent__; Object.prototype.__noSuchMethod__; Object.prototype.__defineGetter__(); Object.prototype.__defineSetter__(); Object.prototype.__lookupGetter__(); Object.prototype.__lookupSetter__(); Object.prototype.eval(); Object.prototype.toSource(); Object.prototype.unwatch(); Object.prototype.watch(); 

これはどのように私たちを脅かすことができますか?

プリミティブな電話帳があり、ユーザーがそのAPIにアクセスできるとします。

 var phone = (function(){ var db = { '': '+7987654', '': '+7654321' }; return { has: function(name){ return name in db; }, get: function(name){ return db[name]; }, set: function(name, phone){ db[name] = phone; }, delete: function(name){ delete db[name]; } }; })(); 

私達は得る

 console.log(phone.has('')); // => true console.log(phone.get('')); // => '+7987654' console.log(phone.has('')); // => false console.log(phone.get('')); // => undefined console.log(phone.has('toString')); // => true console.log(phone.get('toString')); // => function toString() { [native code] } 

プロパティは、存在しない場合、プロトタイプチェーンから取得され、in同様にその存在をチェックします。プロトタイプチェーンを考慮せずに、オブジェクトのプロパティの存在をチェックinするメソッドhasOwnProperty追加/置き換えましょう私達は得る

 // ... has: function(name){ return db.hasOwnProperty(name); }, get: function(name){ if(db.hasOwnProperty(name))return db[name]; }, // ... console.log(phone.get('')); // => '+7987654' phone.set('hasOwnProperty', '+7666666'); //   "" console.log(phone.get('')); // TypeError: string is not a function 

特に「電話帳」がサーバー側にある場合は特に深刻です。プロトタイプメソッドはオーバーライドできます。したがって、hasOwnPropertyオブジェクトから結び付けられていないメソッドを使用する必要があります。誰にとっても面倒なチェックを使用する必要があります。ほぼそのようなゴミ

 // ... has: function(name){ return Object.prototype.hasOwnProperty.call(db, name); }, get: function(name){ if(Object.prototype.hasOwnProperty.call(db, name))return db[name]; }, // ... 

言語でこの問題を解決するために、プロパティがに似た独自のプロパティである場合、検証演算子が役立ちinます。

問題は終わったと決めましたか?次のようなものはありません

 phone.set('__proto__', '+7666666'); //   "" console.log(phone.get('__proto__')); // => undefined 

ではObject.prototype、「魔法」のgetter / setterメソッドがあり__proto__ダメージに、例えば、プロパティを横断するとき-このキーは無視され、オブジェクトされるためにプリミティブの設定、。古いエンジンには、他の「マジック」プロパティがありました。ここで役立つのはObject.definePropertyサンドボクサーのみです

 // ... set: function(name, phone){ Object.defineProperty(db, name, { enumerable : true, configurable: true, writable : true, value : phone }); }, // ... 

特に話をしない辞書をバイパスについて-それは本当に悪いではない場合は、辞書を横断するとき、辞書は性質perecheslyatプロトタイプが含まれていないfor-in、あなたはテストなしで行うことができますhasOwnPropertyそれはただのバグです #「列挙不可能な列挙可能」プロパティfor-inObject.prototypeそれが配置されている辞書を介して、古いIEでは劣っています。

ECMAScript 5には、プロトタイプなしでオブジェクトを作成する方法があります- Object.create(null)最初から提案されたメソッド(sandboxの実装を使用できます

 var phone = (function(){ var db = Object.create(null); Object.assign(db, { '': '+7987654', '': '+7654321' }); return { has: function(name){ return name in db; }, get: function(name){ return db[name]; }, set: function(name, phone){ db[name] = phone; }, delete: function(name){ delete db[name]; } }; })(); 

すべてが素晴らしく、その作成と初期化だけObject.assignが、非常にコンパクトではありません。

そのような辞書にはプロトタイプがないためtoStringメソッドはありませんvalueOfこれはどのように私たちを脅かしますか?
好きな人にとっては、しかし私にとってはマイナスよりもプラスです。

コンストラクターDict


そのため、辞書を作成してObject.create(null)記入するのは、辞書を作成するよりもはるかに面倒です{}もちろん、最も美しい解決策は、リテラルの言語に辞書を追加することですが、これは、少なくとも短期的には、ありそうもないことです。リテラルによる初期化の可能性はありません。記録がありますが、{__proto__: null, foo: 'bar'}どこでもサポートされていません。現時点ではコードの最適化つながりますが、それでもかなり面倒です。かなり興味深い決定

が議論されました- 速記として「コンストラクター」を作成するDictObject.create(null)それが今どのようになっているのか、それが何をしているのか、私にはわかりません。しかし、少し拡大してみてはどうでしょうか?同時に、オブジェクトを辞書として扱うためのメソッドの名前空間を取得します。イテレータentriesまたはイテレータのないオブジェクトで初期化する機能を追加します。これは、辞書のバージョン番号のArray.fromようなものです

以来Dict() instanceof Dict、それは動作しません、とObject.classof(Dict())戻り'Object'、辞書の識別方法に追加しますDict.isDict

大体このような

 function Dict(props){ var dict = Object.create(null); if(props != null){ if(Symbol.iterator in props){ for(var [key, val] of props)dict[key] = val; } else Object.assign(dict, props); } return dict; } Dict.prototype = null; Dict.isDict = function(it){ return Object.isObject(it) && Object.getPrototypeOf(it) === null; } // ... var map = new Map([['a', 1], ['b', 2], ['c', 3]]); Dict(); // => {__proto__: null} Dict({a: 1, b: 2, c: 3}); // => {__proto__: null, a: 1, b: 2, c: 3} Dict(map); // => {__proto__: null, a: 1, b: 2, c: 3} Dict([1, 2, 3].entries()); // => {__proto__: null, 0: 1, 1: 2, 2: 3} Dict((for([k, v] of map)if(v % 2)[k + k, v * v])); // => {__proto__: null, aa: 1, cc: 9} Dict.isDict({}); // => false Dict.isDict(Dict()); // => true 

プロトタイプを持つ辞書を安全に使用するためのメソッド


ディクショナリのように、その下にプロトタイプがあるオブジェクトで作業する必要がある場合、独自のプロパティで安全に作業するためのメソッドを追加します。

Dict.has-陳腐な静的バージョンhasOwnProperty。ドラフトのECMAScript 6は、モジュールReflect-のためのプラグのセットProxy方法の静的バージョンは、最近まで存在していたhasOwnProperty-方法Reflect.hasOwn。ただし、ドラフト仕様の最近のバージョンでは、このメソッドは削除されています。

Dict.get-プロパティが自分のものであるかどうかを確認するためにキーで値を取得します。戻らないundefined

Dict.set-絶対に妄想的な方法。などのセッターを無視して、ディクショナリのプロパティを設定できます__proto__。を使用しdefinePropertyます。

さて、オペレーターはdelete既に正常に機能しています。



 var dict = {a: 1, b: 2, c: 3}; console.log(Dict.has(dict, 'a')); // => true console.log(Dict.has(dict, 'toString')); // => false console.log(Dict.get(dict, 'a')); // => 1 console.log(Dict.get(dict, 'toString')); // => undefined Dict.set(dict, '__proto__', 42); console.log(Dict.get(dict, '__proto__')); // => 42 

辞書を操作する方法


ECMAScript 5によって配列プロトタイプにバイパス(forEach, map, someなど)するために追加されたメソッドは非常に便利です。辞書の静的な対応物は、ほぼすべての汎用フレームワーク/ライブラリに存在します。しかし、それらを標準に追加しても進展はありません。

それらをモジュールの一部として追加しますDict。ここではすべてがシンプルで、メソッドは静的メソッドの配列メソッドに似ています。それらは:Dict.forEach, Dict.map, Dict.filter, Dict.some, Dict.every, Dict.find, Dict.findKey, Dict.keyOf, Dict.includes , Dict.reduce, Dict.turnKey名前のindex配列メソッドに対応します。 「正しい」バージョンとオプションのインデックス引数(まだ?)はありません。オブジェクトキーをトラバースする順序はどこでも同じではないためです。オブジェクトの独自の列挙要素のみが検索されます。これらのメソッドは、Array.fromまたはと同じようにジェネリックArray.ofです。たとえばDict.map(dict, fn)、新しいを返しますDict、そしてDict.map.call(Object, dict, fn)-新しいObjectしかし、一般的に、すべては原始的で退屈で、他のすべての場所(sandboxと同様です

 var dict = {a: 1, b: 2, c: 3}; Dict.forEach(dict, console.log, console); // => 1, 'a', {a: 1, b: 2, c: 3} // => 2, 'b', {a: 1, b: 2, c: 3} // => 3, 'c', {a: 1, b: 2, c: 3} Dict.map(dict, function(it){ return it * it; }); // => {a: 1, b: 4, c: 9} Dict.filter(dict, function(it){ return it % 2; }); // => {a: 1, c: 3} Dict.some(dict, function(it){ return it === 2; }); // => true Dict.every(dict, function(it){ return it === 2; }); // => false Dict.find(dict, function(it){ return it > 2; }); // => 3 Dict.find(dict, function(it){ return it > 4; }); // => undefined Dict.findKey(dict, function(it){ return it > 2; }); // => 'c' Dict.findKey(dict, function(it){ return it > 4; }); // => undefined Dict.keyOf(dict, 2); // => 'b' Dict.keyOf(dict, 4); // => undefined Dict.includes(dict, 2); // => true Dict.includes(dict, 4); // => false Dict.reduce(dict, function(memo, it){ return memo + it; }); // => 6 Dict.reduce(dict, function(memo, it){ return memo + it; }, ''); // => '123' Dict.turn(dict, function(memo, it, key){ memo[key + key] = it; }); // => {aa: 1, bb: 2, cc: 3} Dict.turn(dict, function(memo, it, key){ it % 2 && memo.push(key + it); }, []); // => ['a1', 'c3'] 

メソッドのチェーンについては、完全に明白な理由のためにDictそれらはモジュール内になく、予期されていません。ここでの救いは抽象的な参照かもしれませんしかし、$for#のMapフレームワーク内では、それらが表示される可能性は十分にあります。

辞書の繰り返し


ES6の明るい未来は#イテレータ#ループでfor-of近づいています。それは辞書としての単なるオブジェクトであり、これは暖かくも冷たくもない-ES6ではイテレータは提供されていません。したがって、それらをループしたりfor-ofMap辞書で初期化するなどの簡単な方法はありません。メソッドを追加.keys, .valuesし、.entries中にObject.prototypeは低い-十分なごみがあり、以前の問題の説明を参照してください。..しかし、非常に可能性の高い他の2つのシナリオ:

最初の-名前空間でイテレータを返す静的メソッドを追加しますDict- Dict.{keys, values, entries}しかし、すでに書いたように、このモジュールを標準に追加する見込みがあるため、私は知りません。

2つ目は、タイプごとにメソッドObject.{values, entries}追加することですObject.keys、イテレータではなく配列を返し、オブジェクトをバイパスするためにすでに配列イテレータを通過しています。

これがどうなるのか分かりません。推測するのが怖いです。辞書の値の配列を取得するには、中間配列を使用してオブジェクトを反復処理するのと同じように、かなり重いイテレータのプロトコルを使用するのは合理的ではありません。そのため、これは部分的に機能し、相互に複製されますが、ライブラリに両方のメソッドセットを実装します。

 var dict = {a: 1, b: 2, c: 3}; console.log(Object.values(dict)); // => [1, 2, 3] console.log(Object.entries(dict)); // => [['a', 1], ['b', 2], ['c', 3]] for(var key of Dict.keys(dict))console.log(key); // => 'a', 'b', 'c' for(var [key, val] of Dict.entries(dict)){ console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } $for(Dict.values(dict)).of(console.log); // => 1, 2, 3 new Map(Dict.entries(dict)); // => Map {a: 1, b: 2, c: 3} new Map((for([k, v] of Dict.entries(dict))if(v % 2)[k + k, v * v])); // => Map {aa: 1, cc: 9} 

見込み客


#文字だけDictなくObject.create(null)、イテレータとオブジェクトによる初期化の可能性を示す短縮形だけでなく、文字列キーを含まないプロトタイプを備えた本格的なコンストラクタを作成して、さらに先へ進むことも可能です。このようなもの

 function Dict(props){ if(!(this instanceof Dict))return new Dict(props); if(props != null){ if(Symbol.iterator in props){ for(var [key, val] of props)this[key] = val; } else Object.assign(this, props); } } Dict.prototype = Object.create(null); Dict.prototype[Symbol.toStringTag] = 'Dict'; Dict.prototype[Symbol.iterator] = function(){ return Dict.entries(this); }; 

それは私たちに何を与えますか?


ただし、少なくとも、IE8が最終的に消滅し、FirefoxがECMAScript 6イテレータプロトコルに完全に切り替わるまで、このアプローチの実装を延期する理由があります。Dictそこに着きます。

部分適用



おそらく、ECMAScriptの5で最も有用な技術革新の一つは、メソッドだったFunction#bindこれは、この方法部分的に適用するだけでは完全に開示されない可能性があるだけです。この章では、次のようなことを検討します。


一つは、追加することができカリー化を、しかし、JavaScriptはカリー化と部分適用が非常に頻繁に必要とされていないです。メソッドの「正しい」バージョンのように。トピックに関する適切な(そしておそらく、有名な)記事へリンク追加します。

コンテキストバインディングのない部分的なアプリケーション


Function#bind部分的なアプリケーションとコンテキストバインディングを組み合わせますthis後者は常に必要というわけでthisはなく、この場合、結び付けられる必要がある「余分な」引数だけがバインドされているわけではありません。部分的に適用された機能を起動するコンテキストが事前に不明Function#bindな場合、この方法は適用できません。たとえば、これがプロトタイプメソッド(sandbox)の場合:

 Array.prototype.compact = [].filter.bind(Array.prototype, function(val){ return val != null; }); [0, null, 1, undefined, 2].compact(); // => [] -     Array.prototype,     //    : Array.prototype.compact = function(){ return this.filter(function(val){ return val != null; }); }; [0, null, 1, undefined, 2].compact(); // => [0, 1, 2]; 

バインドせずに部分的なアプリケーションメソッドを追加しますthis- Function#partsandbox):

 Array.prototype.compact = [].filter.part(function(val){ return val != null; }); [0, null, 1, undefined, 2].compact(); // => [0, 1, 2]; var fn = console.log.part(1, 2); fn(3, 4); // => 1, 2, 3, 4 

任意の引数の部分的な使用


多くの場合、部分的に適用する場合、任意の引数を渡す必要があります。たとえば、最初の2ではなく、2番目と4番目または2番目と3番目だけです。ここでFunction#bind彼女は私たちを助けることができません-私は特定のケースごとに手動でラッパーを書く必要があります。

 function fn1(a, c){ console.log(a, 2, c, 4); }; fn1(1, 3); // => 1, 2, 3, 4 function fn2(b, c){ console.log(1, b, c, 4); }; fn2(2, 3); // => 1, 2, 3, 4 

このタスクを容易にするために、プレースホルダーを追加します。プレースホルダーは、最終関数が呼び出されたときに渡される引数を置き換えるオブジェクトです。プレースホルダーへの参照として、グローバル変数はそれ自体を要求します_(たとえば、LiveScriptサンドボックスなど)が、Undescore.jsライブラリーはこの変数を使用します(ちなみに、それはプレースホルダーでもあります_.partial)およびその名前空間としてLoDashです。それらとの競合を避けるために、_存在しない場合にのみ新しいグローバルオブジェクトを作成し、操作中はグローバルオブジェクトを使用します_。ネイティブオブジェクトを展開しないアセンブリの場合、オブジェクトをプレースホルダーとして使用しますcore._

 var fn1 = console.log.part(_, 2, _, 4); fn1(1, 3); // => 1, 2, 3, 4 var fn2 = console.log.part(1, _, _, 4); fn2(2, 3); // => 1, 2, 3, 4 fn1(1, 3, 5); // => 1, 2, 3, 4, 5 fn1(1); // => 1, 2, undefined, 4 

Function#by同様のメソッドも追加Function#bindしますが、引数にプレースホルダーを使用する可能性があります。まとめてFunction#bindプレースホルダを操作することもできますが、これは仕様違反であり、この方法はほとんどすべてのエンジンですでに非常にブレーキがかかっています。

 var fn = console.log.by(console, _, 2, _, 4); fn(1, 3, 5); // => 1, 2, 3, 4, 5 

オブジェクトからメソッドを抽出


ほとんどの場合、たとえば、関数にコールバックを渡すとき、メソッドを取得元のオブジェクトにバインドする必要があります。そして、ここで問題が発生します- fn(foo.bar.baz.bind(foo.bar))。私たちはfoo.bar2回書くことを余儀なくされていますが、これはDRYの原則に明らかに違反しています。将来抽象的な参照がこの問題から救われることを望みますが、提案された実装問題を解決しません。おそらく、最もおいしいとエレガントな解決策は、保全状況、同じへのオペレータアクセスの言語で追加することです~LiveScriptから - fn(foo.bar~baz)砂場)。

キーによってオブジェクトからメソッドを抽出する場合を除いて、頭に浮かぶライブラリに基づいた問題に対する多くの解決策はありません。これは、例えば、静的な方法である_.bindKeyからLoDash(しかし、初期結合を有する)が、それはまた、非常に面倒でさらに可読性を悪化させる、またはメソッドと機能的に類似しておりObject.prototype、例えば、Object#boundToからEddy.js

どんなに恐ろしく聞こえても、にメソッドを追加しObject.prototypeます。Object.prototype短いキー文字列でメソッドを拡張してもリスクはありません。少なくとも現時点では、競合を回避することfor-inは難しく、IE8-でそれを破ります。この章の前半で、すでにグローバル変数を使用しました_。不要なエンティティを作成しないよう、また簡潔にするために、ここで適用します。オブジェクト_メソッドを置き換えますtoString(それぞれ、Undescore.jsまたはLoDashと組み合わせて使用​​する場合は、core.jsを接続する必要があります)それらの後)。#character polyphile keyに似た、一意のキー文字列を返します。このキーにメソッドcを追加しますObject.prototype。汚いハックを使用することにより#面白いバグ我々は壊すことなく、この方法およびIE8-を追加しますfor-in

合計、私たちが始めた例から、私たちは得ますfn(foo.bar[_]('baz'))-理想からはほど遠いですが、少なくともオブジェクトの2番目の言及を取り除きました。返されたメソッドはキャッシュされます。

 ['foobar', 'foobaz', 'barbaz'].filter(/bar/[_]('test')); // => ['foobar', 'barbaz'] var has = {}.hasOwnProperty[_]('call'); console.log(has({key: 42}, 'foo')); // => false console.log(has({key: 42}, 'key')); // => true var array = [] , push = array[_]('push'); push(1); push(2, 3); console.log(array); // => [1, 2, 3]; 

良い方法では、IE8ライブラリのサポートを拒否した後、メソッドの名前を変更する必要があります。名前を変更しないと、どういうわけtie, boundTo, bindKey怖いです:) またはそのような何か、最も競合の少ないキーを選択します。ここでES6から

使用Proxyする方がはるかにきれいです-メソッドにキーを渡す代わりにプロパティに定期的にアクセスしますfn(foo.bar[_].baz)が、Proxy同類(ゲッター、オブジェクトトラバーサル、すべてのメソッドのバインド)がなければ、パフォーマンスを大幅に損なうことなく、まだ余裕がありません。

プロキシを使用した例、これまでは夜間火災でのみ動作します
 var _ = Symbol(); Object.defineProperty(Object.prototype, _, { get: function(){ return new Proxy(this, { apply: function(){ /*    [_]    */ }, get: function(context, name){ return context[name].bind(context); } }); } }); ['foobar', 'foobaz', 'barbaz'].filter(/bar/[_].test); // => ['foobar', 'barbaz'] var has = {}.hasOwnProperty[_].call; console.log(has({key: 42}, 'foo')); // => false console.log(has({key: 42}, 'key')); // => true var array = [] , push = array[_].push; push(1); push(2, 3); console.log(array); // => [1, 2, 3]; 

引数の数を制限する


オプションの引数の問題については、この記事で説明しますその一例はparseInt-非常に不明瞭で、たとえば文字列を数字にキャストすることを誰も気にNumberしません。たとえば、追加の引数を期待ていません。重要なのは「危険」ではなく、追加のラッパーを作成する必要があることです。

たとえば、配列のすべての要素をコンソールに出力し、要素自体のみを印刷します。

 [1, 2, 3].forEach(console.log); // => 1 0 [1, 2, 3] // => 2 1 [1, 2, 3] // => 3 2 [1, 2, 3] 

.forEach他の多くのメソッドと同様に、このメソッドはコールバックにオプションの引数(インデックスと配列自体)を提供します。しかし、それらは必要ありません。そのため、コールバックを別の関数にラップする必要があるたびに:

 [1, 2, 3].forEach(function(it){ console.log(it); }); // => 1, 2, 3 

上記の記事では、関数の引数を制限する方法が提案されましたFunction#onlyそのオプションを実装します。最初の引数は引数の最大数であり、2番目の引数はオプションであり、コンテキストです。

 [1, 2, 3].forEach(console.log.only(1)); // => 1, 2, 3 

もちろん、引数の最大数が1の場合、ES6またはコーヒーのような言語の矢印関数を使用する方が簡単であれば可能ですが、それ以上の場合はすでに問題があります。

日付のフォーマット



JavaScriptで日付をフォーマットする簡単なタスクはそれほど単純ではないように思われます。「11/18/2014 06:07:25」形式の文字列を取得する必要がある場合はどうなりますか?すべてがかなり怖いです:

 var date = new Date; function lz2(it){ return it > 9 ? it : '0' + it; } var format = [date.getDate(), date.getMonth() + 1, date.getFullYear()].map(lz2).join('.') + ' ' + [date.getHours(), date.getMinutes(), date.getSeconds()].map(lz2).join(':'); console.log(format); // => '18.11.2014 06:07:25 ' 

また、たとえば、「火曜日、2014年11月18日、6:07:25」という形式の文字列を取得する必要がある場合はどうでしょうか。

記事の冒頭で、仕様ある国際化標準ECMA402が言及されました。この規格は、日付、数値、文字列比較のローカライズされたフォーマットの手段を含むIntlオブジェクトをJavaScriptに追加します。基本的なレベルでこの記事ではIntl について説明します。さらに、この標準は、2つの引数を追加することでメソッドオーバーロードします:ローカライズとフォーマットオプション。それらを用いて、上記の形式のライン近くを得ることができるようにDate#toLocaleString, Date#toLocaleDateString, Date#toLocaleTimeString

 new Date().toLocaleString('ru-RU', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }); // => ', 18  2014 ., 6:07:25' 

もちろん面倒ですが、何もしないよりはその方がいいです。標準のサポートに関して-一般に、悪くない。Chrome、Opera、IE11、および最近ではFirefoxをサポートしています。しかし、通常、IE10、Safari、モバイルプラットフォームのサポートが必要であり、悪魔はまだ何を知っています。この場合にはポリフィルがあります。しかし問題は、ロケールを考慮しなくても、この機能の実装が重くなりすぎることです。このため、のCore.jsで無ポリュフェモスECMA402

簡単な日付フォーマットを追加します。


simpleはどういう意味ですか?どのくらいの頻度で完全なローカライズが必要ですか、または他の高度な日付ツールは何ですか?私は本当にそうではありません。通常、書式文字列を使用した簡単で便利な日付書式が必要です。必要な場合は、Moment.jsやpolyfil を接続する必要はありませんIntlここで、日付を操作するためのモジュール全体は数十行です。

メソッドDate#formatとそのUTCバージョンDate#formatUTCsandboxを追加します。

 new Date().format('W, D MM Y ., h:mm:ss', 'ru'); // => ', 18  2014 ., 6:07:25' new Date().formatUTC('W, D MM Y ., h:mm:ss', 'ru'); // => ', 18  2014 ., 0:07:25' 

書式文字列の単純さと読みやすさのために、表記法を保護することに煩わされません。それらの最小値は利用可能ですが:

 s |  | 0-59 ss | , 2  | 00-59 m |  | 0-59 mm | , 2  | 00-59 h |  | 0-23 hh | , 2  | 00-23 D |  | 1-31 DD | , 2  | 01-31 W |  ,  |  N |  | 1-12 NN | , 2  | 01-12 M | ,  |  MM | ,  |  Y | ,  | 2014 YY | , 2  | 14 

ライブラリにはすでにロシア語(ru)および英語(en)ロケールが含まれていますロケールは、メソッドcore.localeまたはメソッドの2番目の引数Date#formatDate#formatUTCsandbox)のいずれかによって指定されます

 new Date().format('W, D MM Y', 'ru'); // => ', 18  2014' new Date().format('W, D MM Y'); // => 'Tuesday, 18 November 2014' core.locale('ru'); new Date().format('W, D MM Y'); // => ', 18  2014' 

ロケール形式を以下に示します。独自のコードでは、自分自身を制限できますがcore.addLocale、ネイティブオブジェクトを展開せずにライブラリを構築できるため、ユニバーサルロケールモジュールは次のようになります。

 (typeof core != 'undefined' ? core : require('core-js/library')).addLocale('ru', { weekdays: ',,,,,,', months: ':|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|' }); 

いくつかの例

 new Date().format('DD.NN.YY'); // => '18.11.14' new Date().format('hh:mm:ss'); // => '06:07:25' new Date().format('DD.NN.Y hh:mm:ss'); // => '18.11.2014 06:07:25' new Date().format('W, D MM Y '); // => ', 18  2014 ' new Date().format('D MM, h:mm'); // => '18 , 6:07' new Date().format('M Y'); // => ' 2014' 

オブジェクトAPI



最初の問題:ECMAScript 5では、オブジェクトリテラルでゲッターとセッターを宣言する機能が追加されました。ただしObject.defineProperty/のみを使用して既存のオブジェクトのゲッター/セッターを追加できますObject.defineProperties。これにより、完全な(非列挙または再定義できないなどの追加オプションが必要ない場合)記述子オブジェクトを転送する必要があり、これは面倒です。

メソッドObject.assign加えて、ECMAScript 6ではObject.mixin、記述子を考慮して、ソースオブジェクトのプロパティをターゲットオブジェクトにコピーするメソッドを追加する予定でした。さらに、このメソッドは、キーワードを介して取得したソースオブジェクトのメソッドの親を再割り当てすることになっていますsuper。しかし、彼らはそれを修正することを決め、標準への追加を延期しました。

Object.define説明どおりに動作するメソッド追加Object.mixinしますsuper。ECMAScript5にキーワードがない場合、ソースオブジェクトのプロパティをターゲットにコピーし、記述子を考慮に入れて、親を再定義しません

 // : Object.defineProperty(target, 'c', { enumerable: true, configurable: true, get: function(){ return this.a + this.b; } }); // : Object.define(target, { get c(){ return this.a + this.b; } }); 

2番目の問題:ECMAScript 5では、コンストラクターを使用せずにオブジェクトを作成する機能もを介して追加されObject.createます。作成する際に、独自のオブジェクトのプロパティを追加するには良いでしょうが、2番目の引数Object.createなどはObject.defineProperties、オブジェクト記述子がそのひどく面倒なプロパティを含むオブジェクトを受け取ります。

メソッドを追加しますObject.make-アナログObject.create、2番目の引数は記述子オブジェクトではなく、記述子を考慮して、自身のプロパティが作成されたオブジェクトにコピーされる単純なオブジェクトを期待します。

 //        : var copy = Object.make(Object.getPrototypeOf(src), src); //   : function Vector2D(x, y){ this.x = x; this.y = y; } Object.define(Vector2D.prototype, { get xy(){ return Math.hypot(this.x, this.y); } }); function Vector3D(x, y, z){ Vector2D.apply(this, arguments); this.z = z; } Vector3D.prototype = Object.make(Vector2D.prototype, { constructor: Vector3D, get xyz(){ return Math.hypot(this.x, this.y, this.z); } }); var vector = new Vector3D(9, 12, 20); console.log(vector.xy); // => 15 console.log(vector.xyz); // => 25 vector.y++; console.log(vector.xy); // => 15.811388300841896 console.log(vector.xyz); // => 25.495097567963924 

ECMAScript 7はObject.getOwnPropertyDescriptorsメソッドを追加することを提案しています。このメソッドは、その名前が示すように、オブジェクト自体のプロパティのすべての記述子を含むオブジェクトを返します。理想的な二番目の引数を生成するためのペアObject.defineProperties、およびObject.create、および、ある程度、私たちの代わりObject.makeObject.defineそれは面倒です。

配列



Array#includeメソッド(最近まで-Array#contains MooToolsのバグにより名前が変更されましたが、古い名前で利用可能です)がECMAScript 7に追加される予定です。配列内の要素のエントリを口頭でチェックします。とは異なりArray#indexOf#SameValueZero比較アルゴリズム使用し、ホールを無視しません。 2番目のオプションの引数は開始位置です。

 [1, 2, 3].includes(2); // => true [1, 2, 3].includes(4); // => false [1, 2, 3].includes(2, 2); // => false [NaN].indexOf(NaN); // => -1 [NaN].includes(NaN); // => true Array(1).indexOf(undefined); // => -1 Array(1).includes(undefined); // => true 

しかし、この方法Array#turnは私の病気の想像力の成果です。しかし、判明したように、一意ではありません -LoDash_.transformからの同様のメソッドあります。これはArray#reduce、コールバックからバッテリーを返さなくても、配列を任意のバッテリーオブジェクトに折り畳む方法(デフォルトは新しい配列)の代替です。メソッドとコールバックの署名は似ていArray#reduceます。コールバックから戻ることにより、コレクションを中断できますfalse

 //   : [1, 2, 3, 4, 5].reduce(function(memo, it){ memo['key' + it] = !!(it % 2); return memo; }, {}); // => {key1: true, key2: false, key3: true, key4: false, key5: true} [1, 2, 3, 4, 5].turn(function(memo, it){ memo['key' + it] = !!(it % 2); }, {}); // => {key1: true, key2: false, key3: true, key4: false, key5: true} // filter + map + slice,   : [1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(it){ return it * it; }).filter(function(it){ return it % 2; }).slice(0, 2); // => [1, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9].turn(function(memo, it){ it % 2 && memo.push(it * it); if(memo.length == 2)return false; }); // => [1, 9] 

番号



覚えているイテレータの章の反復可能な数字の例?標準ライブラリで似たようなものを拒否するあまりにも普遍的な機会。非常に短いサイクルに基づいて、所定回数実行for-ofを通じて指定された長さの配列の簡単な世代Array.from(そしておそらく#スプレッド)、等そのようなプリミティブな実装ではありませんが、数値のイテレータを追加しましょう。

 //  : for(var i = 0; i < 3; i++)console.log(i); // => 0, 1, 2 // for-of   : for(var i of 3)console.log(i); // => 0, 1, 2 //   for-of, : $for(3).of(console.log); // => 0, 1, 2 //    : // .map  ""   Array(10).map(Math.random); // => [undefined × 10] // ES5 ,   : Array.apply(undefined, Array(10)).map(Math.random); // => [0.9442228835541755, 0.8101077508181334, ...] // ES6 ,   : Array(10).fill(undefined).map(Math.random); // => [0.5587614295072854, 0.009569905698299408, ...] // Number Iterator: Array.from(10, Math.random); // => [0.9817775336559862, 0.02720663254149258, ...] Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array.from(10, function(it){ return this + it * it; }, .42); // => [0.42, 1.42, 4.42, 9.42, 16.42, 25.42, 36.42, 49.42, 64.42, 81.42] // Comprehensions: [for(i of 10)if(i % 2)i * i]; // => [1, 9, 25, 49, 81] Dict((for(i of 3)['key' + i, !(i % 2)])); // => {key0: true, key1: false, key2: true} $for(10).filter(function(i){ return i % 2; }).array(function(i){ return i * i; }); // => [1, 9, 25, 49, 81] Dict($for(3).map(function(i){ return ['key' + i, !(i % 2)]; })); // => {key0: true, key1: false, key2: true} 

数学関数Number.prototype-快適なもののカテゴリから。ただ、砂糖とMooToolsはのように、オブジェクトのメソッド提出するMath中をNumber.prototypeここで言うことはあまりありません-コンテキストは数学関数の最初の引数になります。既存の標準化された機能を複製できますが、非常に便利です:)

別の行としてこのメ​​ソッドに言及しNumber#randomます。コンテキスト番号と渡された引数の間の乱数を返します(デフォルトは0です)。



 3..pow(3); // => 27 (-729).abs().sqrt(); // => 27 10..random(20); // =>   (10, 20), , 16.818793776910752 10..random(20).floor(); // =>   [10, 19], , 16 var array = [1, 2, 3, 4, 5]; array[array.length.random().floor()]; // =>   , , 4 

特殊文字のエスケープ



突然、記事の最後で、JavaScriptが主にHTMLの操作に使用されていると言った場合、大きな秘密を明かしません。クライアントとサーバーの両方でHTMLを使用するには、HTMLを保護する必要があります。これはフレームワークまたはテンプレートエンジンのタスクであると誰かが言うかもしれません。しかし、そのような原始的なタスクのためにそれらを引っ張る価値はありますか? HTMLエスケープのメソッドはすべての標準ライブラリにあります。砂糖、プロトタイプでは、MooToolsのは方法ですescapeHTMLunescapeHTMLプロトタイプライン。この伝統を壊さないようにしましょう。

 '<script>doSomething();</script>'.escapeHTML(); // => '&lt;script&gt;doSomething();&lt;/script&gt;' '&lt;script&gt;doSomething();&lt;/script&gt;'.unescapeHTML(); // => '<script>doSomething();</script>' 

多くの場合、ユーザーデータから正規表現を作成する必要があり、正しい/安全な操作のためには、それらも保護する必要があります。Sugar、Prototype、MooToolsには、静的メソッドとしてRegExp、どこかにmethodとして、このためのメソッドがありますString.prototypeこのようなメソッドをECMAScript追加する方法は長い間議論されてきましたこれを待つことを望みますが、今のところ、提案されたオプションをライブラリに実装します

 RegExp.escape(' -[]{}()*+?.,\\^$|'); // => ' \-\[\]\{\}\(\)\*\+\?\.\,\\\^\$\|' 

結論



まあ、そのようなもの。

標準に関するよく知られているxkcd画像のコメントに登場することを予想していますが、ライブラリ内のほとんどすべてが利用可能であり、数百のキロバイトのライブラリからの寄せ集めを除き、それに代わるものはありません。

図書館の将来の計画に関しては、それらは主に記事の本文全体に散らばっています。もちろん、パフォーマンスを最適化し、テストでコードをより適切にカバーするためにも必要です-私はまだこれを気にしませんでした。

私が見逃したかもしれないものと、より良く実装できるものについてのあなたの意見に興味があります。

はい それでも、私はそのように苦しみ始めたので、私は退屈しています。私はまともな給料で面白いプロジェクトを探しています。

Source: https://habr.com/ru/post/J216997/


All Articles