私の言語の境界は、私の世界の境界を表しています。
-ルートヴィヒ・ヴィトゲンシュタイン
過去数か月にわたって、現在サポートされているJavaScriptバージョンへの変換[1]を使用してECMAScript 6コードのみを記述しています。
ECMAScript 6(以降ES6 、以前はES.next )は、仕様の最新バージョンです。 2014年8月の時点で、新しい機能については議論されていませんが、詳細と極端なケースはまだ明らかにされています。 この規格は2015年半ばに完成および公開される予定です。
同時にES6を採用したことで生産性が向上し(これによりコードがより簡潔になります)、一般的なJavaScriptの落とし穴をなくすことでエラーのクラス全体を排除しました。
さらに、 スレートのレクリエーションとは対照的に、言語とソフトウェアの設計に対する進化的アプローチに対する私の信念を確認しました。
これは、JSの良い部分に焦点を当て、悪い部分を隠すCoffeeScriptを使用した場合、あなたにはかなり明白なはずです。 ES6はCoffeeScriptから非常に多くの革新を引き受けることができたため、CoffeeScriptのさらなる開発に疑問を呈する人もいます。
すべての意図と目的のために、JavaScriptはCoffeeScriptをmasterにマージしました。 私はそれをものを作り、それを試してみることの勝利と呼んでいます。
-Reginald Braithwaite(@raganwald) 2015年1月14日
新機能を徹底的に分析する代わりに、最も興味深い機能について説明します。 開発者に更新を促すために、新しい言語とフレームワークは(1)説得力のある互換性のストーリーを持ち、(2) かなり大きなニンジンを提供する必要があります 。
# モジュールの構文
ES6では、モジュールを定義し、依存関係を宣言するための構文を導入しています 。 ES6はモジュールの選択またはロード方法の実際の実装とは関係がないため、 構文という言葉を強調します 。
これにより、JavaScriptを実行できるさまざまなコンテキスト間の相互作用がさらに強化されます。
JavaScriptで再利用可能なCRC32を記述する簡単なタスクを例として考えてください。
これまでのところ、この問題を実際に解決する方法に関する推奨事項はありませんでした。 一般的なアプローチは、関数を宣言することです:
function crc32(){ // … }
もちろん、コードの他の部分が参照しなければならない単一の固定されたグローバル名を導入するという警告があります。 また、 crc32関数を使用するコードに関しては、依存関係を宣言する方法はありません。 関数が宣言されると、コードが解釈されるまで存在します。
この状況では、Node.JSはrequire関数とmodule.exportsおよびexportsオブジェクトを導入するパスを選択しました。 モジュールの豊かなエコシステムを作成することに成功したにもかかわらず、相互運用性はまだいくらか制限されていました。
これらの欠点を説明する典型的なシナリオは、 browserifyやwebpackなどのツールを使用して、ブラウザー用のモジュールのバンドルを生成することです 。 require()を構文として認識し、固有のダイナミズムを効果的に軽減するため、まだ初期段階にあり ます 。
上記の例は静的分析の対象ではないため、このコードをブラウザーに転送しようとすると、破損します。
require(woot() + '_module.js');
つまり、packerアルゴリズムは、 woot()の意味を事前に知ることができません。
ES6は、ほとんどの既存のユースケースを考慮して、jQuery $などの最も非公式に存在する特別なモジュラーシステムからインスピレーションを得て、適切な制限を導入しました。
構文には慣れる必要があります。 最も一般的な依存パターンは、驚くほど非実用的です。
次のコード:
import crc32 from 'crc32';
のために働く
export default function crc32(){}
ではなく
export function crc32(){}
後者は名前付きエクスポートと見なされ、インポート構造に{}構文が必要です。
import { crc32 } from 'crc32';
言い換えれば、モジュールを定義するための最も単純な(そしておそらく最も望ましい)形式には、追加のキーワードdefaultが必要です 。 または、存在しない場合は、インポート時に{}を使用します。
# 再編
最新のJavaScriptコードで出現した最も一般的なパターンの1つは、バリアントオブジェクトの使用です。
この方法は、新しいブラウザーAPI、たとえばWHATWG フェッチ ( XMLHttpRequestの最新の代替)で広く使用されています。
fetch('/users', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ first: 'Guillermo', last: 'Rauch' }) });
このモデルの普及により、JavaScriptエコシステムが論理的なtrapに陥ることを効果的に防ぎます。
APIがパラメーターを持つオブジェクトではなく通常の引数を受け入れることを受け入れる場合、フェッチ呼び出しは引数の順序を記憶し、適切な場所にnullキーワードを入力するタスクになります 。
// fetch('/users', 'POST', null, null, { Accept: 'application/json', 'Content-Type': 'application/json' }, null, JSON.stringify({ first: 'Guillermo', last: 'Rauch' }));
ただし、実装面では、見た目は美しくありません。 関数宣言を見ると、そのシグネチャは入力機能を記述していません。
function fetch(url, opts){ // … }
通常、これにはローカル変数へのデフォルト値の手動設定が伴います。
opts = opts || {}; var body = opts.body || ''; var headers = opts.headers || {}; var method = opts.method || 'GET';
残念ながら、その普及率にもかかわらず、 ||を使用する習慣 実際にエラーを検出するのは難しいです。 たとえば、この場合、 opts.bodyが0であると想定していないため、信頼できるコードは次のようになります。
var body = opts.body === undefined ? '' : opts.body;
破壊のため、パラメータをすぐに明確に定義し、デフォルト値を正しく設定し、ローカルスコープに設定できます。
fetch(url, { body='', method='GET', headers={} }){ console.log(method); // opts. }
実際、デフォルト値はパラメータを使用してオブジェクト全体に適用できます。
fetch(url, { method='GET' } = {}){ // - {} // "GET": console.log(method); }
代入演算子を分解することもできます:
var { method, body } = opts;
これは、で提供される表現力を思い出させますが、魔法やネガティブな効果はありません。
# 新しい契約
言語の一部は、 より優れた代替物に完全に置き換えられました 。これは、JavaScriptの記述方法の新しい標準になります。
それらのいくつかについてお話します。
# varの代わりにlet / const
var x = yと書く代わりに、 let x = yと書くでしょう。 letを使用すると、 ブロックスコープで変数を宣言できます。
if (foo) { let x = 5; setTimeout(function(){ // x `5` }, 500); } // x `undefined`
これはforループまたはwhileループに特に役立ちます。
for (let i = 0; i < 10; i++) {} // `i` .
letと同じセマンティクスで不変性を提供する場合は、 constを使用します。
# 連結ではなく文字列パターン
標準のJavaScriptライブラリにはsprintfまたは同様のユーティリティがないため、ラインナップは必要以上に苦痛を伴います。
文字列パターンにより、複数行のサポートだけでなく、文字列への式の埋め込みも簡単になりました。 「で」を置き換えるだけ
let str = ` ${first}. ${new Date().getFullYear()} `;
# プロトタイプではなくクラス
クラスの定義は面倒な操作であり、言語の内部構造に関する深い知識が必要でした。 内部構造を理解することの利点は明らかですが、初心者の入場基準は不当に高いものでした。
クラスは、コンストラクター関数 、 プロトタイプメソッド、およびゲッター/セッターを定義するための構文糖を提供します。 また、組み込みの構文を使用してプロトタイプの継承を実装します(追加のライブラリまたはモジュールなし)。
class A extends B { constructor(){} method(){} get prop(){} set prop(){} }
私は最初、クラスがポップアップしない(引き上げられる)ことを知って驚いた(説明はこちら )。 したがって、 関数A(){}ではなくvar A = function(){}に変換して、それらについて考える必要があります。
# ()=>関数の代わりに
(x、y)=> {}が 関数(x、y){}よりも記述が短いためだけでなく、関数の本体でのこの動作は、ほとんどの場合、必要なものを参照します。
いわゆる「太い矢印」関数は字句的に関連しています。 2つのタイマーを開始するクラス内のメソッドの例を考えてみましょう。
class Person { constructor(name){ this.name = name; } timers(){ setTimeout(function(){ console.log(this.name); }, 100); setTimeout(() => { console.log(this.name); }, 100); } }
初心者の恐怖に対して、最初のタイマー( functionを使用)は「未定義」を出力します。 そして、ここで2番目は正しくnameを表示します 。
# 一流の非同期I / Oサポート
非同期コードの実行は、言語のほぼすべての歴史にわたって私たちを伴ってきました。 結局のところ、 setTimeoutはJavaScript 1.0が登場した頃に導入されました。
しかし、おそらくこの言語は非同期性をサポートしていません。 将来実行されるようにスケジュールされている関数呼び出しの戻り値は、通常未定義であるか、 setTimeoutの場合はNumberです。
Promiseの導入は、互換性と構成の非常に大きなギャップを埋めました。
一方では、APIがより予測可能になります。 テストとして、新しいフェッチ APIを検討してください。 これは先ほど説明した署名に対してどのように機能しますか? あなたはそれを推測しました。 Promiseを返します。
過去にNode.JSを使用したことがある場合、コールバックが署名に従うという非公式の配置があることを知っています。
function (err, result){}
コールバックは一度しか呼び出されないという考えも非公式に指摘されています。 そして、 nullはエラーがない場合の値になります( 未定義またはfalseではありません)。 例外として、おそらくこれは必ずしもそうではありません。
# 未来へ
ES6は、エコシステムでかなりの勢いを得ています。 Chromeとio.jsにはES6の機能が既に追加されています。 これについてはすでに多くのことが書かれています 。
しかし、この人気の大部分は、実際のサポートではなく、 変換のためのユーティリティの存在によるものであることに注意してください。 ES6の変換とエミュレーションを可能にする優れたツールが登場し、最終的にブラウザーはコードのデバッグとエラーのキャッチ(コードカードを使用)のサポートを追加しました。
言語とその提案された機能の進化は、実装に先んじています。 上で述べたように、 Promiseは、 コールバック地獄の問題の解決策を一度だけ提供する独立したユニットとして本当に興味深いものです。
ES7標準では 、Promiseオブジェクトの非同期機能を導入することでこれを行うことを提案しています。
async function uploadAvatar(){ let user = await getUser(); user.avatar = await getAvatar(); return await user.save(); }
この仕様は長い間議論されてきましたが、ES6をES5にコンパイルする同じツールがすでにこれを実装しています 。
新しい言語構文とAPIを採用するプロセスが、始めたばかりの人にとって奇妙さをさらに欠くようにするために、まだ多くの作業があります。
しかし、1つ確かなことがあります。この未来を受け入れる必要があります。
脚注:
1。 ^この記事では、 ソースコードをJavaScriptでソースコードにコンパイルする方法を説明するために、「変換」という言葉を使用しています。 しかし、この用語の意味は技術的に物議を醸しています。