私の暇な時間の最後の日はスケジューラを作成していました 。 アイデアは簡単でした。AngularJSと他のJavaScriptライブラリを使用して、 世界の仲間のクローンを作成しました。
そして、あなたは何を知っていますか? 決して楽しいものではありませんでした。 何かに取り組んでいる間、私は長い間怒っていませんでした。つまり、通常はすぐに不満を表明するためです(Twitterでフォロワーに謝罪します)。
JavaScriptを定期的に使用していますが、他の人のコードを扱う必要はほとんどありませんでした。 通常、jQuery、アンダースコア、そして時にはAngularJSにのみアタッチされます。 ただし、今回はオールインし、さまざまなサードパーティライブラリを使用することにしました。
このプロジェクトでは、jQueryを使用しました。jQueryはこれなしではできません(そしてその理由は?)。また、AngularJSといくつかのUIコンポーネント(angular-uiおよびjQuery UIバインディング)を使用しました。 タイムゾーンを操作するために、moment.jsが使用されました。
私はすぐに、誰かの特定のコードを批判するつもりはないことに注意したい。 さらに、誰かが私のJavaScriptソースを見ると、コードは少し良くなり、時には悪くなります。なぜなら、私はそれにあまり時間を費やさなかったからです。実際、この言語を使った経験はあまりありません。
しかし、JavaScriptライブラリー(少なくとも使用しているライブラリー)にひどい品質のコードが表示されるという驚くべき傾向に気づき、なぜこれが起こっているのか疑問に思いました。
私はjsライブラリに多くの問題を抱えていましたが、それらはすべて、誰もが言語の機能について気にしなかったという事実の結果でした。
サードパーティのJavaScriptコードを積極的に研究し始めた理由は、3MBの都市名をtypeahead.jsオートコンプリートライブラリに送信しようとする素朴な試みが、信じられないほど遅いUIにつながったためです。 明らかに、今では頭の良い人は一度にオートコンプリートでフィールドに大量のデータを送信しませんが、最初にそれらをサーバー側でフィルタリングします。 しかし、この問題は、データの読み込みが遅いことではなく、フィルタリングが遅いことにあります。 私が理解できなかったのは、26,000の要素の線形検索があったとしても、それほど遅くないはずだからです。
背景
そのため、インターフェースは低速でした-明らかに、エラーは多すぎるデータを転送しようとした私の試みにありました。 しかし、興味深いのは、先行入力ウィジェットを使用するとパフォーマンスが低下したことです。 そして時には非常に独特な方法で。 それがどれほどクレイジーだったかを示すために、いくつかの初期テストを行います。
- 「san」と入力して、サンフランシスコを探しています。 〜200ミリ秒。
- 「fran」と入力して、サンフランシスコを探しています。 〜200ミリ秒。
- 「san fran」と入力して、サンフランシスコを探しています。 第二に。
- 再び「san」と入力して、サンフランシスコを探しています。 第二に。
何が起こっているの? 何かを複数回検索した場合、検索はどのように分類されますか?
私が最初にしたことは、新しいFirefoxプロファイラーを使用して、それが非常に時間がかかることを確認することでした。 そしてすぐに、彼は先読みで、あまりにも奇妙なものをたくさん見つけました。
ボトルネックはすぐに見つかりました。 問題は、 データ構造と奇妙なアルゴリズムを選択する際の重大なミスでした。 一致を見つける方法は空想的で、行のリストを調べてから、それぞれを元のリストを含む他のリストに含めるかどうかをチェックするなどの素晴らしいことを含みます。 最初のリストに6,000個のアイテムがあり、それぞれについて、このアイテムが実際にリストにあるかどうかを確認するために線形検索が起動されると、すべてに非常に長い時間がかかります。
はい、エラーが発生します。少量のデータでテストを実行しても、気づくことはありません。 私が送った何千もの都市とタイムゾーンが多すぎました。 また、誰もが毎日検索機能を書いているわけではないので、私は誰も責めません。
しかし、この作品をデバッグしなければならなかったという事実のために、私は以前見た中で最も奇妙なコードに出会いました。 さらなる調査の後、同じ偏心が先行入力だけでなく見つかったことが判明しました。
これに基づいて、JSは一種のWild Westソフトウェア開発であると確信しています。 まず第一に、それは品質の点で2003年のPHPコードと競合しますが、サーバーではなくクライアント側で動作するため、心配する人が少ないようです。 低速のJavaScriptを支払う必要はありません。
スマートコード
最初の問題点は、JSがキュートで「スマート」な言語のように見える人々です。 そして、コードのレビューを行ってバグを見つけるとき、私はとんでもなく妄想的になります。 使用されているイディオムを知っていても、副作用が意図的なものなのか、誰かが間違えただけなのかはわかりません。
例として、typeahead.jsの一部を示します。
_transformDatum: function(datum) {
var value = utils.isString(datum) ? datum : datum[this.valueKey],
tokens = datum.tokens || utils.tokenizeText(value),
item = {
value: value,
tokens: tokens
};
if (utils.isString(datum)) {
item.datum = {};
item.datum[this.valueKey] = datum;
} else {
item.datum = datum;
}
item.tokens = utils.filter(item.tokens, function(token) {
return !utils.isBlankString(token);
});
item.tokens = utils.map(item.tokens, function(token) {
return token.toLowerCase();
});
return item;
}
, . , – c . ? - . , - . value- ( ) . – ( ) . , .
, :
{
"value": "San Francisco",
"tokens": ["san", "francisco"],
"extra": {}
}
:
{
"value": "San Francisco",
"tokens": ["san", "francisco"],
"datum": {
"value": "San Francisco",
"tokens": ["san", "francisco"],
"extra": {}
}
}
, , , , datum- , . : , . , , 10MB.
JavaScript, . , , . ''.
. : datum, ? . , – , - . , JS , - .
, , . , JS-, ,
map
.
map
,
["1", "2", "3"].map(parseInt)
[1, NaN, NaN]
.
. :
_processData: function(data) {
var that = this, itemHash = {}, adjacencyList = {};
utils.each(data, function(i, datum) {
var item = that._transformDatum(datum), id = utils.getUniqueId(item.value);
itemHash[id] = item;
utils.each(item.tokens, function(i, token) {
var character = token.charAt(0), adjacency =
adjacencyList[character] || (adjacencyList[character] = [ id ]);
!~utils.indexOf(adjacency, id) && adjacency.push(id);
});
});
return {
itemHash: itemHash,
adjacencyList: adjacencyList
};
}
:
utils.indexOf
– ,
utils.getUniqueId
.
, -
O(1)
, hashmap. . 100 000 , , .
:
utils.each(item.tokens, function(i, token) {
var character = token.charAt(0), adjacency =
adjacencyList[character] || (adjacencyList[character] = [ id ]);
!~utils.indexOf(adjacency, id) && adjacency.push(id);
});
, . , ?
!~utils.indexOf(...) &&
if (utils.indexOf(...) >= 0)
? , hashmap
adjacencyList
… , ID , . - ‘’ .
–
+
( ,
noop
) .
+value
– ,
parseInt(value, 10)
.
, Ruby. Ruby ,
''
:
false
nil
. –
''
. . JS . – .
,
""
false
. , . . jQuery
each
this
. ,
this
, , .
:
> !'';
true
> !new String('');
false
> '' == new String('');
true
Ruby, JavaScript. . , , . - , , .
~
indexOf
,
-1
, . , , « ».
«»
, , JS . , Python – , runtime . JavaScript . AngularJS.
, JS , . . . , , .
, Angular , , , , . , – , . , , , ' ' – .
, , . - . , , .
, Angular DOM . , , firing’ . .
, . JavaScript. API stateful-. , , . moment-. , ,
foo.add('minutes', 1)
. , , API . , , , .
, JS API, ‘’ . , Python. Python , immutable- . , , first-, -.
« »
Angular, . UI JavaScript, . . , .
fooBar
, DOM
foo-bar
. ? ,
style
DOM API, . , , . . Angular-, .
Angular JS- . AnguarJS, , . , JS: . . . Angular, .
?
Python JavaScript . , Angular URL . , . ? , - , .
Angular. JS HTML. DOM, , . - HTML :
function escapeHTML(string) {
var el = document.createElement('span');
el.appendChild(document.createTextNode(string));
return el.innerHTML;
}
URL:
function getQueryString(url) {
var el = document.createElement('a');
el.href = url;
return el.search;
}
, .
- , , , , . JS-.
« »
PHP , , . . , . : , , . , , PHP- mod-. - , . , register_globals, SQL, .
JS . , , . , , .
: , . PHP , JS « » , .
?
, JavaScript. , , , PHP: , , , , . , , , , .
Python-. , , , . Django , , .
, JavaScript- , .
Armin Ronacher,
09/12/2013