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ルールが、
やがて-MooToolsと
Prototypeになります。 多くの便利な機能が追加されますが、多くの場合、メソッドは互いにほぼ完全に複製されます。 多相性はここで展開する必要があります-しかし、多相性の場合、そのようなライブラリは通常、配列のプロトタイプメソッド、
Function#bind
、および他のいくつかに制限され、ほとんどの標準を無視します。 紛争に関しては、ここではすべてが非常に悪いです。 このようなライブラリは、多くの場合、同じ名前で異なるシグネチャを持つメソッドを使用してネイティブオブジェクトを拡張します。 競合を避けるために、最終アプリケーションを開発するときは、ポリファイルをカウントせずにネイティブオブジェクトを展開する複数のライブラリを使用しないでください。また、ライブラリを記述するとき、このような依存関係は一般に受け入れられません。
1つの汎用標準ライブラリの代わりに、不必要なトラブルなしで希望する機会を提供するために、
Undescore / LoDash / Sugar + es5-shim、es6-shim、es6-symbol、setImmediate.js / asap、Moment.jsから寄せ集めを引き出す必要があります/ Intl.js、コンソールダミー ...など。
#これらのアプローチのそれぞれからベストを取りましょう。
core.jsの概念は次のとおりです。
- 標準ライブラリには、特定のプラットフォームのAPIを操作する機能を含め、快適な作業に必要な最小限のものがすべて含まれている必要があります。
- 標準は私たちのすべてです。 ライブラリの主要な部分は、親友です。 必要な機能がすべて標準化されているわけではありません。
- システムで使用可能な機能が仕様または事実上の標準に従って実装されている場合、ネイティブのままにしますが、機能が標準化されていない場合、将来の競合を避けるために強制的に置き換えます。
- ライブラリはコンパクトで十分に圧縮する必要があります。
- モジュール性、必要な機能のみを組み立てる機能。
- 最終的なアプリケーションを作成します-あなたは王であり、神であり、ネイティブオブジェクトを展開するライブラリを使用するすべての権利を持っています。 主なことは、1つのライブラリだけがそれを行うということです。
- ライブラリまたはnpmモジュールを作成します-ネイティブオブジェクトを展開するライブラリを使用することはできません。 最終アプリケーションを作成するプログラマーの競合を運命づけるリスク。 この場合、拡張せずに組み立てられる可能性があります。
#通常のビルドの場合、
core.jsの操作は非常に明白です。
console.log(Array.from(new Set([1, 2, 3, 2, 1])));
#ネイティブオブジェクトを拡張しないアセンブリの場合、機能はグローバル
core
オブジェクトまたはモジュラーシステムにエクスポートされます。 たとえば、
Promise
コンストラクターは
core.Promise
として使用でき、
Array.from
メソッドは
core.Array.from
として
Array.from
。 ライブラリの追加ではなく、既存のプロトタイプに追加することを目的としたメソッドは、コンストラクターが静的になります。たとえば、
core.String.repeat
は
String.prototype.repeat
メソッドの静的バージョンです。
var log = core.console.log; log(core.Array.from(new core.Set([1, 2, 3, 2, 1])));
ポリフィルのみをそれぞれ含むアセンブリは、それらを追加するだけです。 実際、通常のアセンブリを使用した例では、ポリフィルのみが使用されています。
# Node.jsへのインストール:
npm i core-js
アセンブリの1つを接続して、以下から選択できます。
ブラウザ用のビルド :
#これらのアセンブリのいずれにも満足できない場合は、独自のアセンブリを作成できます。 たとえば、ネイティブオブジェクトを展開せずに、コンソールモジュールと単純な日付形式のみが必要です。 これを行うには、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.js
、
custom.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つの機能があります。 配列メソッドは
ジェネリックであり、配列だけでなく、配列に似たオブジェクトのコンテキストで呼び出すことができ
ます 。この詳細については、
以下で説明します 。 そのため、ES5仕様によると、文字列は配列のようなオブジェクトであり、文字列の文字はインデックスによって取得できます。たとえば、
'string'[2] // => 'r'
ですが、古いIEではそうではありません。 これらのメソッドが文字列のコンテキストで適用される場合、文字列を配列にキャストします。 同じ問題を解決するには、必要に応じて、古いIEの
Array#slice
と
Array#join
を置き換えます。
Array.prototype.map.call('123', function(it){ return it * it; });
さて、古代の真実を忘れないでください。for
for-in
ループで配列をバイパスしないfor-in
ください 。 これは遅いだけでなく、IE8-サポートが必要な場合、キーが適切であるかどうかを確認することも強制します-そうでなければ、配列の要素だけでなく、そのプロトタイプのメソッドもバイパスします:)
#静的メソッド
Array.isArrayは同じカテゴリに属します。 このメソッドは、オブジェクトがプロトタイプチェーンではなく、内部クラスによる配列であるかどうかをチェックします。 便利ですが、普遍的ではありません。 オブジェクトの分類について
は、記事の第2部である自転車で詳細に説明
します 。
Array.isArray([1, 2, 3]);
#オブジェクト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 );
# 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);
# Object.keysメソッドは、オブジェクトの独自の列挙キーの配列を返します。
Object.getOwnPropertyNamesは、オブジェクト自体のキーの配列を返します。 および非上場。
Object.keys
を使用すると、すべてがシンプルに思えます
Object.keys
for-in
を介してオブジェクトを
Object.keys
し、プロパティが独自のものであるかどうかを確認します。 IEの
「列挙不可能な」列挙プロパティのバグではない場合。 そのため、このようなプロパティを個別に確認する必要があります。 同様に、使用可能な非表示プロパティのリストを追加チェックすると、
Object.getOwnPropertyNames
も
Object.getOwnPropertyNames
ます。
console.log(Object.keys({q: 1, w: 2, e: 3}));
#記述子のすべてが悪いので、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.getOwnPropertyDescriptorで
value
と
enumerable
を正直に取得し
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);
# Date.nowメソッドは、現在の時刻を数値表現で返します。結果は
+new Date
と同じです。
Date.now();
# String#trimメソッドは、行の先頭と末尾から空白を削除します。
'\n \n'.trim();
JSON
モジュールについては、IE8でサポートされており、このライブラリのフレームワーク内では、実装する理由はありません。 あなたが絶対に先史時代のIEでそれを必要とするならば-誰も例えば
この親友を使うことを気にしません。
#ECMAScript 6
ECMAScript 5仕様は、決して受け入れられなかったECMAScript 4仕様の代わりに急いで書かれており、前世紀に採用されたECMAScript 3にはあまり拡張されていませんでした。 新機能の追加は凍結され、ECMAScript 7の提案にはすべての主要な変更が加えられ、ドラフト仕様への最近の変更のほとんどはバグ修正です。 したがって、標準ライブラリでは、主にES6に焦点を当てます。
現在のエンジンでサポートされていることは、
この表にはっきりと表れて
います 。
- とりわけ、 Firefoxのサポートにより、すでに多くの機能が利用可能です。
- # v8(Chrome、Opera、Node.js)でも多くのオプションが利用可能ですが、オプションのかなりの部分がデフォルトでブロックされています。ブラウザで有効にするには、「実験的なJavaScriptを有効にする」ボックスをチェックする必要があります(パーサーはリンク
chrome://flags/#enable-javascript-harmony
)、およびNode.jsは--harmony
フラグで実行されます。 Promise
、 WeakMap
、 WeakMap
など、フラグがなくても使用できるものがあり、Chrome 38 Symbol
、 Map
、 Set
、イテレータからも使用できます。 Node.jsはこの点で遅れています。特に安定したブランチでは、v8はめったに更新されないためです。 しかし、それは、ユーザーのブラウザとは異なり、誰もフラグで実行することを気にしません。 - IEはいつものように悪いですが、11番目のバージョンのコレクションでは、いくつかの機能が追加されました。 近い将来、彼らは多くを約束します 。
- 彼らはSafariに 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を最大限に使い始めることができます。おそらく、いくつかのささいなことを除いて。6to5でES6をコンパイルする機能を備えた非常にシンプルなサンドボックスと、接続された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);
またObject.mixin
、列挙不可能なプロパティをコピーし、記述子を考慮に入れ、キーワードを介して取得した親を再割り当てするメソッドを追加することも計画されていましたsuper
。しかし、彼らはその追加を延期することにしました。その類似物は、ライブラリの自転車部分にあります。# Object.is
JavaScriptの比較演算子は通常、かなり奇妙に動作します。==
キャストのような演算子でさえ忘れてください===
: NaN === NaN
この場合のために、言語には内部SameValue比較アルゴリズムがあります。それのためにNaN
あるNaN
と、+0
そして-0
明瞭な。 ECMAScript 6では、それらを演算子is
およびとして引き出したかっisnt
たのですが、言語の比較演算子はすでに十分ではないことを認識しており、後方互換性のために、Object.isメソッドとして取り出されました。例: Object.is(NaN, NaN);
#また、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;
言語のプロトタイプの方向、機会-少なくとも最初の例-シンプルで明白なプロトタイプの継承がECMAScript 5にないことを考えると、このように一見必要なのは奇妙です。このメソッドなしで既存のオブジェクトのプロトタイプを変更する唯一の方法は非標準のプロパティ__proto__
です。現時点では、IE10-を除く現在のすべてのブラウザーで、現在の実装(プロトタイプのゲッター/セッター)でサポートされていますObject
。プリミティブで、不必要なチェックやオブジェクトの戻りObject.setPrototypeOf
がなければ、バージョンはシンプルに見えるでしょう-セッター__proto__
を引き出して、それから機能を作ります: var setPrototypeOf = Function.call.bind(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set);
ただし、別の問題がここに表示されます-v8の古い、しかしどこか関連するバージョンでは、セッター__proto__
を関数として使用できません。実装Object.setPrototypeOf
の場合、キーで値を設定するだけです__proto__
。私たちは生きています:
- IE10の場合、エミュレーション
Object.setPrototypeOf
は不可能であるため、完全に存在しません。 - 古いバージョンではV8
Object.setPrototypeOf
目標の連鎖にプロトタイプが存在しない場合は動作しませんObject.prototype
またはプロパティは__proto__
、たとえば、経由上書きObject.defineProperty
。
また、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]));
#前のものとは異なり、 Array.ofメソッドは現在ほとんど実用的ではありません。サブクラスには、まずArray
配列リテラルの類似物として必要です[]
。例: Array.of(1);
#メソッドアレイ#の検索およびアレイ#findIndexは、コールバック呼び出して検索配列を搭載しました。例: function isOdd(val){ return val % 2; } [4, 8, 15, 16, 23, 42].find(isOdd);
#配列メソッド#配列#fillは、渡された値で配列を埋めます。オプションの引数-開始位置と終了位置。例: Array(5).map(function(){ return 42; });
#文字列メソッド
ここではすべてが簡単です。
文字列#には(最近まで- String#contains
が含まれていましたが、古い名前で利用できる限り#Array#includes
引き寄せられました)、文字列内の部分文字列をチェックします。文字列#startsWithおよび文字列#endsWithは、文字列が特定の部分文字列で開始または終了するかどうかを確認します。これら3つのメソッドは、追加の引数-開始位置を取ります。例: 'foobarbaz'.includes('bar');
String#repeatメソッドは、指定された回数だけ繰り返される文字列を返します。例: 'string'.repeat(3);
ライブラリには、マルチバイト文字と正直な#行反復子のサポートを改善するための ECMAScript 6/7メソッドがまだ追加されていません;配列反復子が文字列に使用されます。今、彼らは私が単にそれらを必要としないという理由だけではありません。近い将来それらを追加するのは良いことです。#数字を扱う
ECMAScript 6は、膨大な数の数学関数と定数を追加します。説明と例はなく、リンクのみ:Number.EPSILON、Number.parseFloat、Number.parseInt、Number.isFinite、Number.isInteger、Number.isNaN、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER、Number.isSafeInteger、Math.acosh、Math.asinh、Math.atanh、Math.cbrt、Math.clz32、Math.cosh、Math.expm1、Math.hypot、Math.imul、Math.log1p、Math.log10、Math.log2、Math.sign、Math.sinh、Math.tanh、Math.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());
シンボルは完全にプライベートではありません-メソッド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
ています。特に悪用しないでください。パフォーマンスに影響する可能性があります。 - : IE8- ( #
Object.defineProperty
), Object.prototype
, «» . - ,
Object.prototype
(, Object.create(null)
), «» . Symbol() in {}
true
— .typeof Symbol()
'object'
— JavaScript .Object.getOwnPropertySymbols
— Object.getOwnPropertyNames
, , Object.getOwnPropertyNames
, , «».
#決定が非常に疑わしい場合、ここで何をしていますか?プロジェクトに必要な文字数が少なく、自分自身を含まないオブジェクトでそれらを使用する予定がない場合Object.prototype
、このソリューションは機能します。それ以外の場合は、ライブラリにいくつかのヘルパーを追加します。Symbol.pure
利用できるネイティブな文字ならば、それは文字を返し、ない-でセッターを追加することなく、独自の文字列のキーを返しObject.prototype
、そしてSymbol.set
、利用可能な場合、ネイティブシンボルは-ちょうどオブジェクトのキー値に敷設なし-使用しての値が設定されます。この原始的な方法で、上記の問題の半分を取り除きます。呼び出しの代わりにデータヘルパーを使用して上記で使用した例は次のようになります。Object.defineProperty
enumerable: false
Symbol
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);
# ES6は、キャラクターのグローバルなケースのような怪しいものも追加します。それを操作するためのいくつかの方法があります -Symbol.forと Symbol.keyForです。Symbol.for
レジスタで検索し、キー文字列で文字を返しますが、見つかりません-新しい文字を作成し、レジスタに追加して返します。Symbol.keyFor
送信された文字がレジスタ内で一致する文字列を返します。例: var symbol = Symbol.for('key'); symbol === Symbol.for('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);
# Set-一意の値のコレクション。と同様にMap
、回避策があります。例: var set = new Set(['a', 'b', 'a', 'c']); set.add('d').add('b').add('e'); console.log(set.size);
# 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));
# 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));
これらのコレクションはすべて、準線形検索時間を提供する必要があります。キーの一意性は、#SameValueZero比較アルゴリズムによって提供されます。#最新のjsエンジンに対するこれらのコレクションのサポートは何ですか?とても良い。- Firefoxがいっぱいです
Map, Set
とWeakMap
。毎晩のビルドで登場しましたWeakSet
。反復可能なオブジェクトMap
をSet
取得します。Map
そしてSet
、イテレータとメソッドがあり.forEach
ます。 - v8 — Chrome, Opera Node.js , 4 . Chrome 38, .
WeakMap
WeakSet
. # . v8 , Map
Set
.forEach
, Map
Set
, . - IE11
Map, Set
WeakMap
. . Map
Set
, .forEach
. - Safari , .
Map, Set
WeakMap
. . , , , , , next
. forEach
, 3 , , 1 Set
2 Map
.
ほとんどすべての現在の実装収集方法.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){
Map
2つの配列または要素オブジェクトのチェーンでObject
はなく、キーと値の2つのハッシュに実装します。追加の順序でコレクションをトラバースするために別のキー/値ストアは必要ありません:すべてのエンジンでは、オブジェクトキーは数字キーを除き、追加の順序で格納されますが、ここではすべてのキーにプレフィックス文字があるため、存在しません。合計: Map test: Map test: 669.93 Map size: 100000
もちろん、ネイティブのものよりも遅いですが、うまくいくと思います。はい、キーオブジェクトに隠しプロパティを記述します-キーとして#frozen-objectsを使用することはできませんが、許容可能な速度が得られます。Set
1ハッシュで同様に実装されます。#疎結合コレクションの実装
弱結合コレクションの実装はさらに簡単です。イテレータ、メソッド.forEach
、プロパティはありません.size
。コレクションオブジェクトにキーと値を保存する場合、それはもはや疎結合ではなくなります-キー/値は削除されず、Set
andの削除されたバージョンを取得し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; } }); })();
キー参照を削除するとき、それがコレクションに残っていないこと、そしてそれに応じてメモリがリークしないことを確認しましょう:
ただし、場合によっては、メモリリークの問題が残ることがあります。コレクションオブジェクトが削除された後、値はキーに関連付けられたままになり、キーであったオブジェクトが削除されるまでメモリリークが発生します。そのため、コレクション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();
# 反復可能オブジェクト(反復可能)-Symbol.iterator
反復子を返すメソッドをキーに含むオブジェクト。したがって、反復子を反復可能にSymbol.iterator
するには、を返すキーによるメソッドが必要this
です。たとえば、数字を反復可能にします( sandbox): Number.prototype[Symbol.iterator] = function(){ return NumberIterator(this); } Array.from(10);
に注意してください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);
# ループはfor-of
反復可能なオブジェクトをバイパスするように設計されています。反復可能な数値を使用した例では、次のように機能します( sandbox): for(var num of 5)console.log(num);
#でのECMAScript 6 iskaropkiを繰り返すString, Array, Map, Set
とArguments
。さらに、Array, Map
これらにSet
はメソッドが.keys, .values
あり.entries
、それぞれキー、値、およびキーと値のペアの反復子を返します。Core.jsは、データイテレータとメソッドを追加します。ループとともに、for-of
次のようになります( sandbox): var string = 'abc'; for(var val of string)console.log(val);
- 実際のFirefoxには、iteratorを除くこれらすべてのイテレーターがあります
arguments
。サイクルは機能しますが、独自の廃止されたプロトコルに基づいています。 - v8では、
for-of
Chrome 38から始まるループは(アレイの破壊を除いて)正しく機能し、を除くすべてのデータ反復子が使用可能ですarguments
。古いバージョン(フラグ付き)では、反復可能なオブジェクトではなく反復子が必要でした。 - IEでは、もちろん、このループはまだ機能せず、イテレーターはありません。
#このサイクルの構文サポートではすべてが非常に悪いので、ES6 +プリプロセッサを使用しない人のために、同様の機能for-of
( sandbox)を実装するヘルパーをライブラリに追加します。 $for(new Set([1, 2, 3, 2, 1])).of(function(it){ console.log(it);
#オブジェクトのプロトタイプ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));
#でのECMAScript 6つのイテレータを初期化するために使用されている#コレクション Map, Set, WeakMap, WeakSet
、配列、後に#をArray.from
自分の予想方法Promise.all, Promise.race
で仕事に#約束。#関数、コンストラクター、および配列リテラルを呼び出すときに適用されるスプレッド演算子も、反復可能なオブジェクトを必要としますが、これは標準ライブラリからはほど遠いトピックです。今すぐ使用したいという要望があります-歯の中のプリプロセッサ。例: [...new Set([1, 2, 3, 2, 1])];
#他のすべての場合、配列/ジェネレーター内包表記(配列/ジェネレーターの抽象化?)イテレータープロトコルにも属します。以前はECMAScript 6ドラフトに含まれていましたが、 Firefoxでは長い間サポートされていましたが、ECMAScript 7まで延期されました。これは、フィルター処理と変換を使用して、反復可能なオブジェクトから配列またはイテレーターを生成する機能です。つまり
構文filter
およびmap
任意のiteriruemuhオブジェクトについて。最後の配列の破壊はサポートされていませんが、FFで動作する例を除きます。すべてが6to5とTraceurで機能します。例: var ar1 = [for(i of [1, 2, 3])i * i];
私に関しては-素晴らしいことです。それも単なる構文です。# 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; });
リテラルでは、ES5の関数はやや扱いにくいですが、矢印関数では、構文理解を使用するのとほぼ同じになります。反復子に対する他の操作をモジュール$for
に追加することができます。これにより、反復可能なオブジェクトをトラバースおよび変換する普遍的で怠zyな(反復子プロトコルなど)方法が提供されます。しかし、将来のために延期します。または多分地獄に。ライブラリはさらに2つのイテレータ(#times、#two)を追加します。#Dict
コンストラクタは反復可能なオブジェクトを期待しますが、イテレータに関連付けられたすべてのバイクをこの章にドラッグしません。# ECMAScriptの6:約束
非同期性とJavaScriptは、多くの人にとってほぼ同義語です。これはECMAScript 5標準の非同期性にすぎず、まったく何もありません。などであっても、このような基本的な技術setTimeout
とsetInterval
W3CとWHATWGのウェブ基準が設けられており、それらについて#話もう少し。最初のクラスのオブジェクトとして機能しない限り、コールバックの転送は便利です。これにより、コールバックhellが発生します。非同期関数の並列および順次実行を簡素化する1つの方法は、async.jsなどのライブラリを使用することです。非同期プログラミングを簡素化する別のアプローチは、Promiseテンプレートです。非同期関数が返すことができるpromiseオブジェクトを使用して、その結果をサブスクライブできます。結果をサブスクライブできるメソッドは、新しいプロミスを返します。これは、プロミスをチェーンに配置することにより、コードをより適切に構成するのに役立ちます。また、Promiseはエラー処理の問題を解決します。非同期コードでtry-catch
は機能せず、コールバック引数でエラーを渡す必要があり、コードがさらに混乱する場合があります。 promiseチェーンの最後に、メソッドreject
とthrowの両方でスローされるミスが発生する可能性がありますthrow
。QやRSVPのようなPromiseライブラリが一般的です。時間が経つにつれて、標準が登場しましたPromises / A +。すべてのプロミスは非同期に解決され、メソッドを使用して結果をサブスクライブできます.then
(最初の引数は正常終了時に実行される関数、2番目はエラーが発生した場合)。そのため、非同期コードでの作業を最終的に標準化するために、ECMAScript 6 はPromises / A +標準と互換性があり、ほとんどの機能を最小限に抑えながら、promise実装を追加しました。それらについては特に説明しません。詳細についてはこちら(翻訳ですが、少し時代遅れです)、こちらまたはこちらをご覧ください。 ES6 Promiseはv8とFirefoxですでに利用可能です。ポリファイルがあります-es6-promiseとnative-promise-only。#のECMAScript 6の約束の実施は、コンストラクタでPromise
2コールバックを渡される関数を、受け入れる-最初は、第二に障害が発生した約束を、ことができます。さらにthen
、ES6 promiseにはメソッドが含まれています。これは、最初の引数をcatch
省略した場合の省略形でありthen
、それを使用するとエラーをサブスクライブできます。例: var log = console.log.bind(console); function sleepRandom(time){ return new Promise(function(resolve, reject){
私は約束エンジンを標準に完全に適応させるのが面倒だったので、core.jsの promise カーネルはネイティブの約束のみのライブラリに基づいており、そこからコードはほとんど残っていません。非同期性を確保するためにprocess.nextTick
、多相性#setImmediateのメソッドも使用されます。#ヘルパーのペアは、それぞれ、正常に完了した、または渡された値でエラーが発生したプロミスPromise.resolve
をPromise.reject
返します。彼Promise.resolve
が約束を受け入れた場合、彼はそれを返します。また、他のthenable(たとえば、jQuery Deferred)をpromiseに変換するために使用することもできます。標準の古いバージョンでは、このための別のメソッドがありました-Promise.cast
。例: Promise.resolve(42).then(log);
#ヘルパーPromise.all
は、渡された反復可能なコレクションのすべてのプロミスが解決されたときに解決されるプロミスを返します(v8では、配列でのみ機能するようになりました。ライブラリ内でまだ修正を開始していません。他の何かで使用する意味はありません)。約束ではないコレクションの要素は、を通して約束に導かれPromise.resolve
ます。例: Promise.all([ 'foo', sleepRandom(5), sleepRandom(15), sleepRandom(10)
#ヘルパー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);
# 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('');
やがて、Promiseは標準の非同期JavaScript APIの多くを変更します。グローバル関数は既に標準化されています(polyphil)fetch
- 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);
面倒で不明瞭。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', '+');
#遅延実行:setTimeout、setInterval、setImmediate
# setTimeout、setInterval
おそらく、すべての通常で始まりsetTimeout
とsetInterval
。多くの人は、標準(W3C、WHATWG)に従って、これらの関数がコールバックと遅延時間に加えて、転送されたコールバックを開始する追加の引数を取ることを知りません。しかし、ここでは、いつものように、問題はIEにあります。IE9ではsetTimeout
、setInterval
2つの引数のみを取り、これは複数行のラッパーで処理されます。
# 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は追加を急いでいません。人気のこのポリュフェモス。関数の効果的な遅延実行を実装するには、非常に多くの方法があります。さまざまなプラットフォームで最大のパフォーマンスを実現するには、多くのプラットフォームを使用する必要があります(上記のポリフィールのメソッドと同様)。これは:- Node.jsの古いバージョンの場合-
process.nextTick
- 最新のブラウザの場合-
postMessage
- WebWorkersの場合-
MessageChannel
- IE8の場合--
script.onreadystatechange
- そして、これらの方法が役に立たなかった場合-
setTimeout
最小限の遅延で
setImmediate(function(arg1, arg2){ console.log(arg1, arg2);
さらにsetImmediate
、より高速な代替手段-コンセプトasap
(できるだけ早く、ライブラリ)-可能であれば、マイクロタスクを作成します。これは、入出力の前に実行されます。このようなグローバルメソッドを言語の標準に追加し、tc39で考えてください。たぶんそれはライブラリに追加されるべきですか?#コンソール
コンソールは、ブラウザーとサーバーの両方で唯一の汎用出力およびデバッグツールです。同時に、コンソールはECMAScript仕様の一部ではなく、まったく標準化されていません。放棄されたアウトライン仕様があり、すべてのプラットフォームでの実際の実装は異なります。# IE7では、コンソールが完全に欠落しています。一部のブラウザでは、「ハイゼンベルグコンソール」はconsole
ユーザーが視聴しているときにのみ定義されます。そして、もちろん、すべてのメソッドがすべてのプラットフォームで利用できるわけではありません。Firebug Liteのスタイルでコンソールを発明するのではなく、メソッドのスタブを作成して、可用性を確認せずにメソッドを使用できるようにします。
# Firefoxとクロム方法コンソールでは、コンテキストを使い果たすべきであるconsole
ような-あなたはコールバックがネクタイ持っているようにそれらを与えたいのであれば、#をconsole.log.bind(console)
。IE、Firebug、およびNode.jsでは、バインドせずに渡すことができ、これははるかに便利です。したがって、メソッドをオブジェクトにバインドしますconsole
。
ただし、ここで1つの問題があります。一部のプラットフォームでは、コンソールメソッドを呼び出すときに、メソッドの呼び出し元の行も表示されます。コンソールをオーバーライドする場合は、この行は、ラインになりますsore.js。これが重要な場合は、コンソールモジュールなしでライブラリを構築できます。コンソールは標準化されていないため、小さなギャグを追加します。
#コンソールへの出力をオフにする機能。もちろん、本番環境では、コードにコンソールメソッド呼び出しを残さずに、ペンまたはより高度なツールを使用してそれらを削除する方が良いですが、これは便利であり、必ずしも必要ではありません。 console.disable(); console.warn(' , .'); console.enable(); console.warn(' .');
コンテキストバインディングとコンソールを無効にする機能も、たとえばTheShockのこのライブラリに存在します。#パート2:自転車
オプションなし、オプションなし。
私は世界の半分が欲しかった-自転車に十分。
-中毒のループ
この記事/ライブラリの文脈において、自転車はすべて標準化されていない機能です。実際、私の意見では、標準言語ライブラリに欠けているものは、たとえ現在利用可能なすべての標準に従って実装されていてもです。これには、ES7 +で提供されるものも含まれます。これは、これが繰り返しレビューされることや拒否されることからもほど遠いためです。#データ分類
#ここでは、完全に平凡さから始めます。 JavaScriptではECMAScriptの5仕様のために持っている 6つのデータの種類を:Undefined, Null, Boolean, String, Number
とObject
。 ECMAScript 6は、別のデータ型-#をSymbol
追加します。データのタイプを判別するには、演算子がありますtypeof
。それはまさにそれが具体的にどのように機能するかです。だから、歴史的に、というtypeof null
リターン'object'
と解決しようとするとハーモニーで、それは成功しませんでした。タイプのObject
typeof
場合'object'
、'function'
内部メソッドの可用性に応じて、またはを返します[[Call]]
。合計すると、演算子typeof
はプリミティブのデータ型のみを返し、すべてのプリミティブではありません。変数がnull
単に、それと比較するだけで十分です。その後Object
、毎回何百ものコードを記述するか、ヘルパーを作成する必要があります。もちろん、そうすることができます- Object(foo) === foo
しかし、この解決策は最速とはほど遠い -プリミティブをオブジェクトに導きます。 ES6の初期のドラフトではメソッドが存在Object.isObject
していましたが、どうやらそれを修正する試みのためtypeof null
に削除されました。また、変数がオブジェクトかどうかを確認するには、常にする必要があります。そこで、どこにも簡単に実装されたヘルパーを追加しましょうObject.isObject
(sandbox): Object.isObject = function(it){ return it != null && (typeof it == 'object' || typeof it == 'function'); }
#しかし、オブジェクトの分類がより興味深い。オペレーターinstanceof
はプロトタイプチェーンをチェックします。プロトタイプからオブジェクトを作成する場合、またはオブジェクトをプロトタイプとして設定Function.prototype
する場合、関数にはなりません。インスタンスプロパティconstructor
は何も保証できませんがconstructor.name
、コード圧縮ですべての意味を失うだけでなく、IEはまだサポートされていません。内部プロパティは、オブジェクトの分類に役立ちます[[Class]]
。それを引き裂く唯一の方法は、多くの人になじみのある恐ろしいデザインですObject.prototype.toString.call(foo).slice(8, -1)
。例: Object.prototype.toString.call(1).slice(8, -1);
内部受付クラスに基づき、ほとんどのライブラリは、型のユーティリティのセット追加#をArray.isArray
:Object.is タイプで砂糖のためには、_.is タイプにUndescoreなど私たちは、異なる動作を- 1つの普遍的な方法Object.classof
、データの分類のための声明と同様に、typeof!
のLiveScript(例)。ここにObject.prototype.toString.call(foo).slice(8, -1)
いくつかの問題があります:null, undefined
およびに適用可能な古いIEではarguments
、この構造はを返します"Object"
。これは、追加のチェックで簡単に処理できます。- ECMAScript 6, ?
"Object"
. Object#toString
ECMAScript 6.
突然、ECMAScript 6には通常、などのオブジェクトの内部プロパティがありません[[Class]]
。Object#toString
ES6は、特別な内部プロパティをチェックすることにより、変数が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);
JavaScriptでは、オブジェクトと辞書(連想配列)は1つです。これには長所があります-非常に便利なようですが、JavaScriptオブジェクトシステムに基づいたデータ交換形式が非常に一般的JSON
であり、マイナスであることは無駄ではありません。辞書の場合、キーによって要素を取得することとプロトタイプからメソッドを取得することに違いはありません。これは、オブジェクト(および中括弧付きの表記法で指定されたオブジェクト)の下にプロトタイプがある場合Object.prototype
、辞書の基本操作を中断します。オブジェクトの場合、これらは拡張子の制限Object.prototype
です。ECMAScript 6では、すでに述べた新しいタイプのキー値コレクションが登場しましたMap
。その速度は、オブジェクトの速度よりも速い場合があります(それ自体では、多相性動物には適用されません)。ほとんどの場合、それは置き換えられません。でMap
オブジェクト辞書とは異なり、単純なリテラル表記はありません。プロパティへのアクセスはメソッドを介して行われます-それほど簡潔ではありません。Map
すべての人に愛されることJSON
は決してなく、それほど普遍的ではありません。通常、辞書キーには文字列以外には何も必要ありません。#問題:Object.prototypeと辞書
でObject.prototype
、Mozilla 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(''));
プロパティは、存在しない場合、プロトタイプチェーンから取得され、in
同様にその存在をチェックします。プロトタイプチェーンを考慮せずに、オブジェクトのプロパティの存在をチェックin
するメソッドhasOwnProperty
で追加/置き換えましょう。私達は得る:
特に「電話帳」がサーバー側にある場合は特に深刻です。プロトタイプメソッドはオーバーライドできます。したがって、hasOwnProperty
オブジェクトから結び付けられていないメソッドを使用する必要があります。誰にとっても面倒なチェックを使用する必要があります。ほぼそのようなゴミ:
言語でこの問題を解決するために、プロパティがに似た独自のプロパティである場合、検証演算子が役立ちin
ます。問題は終わったと決めましたか?次のようなものはありません: phone.set('__proto__', '+7666666');
ではObject.prototype
、「魔法」のgetter / setterメソッドがあり__proto__
ダメージに、例えば、プロパティを横断するとき-このキーは無視され、オブジェクトされるためにプリミティブの設定、。古いエンジンには、他の「マジック」プロパティがありました。ここで役立つのはObject.defineProperty
(サンドボクサー)のみです。
特に話をしない辞書をバイパスについて-それは本当に悪いではない場合は、辞書を横断するとき、辞書は性質perecheslyatプロトタイプが含まれていないfor-in
、あなたはテストなしで行うことができますhasOwnProperty
。それはただのバグです #「列挙不可能な列挙可能」プロパティfor-in
はObject.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
。これはどのように私たちを脅かしますか?
- たとえば
+Object.create(null)
、数字や文字列にキャストすることはできません'' + Object.create(null)
-TypeError
- したがって、オブジェクトを抽象等式アルゴリズム、たとえば
==
プリミティブと比較することはできません-TypeError
好きな人にとっては、しかし私にとってはマイナスよりもプラスです。#コンストラクターDict
そのため、辞書を作成してObject.create(null)
記入するのは、辞書を作成するよりもはるかに面倒です{}
。もちろん、最も美しい解決策は、リテラルの言語に辞書を追加することですが、これは、少なくとも短期的には、ありそうもないことです。リテラルによる初期化の可能性はありません。記録がありますが、{__proto__: null, foo: 'bar'}
どこでもサポートされていません。現時点ではコードの最適化につながりますが、それでもかなり面倒です。かなり興味深い決定が議論されました- 速記として「コンストラクター」を作成するDict
Object.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; }
#プロトタイプを持つ辞書を安全に使用するためのメソッド
ディクショナリのように、その下にプロトタイプがあるオブジェクトで作業する必要がある場合、独自のプロパティで安全に作業するためのメソッドを追加します。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'));
#辞書を操作する方法
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.turn
。Key
名前の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);
#メソッドのチェーンについては、完全に明白な理由のために、Dict
それらはモジュール内になく、予期されていません。ここでの救いは抽象的な参照かもしれません。しかし、#$for
と#のMap
フレームワーク内では、それらが表示される可能性は十分にあります。#辞書の繰り返し
ES6の明るい未来は#イテレータと#ループでfor-of
近づいています。それは辞書としての単なるオブジェクトであり、これは暖かくも冷たくもない-ES6ではイテレータは提供されていません。したがって、それらをループしたりfor-of
、#Map
辞書で初期化するなどの簡単な方法はありません。メソッドを追加.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));
#見込み客
#文字だけで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); };
それは私たちに何を与えますか?new Dict instanceof Dict
。for(var [key, value] of dict){...}, new Map(dict)
イテレータを取得する必要はありませんDict.entries
。- #
Object.classof(new Dict)
'Dict'
ではなく、戻り'Object'
ます。
ただし、少なくとも、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();
バインドせずに部分的なアプリケーションメソッドを追加しますthis
- Function#part
(sandbox): Array.prototype.compact = [].filter.part(function(val){ return val != null; }); [0, null, 1, undefined, 2].compact();
#任意の引数の部分的な使用
多くの場合、部分的に適用する場合、任意の引数を渡す必要があります。たとえば、最初の2ではなく、2番目と4番目または2番目と3番目だけです。ここでFunction#bind
彼女は私たちを助けることができません-私は特定のケースごとに手動でラッパーを書く必要があります。 function fn1(a, c){ console.log(a, 2, c, 4); }; fn1(1, 3);
このタスクを容易にするために、プレースホルダーを追加します。プレースホルダーは、最終関数が呼び出されたときに渡される引数を置き換えるオブジェクトです。プレースホルダーへの参照として、グローバル変数はそれ自体を要求します_
(たとえば、LiveScript、サンドボックスなど)が、Undescore.jsライブラリーはこの変数を使用します(ちなみに、それはプレースホルダーでもあります_.partial
)およびその名前空間としてLoDashです。それらとの競合を避けるために、_
存在しない場合にのみ新しいグローバルオブジェクトを作成し、操作中はグローバルオブジェクトを使用します_
。ネイティブオブジェクトを展開しないアセンブリの場合、オブジェクトをプレースホルダーとして使用しますcore._
。例: var fn1 = console.log.part(_, 2, _, 4); fn1(1, 3);
Function#by
同様のメソッドも追加Function#bind
しますが、引数にプレースホルダーを使用する可能性があります。まとめてFunction#bind
プレースホルダを操作することもできますが、これは仕様違反であり、この方法はほとんどすべてのエンジンですでに非常にブレーキがかかっています。 var fn = console.log.by(console, _, 2, _, 4); fn(1, 3, 5);
#オブジェクトからメソッドを抽出
ほとんどの場合、たとえば、関数にコールバックを渡すとき、メソッドを取得元のオブジェクトにバインドする必要があります。そして、ここで問題が発生します- fn(foo.bar.baz.bind(foo.bar))
。私たちはfoo.bar
2回書くことを余儀なくされていますが、これは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'));
良い方法では、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);
#引数の数を制限する
オプションの引数の問題については、この記事で説明します。その一例はparseInt
-非常に不明瞭で、たとえば文字列を数字にキャストすることを誰も気にNumber
しません。たとえば、追加の引数を期待していません。重要なのは「危険」ではなく、追加のラッパーを作成する必要があることです。たとえば、配列のすべての要素をコンソールに出力し、要素自体のみを印刷します。 [1, 2, 3].forEach(console.log);
.forEach
他の多くのメソッドと同様に、このメソッドはコールバックにオプションの引数(インデックスと配列自体)を提供します。しかし、それらは必要ありません。そのため、コールバックを別の関数にラップする必要があるたびに: [1, 2, 3].forEach(function(it){ console.log(it); });
上記の記事では、関数の引数を制限する方法が提案されましたFunction#only
。そのオプションを実装します。最初の引数は引数の最大数であり、2番目の引数はオプションであり、コンテキストです。例: [1, 2, 3].forEach(console.log.only(1));
もちろん、引数の最大数が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);
また、たとえば、「火曜日、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' });
もちろん面倒ですが、何もしないよりはその方がいいです。標準のサポートに関して-一般に、悪くない。Chrome、Opera、IE11、および最近ではFirefoxをサポートしています。しかし、通常、IE10、Safari、モバイルプラットフォームのサポートが必要であり、悪魔はまだ何を知っています。この場合にはポリフィルがあります。しかし問題は、ロケールを考慮しなくても、この機能の実装が重くなりすぎることです。このため、のCore.jsで無ポリュフェモスECMA402。#簡単な日付フォーマットを追加します。
simpleはどういう意味ですか?どのくらいの頻度で完全なローカライズが必要ですか、または他の高度な日付ツールは何ですか?私は本当にそうではありません。通常、書式文字列を使用した簡単で便利な日付書式が必要です。必要な場合は、Moment.jsやpolyfil を接続する必要はありませんIntl
。ここで、日付を操作するためのモジュール全体は数十行です。メソッドDate#format
とそのUTCバージョンDate#formatUTC
(sandbox)を追加します。 new Date().format('W, D MM Y ., h:mm:ss', 'ru');
書式文字列の単純さと読みやすさのために、表記法を保護することに煩わされません。それらの最小値は利用可能ですが: 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#format
とDate#formatUTC
(sandbox)のいずれかによって指定されます。 new Date().format('W, D MM Y', 'ru');
ロケール形式を以下に示します。独自のコードでは、自分自身を制限できますがcore.addLocale
、ネイティブオブジェクトを展開せずにライブラリを構築できるため、ユニバーサルロケールモジュールは次のようになります。 (typeof core != 'undefined' ? core : require('core-js/library')).addLocale('ru', { weekdays: ',,,,,,', months: ':|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|' });
いくつかの例: new Date().format('DD.NN.YY');
#オブジェクトAPI
# 最初の問題:ECMAScript 5では、オブジェクトリテラルでゲッターとセッターを宣言する機能が追加されました。ただし、Object.defineProperty
/のみを使用して既存のオブジェクトのゲッター/セッターを追加できますObject.defineProperties
。これにより、完全な(非列挙または再定義できないなどの追加オプションが必要ない場合)記述子オブジェクトを転送する必要があり、これは面倒です。メソッド#Object.assign
に加えて、ECMAScript 6ではObject.mixin
、記述子を考慮して、ソースオブジェクトのプロパティをターゲットオブジェクトにコピーするメソッドを追加する予定でした。さらに、このメソッドは、キーワードを介して取得したソースオブジェクトのメソッドの親を再割り当てすることになっていますsuper
。しかし、彼らはそれを修正することを決め、標準への追加を延期しました。Object.define
説明どおりに動作するメソッドを追加Object.mixin
しますsuper
。ECMAScript5にキーワードがない場合、ソースオブジェクトのプロパティをターゲットにコピーし、記述子を考慮に入れて、親を再定義しません。
# 2番目の問題:ECMAScript 5では、コンストラクターを使用せずにオブジェクトを作成する機能もを介して追加されObject.create
ます。作成する際に、独自のオブジェクトのプロパティを追加するには良いでしょうが、2番目の引数Object.create
などはObject.defineProperties
、オブジェクト記述子がそのひどく面倒なプロパティを含むオブジェクトを受け取ります。メソッドを追加しますObject.make
-アナログObject.create
、2番目の引数は記述子オブジェクトではなく、記述子を考慮して、自身のプロパティが作成されたオブジェクトにコピーされる単純なオブジェクトを期待します。
ECMAScript 7はObject.getOwnPropertyDescriptorsメソッドを追加することを提案しています。このメソッドは、その名前が示すように、オブジェクト自体のプロパティのすべての記述子を含むオブジェクトを返します。理想的な二番目の引数を生成するためのペアObject.defineProperties
、およびObject.create
、および、ある程度、私たちの代わりObject.make
とObject.define
。それは面倒です。
# Array#includeメソッド(最近まで-Array#contains
、 MooToolsのバグにより名前が変更されましたが、古い名前で利用可能です)がECMAScript 7に追加される予定です。配列内の要素のエントリを口頭でチェックします。とは異なりArray#indexOf
、#SameValueZero比較アルゴリズムを使用し、ホールを無視しません。 2番目のオプションの引数は開始位置です。例: [1, 2, 3].includes(2);
#しかし、この方法Array#turn
は私の病気の想像力の成果です。しかし、判明したように、一意ではありません -LoDash_.transform
からの同様のメソッドがあります。これはArray#reduce
、コールバックからバッテリーを返さなくても、配列を任意のバッテリーオブジェクトに折り畳む方法(デフォルトは新しい配列)の代替です。メソッドとコールバックの署名は似ていArray#reduce
ます。コールバックから戻ることにより、コレクションを中断できますfalse
。例:
#覚えている#イテレータの章の反復可能な数字の例?標準ライブラリで似たようなものを拒否するあまりにも普遍的な機会。非常に短いサイクルに基づいて、所定回数実行for-of
を通じて指定された長さの配列の簡単な世代#Array.from
(そしておそらく#スプレッド)、等そのようなプリミティブな実装ではありませんが、数値のイテレータを追加しましょう。例:
#数学関数Number.prototype
-快適なもののカテゴリから。ただ、砂糖とMooToolsはのように、オブジェクトのメソッド提出するMath
中をNumber.prototype
。ここで言うことはあまりありません-コンテキストは数学関数の最初の引数になります。既存の標準化された機能を複製できますが、非常に便利です:)別の行としてこのメソッドに言及しNumber#random
ます。コンテキスト番号と渡された引数の間の乱数を返します(デフォルトは0です)。例: 3..pow(3);
#特殊文字のエスケープ
#突然、記事の最後で、JavaScriptが主にHTMLの操作に使用されていると言った場合、大きな秘密を明かしません。クライアントとサーバーの両方でHTMLを使用するには、HTMLを保護する必要があります。これはフレームワークまたはテンプレートエンジンのタスクであると誰かが言うかもしれません。しかし、そのような原始的なタスクのためにそれらを引っ張る価値はありますか? HTMLエスケープのメソッドはすべての標準ライブラリにあります。砂糖、プロトタイプでは、MooToolsのは方法ですescapeHTML
とunescapeHTML
プロトタイプライン。この伝統を壊さないようにしましょう。 '<script>doSomething();</script>'.escapeHTML();
#多くの場合、ユーザーデータから正規表現を作成する必要があり、正しい/安全な操作のためには、それらも保護する必要があります。Sugar、Prototype、MooToolsには、静的メソッドとしてRegExp
、どこかにmethodとして、このためのメソッドがありますString.prototype
。このようなメソッドをECMAScriptに追加する方法は長い間議論されてきました。これを待つことを望みますが、今のところ、提案されたオプションをライブラリに実装します。 RegExp.escape(' -[]{}()*+?.,\\^$|');
まあ、そのようなもの。
標準に関するよく知られているxkcd画像のコメントに登場することを予想していますが、ライブラリ内のほとんどすべてが利用可能であり、数百のキロバイトのライブラリからの寄せ集めを除き、それに代わるものはありません。図書館の将来の計画に関しては、それらは主に記事の本文全体に散らばっています。もちろん、パフォーマンスを最適化し、テストでコードをより適切にカバーするためにも必要です-私はまだこれを気にしませんでした。私が見逃したかもしれないものと、より良く実装できるものについてのあなたの意見に興味があります。はい それでも、私はそのように苦しみ始めたので、私は退屈しています。私はまともな給料で面白いプロジェクトを探しています。