忍者ブログ
プログラマ、ララ・ベル子さんのゆるふわ奮闘記。

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

ES2015のブロックスコープとアロー関数でJSの罠回避

皆さん、こんにちはベル子です。
このブログでコード貼り付けると見た目が悪くて耐えられないので
これからgistを使うことにしました。
デキるレディーはいつもオシャレ。

ということで本題です。
こないだのES2015でうまく説明できなかったfor文で同じ値が参照され続けてしまう罠のサンプルを作成したので、解説しようと思います。
まずは下のコードを見て出力結果を予想してみてください。何が表示されるでしょうか?
これを書いた人が期待しているのは、以下のような出力結果です。

0 hello I'm hanako 
1 hello I'm hanako
2 hello I'm hanako





ですが、実際は以下のように出力されます。
※以下はJS Binでの出力結果です。

3 hello I'm JS Bin Output 
3 hello I'm JS Bin Output
3 hello I'm JS Bin Output





上のコードには2つの問題点があり、それの原因となっているのがjavascriptのスコープです。
スコープというのは「変数が参照できる範囲」のことです。
元々、ES6以前のjavascriptにはブロックスコープがありませんでした。
ブロックスコープというのはif文やfor文の{}で囲まれた範囲内でしか変数を参照できなくさせるスコープのことです。
なので、for文の初期化で定義したカウンタ変数iはfor文の外で定義されたのと、スコープ上は変わりがありません。


var i = 0;
for (i = 0; i < 3; i++) {
setTimeout(function(){
console.log(i + " hello I'm " + this.name);
}, 0);





そしてsetTimeoutの中のコールバック関数には、iという変数の参照を渡しているだけなので、ループが先に実行されて、ループが終わったあとでsetTimeoutの中の関数が実際に実行される際には最後に代入された3を参照してしまうので、期待の結果が得られません。

これをvar i = 0の部分をlet i = 0に置き換えてみると、ループの度に新しいiが定義されて、その新しいiを関数に渡すことになるので、期待どおりにインデックスを出力してくれます。

そしてもう一つはthisの参照先の問題です。
setTimeoutの中のコールバック関数は、setTimeout() が呼び出された関数とは「別の実行コンテキスト内で」実行されてしまうので、結果的にグローバルオブジェクトを参照することになります。

実行コンテキストってなんだよって思うと思うんですが、javascriptは関数を実行するたびに新しい実行コンテキストというものを作成しています。
関数の外にあるコードはどうなるかというとグローバル実行コンテキストというのがあって、グローバルオブジェクトで管理されています。

要するに、setTimeoutのコールバック関数内にthisを書いてしまうと、this=グローバルオブジェクトになるということです。
だから、期待の値が出力されません。
これをアロー関数を使うと、setTimeoutを囲ってる関数の実行コンテキストのthisの値が設定されるようになります。

難しい言い方をしましたがsetTimeoutを囲ってる関数のthisとコールバック内のthisが同じになるということです。

というわけで、ES2015のブロックスコープletとアロー関数を使って書き換えると、
すごく直感的に分かりやすいコードになるというわけです。めでたしめでたし。

拍手[0回]

PR

ES2015関連の英語を日本語に訳しておく

次のPG会のホストは私ベル子です。

参考資料が英語で書いてあることが多いので、スライドを書く際に簡単に日本語に変換できるように、ES2015関連の英語/日本語辞書を作っておこうと思います。

Constant
定数

immutable variable
不変値
Block-Scoped Variables
ブロックスコープ変数

Block-Scoped Functions
ブロックスコープ関数

Arrow Functions
アロー関数

Expression


Statement


Bodies
文体

intuitive
直感的な

Lexical
語彙的な

Parameter Handling
引数操作

Default Parameter Values
デフォルト引数

Rest Parameter
残余引数

Spread Operator
スプレッド演算子

Template Literals
テンプレートリテラル

String Interpolation
文字列補間

Raw String Access

生の文字列にアクセス

Binary & Octal Literal

バイナリ&八進リテラル

Unicode String & RegExp Literal

ユニコード文字列&正規表現リテラル

Property Shorthand

プロパティの省略

Computed Property Names

算出済みプロパティ名

Destructuring Assignment

分割代入

Fail-Soft Destructuring

フェイルソフト分割

Class Definition

クラス定義

Class Inheritance

クラス継承

Iterators

繰返し処理、イテレータ

Collation

照合

Currency Formatting
通貨フォーマット、通貨書式

うーーん。こうやって読み返すと辞書を作るほどでもなかったかな。
引数、演算子、関数とか特定の単語は、英語ではなく日本語で表記する傾向にあるっぽいけど、それ以外はカタカナで当てていけばよさそうです。

個人的にも全てカタカナよりは漢字が混じってるほうが読みやすいので、翻訳する人もそういう部分も意識しているだろうなと思います。

それでは!

拍手[0回]

JSでObjectを配列にする様々な方法

皆さん、こんばんはベル子です。

あまりJSに詳しくない人に教えるときに
きっと一度は経験するかもしれない質問を紹介します。

「これはObjectなんで〜〜」
「Objectって何ですか?」

あなたの心の声:で、出たーーーーーーーーその質問!

「phpで言うところの連想配列みたいなものです(早口&小声)」

ほらそんな細かいこと気にしないで先に進みますよ★感を出しまくってスルーするのが定番のこの質問です。
なぜなら、ぱっと話してその日のうちに理解できるような代物ではないということをあなたは知っているからです。

Objectが何なのか、実際は連想配列というよりはClassに近いものだと認識していますが、実装上では連想配列のように使われることが多いので、分かりやすく説明するために後ろのマサカリを担いだ誰かに見つからないように、震えながらそう応えたりGoogleのURLを投げつけたりするのが定番です。


どのように作るかというと

var Belko = new Object();

または

var Belko = {};

ですね。よく見るのは後者だと思います。
厳密に言うと、こうやって作ってるのはカスタムオブジェクトで、
JSには組み込みのオブジェクトも存在します。


var d = new Date();







こんなやつ(例は日付オブジェクト)です。

さらに言うとコンストラクタ関数を使って作る方法もあります。
よくJavaScriptオブジェクト指向プログラミング入門的なやつに出てくるやり方です。


function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}

var mycar = new Car("Eagle", "Talon TSi", 1993);







でも、まだまだ不安でいっぱいなのでObjectが何なのかドキュメントを確認してみましょう。
もしかしたら、すごく分かりやすいかもしれないですよね!ワクワク。

オブジェクトを利用する

JavaScript において、オブジェクトはプロパティと型を持つ独立した存在

まーーーーーさーーーーかーーーのーーーー

いや、思っていたとおり、安定の全く理解不能な説明です。

一つだけ言えるのは、よく分からない文章をいくら眺めていても分かるようにはならないということ!
これは私がエンジニアをはじめた頃から徹底していることですが、分からない文章の内容を調べあげるより、まずは最後まで読む、分からないまま実装を始める(w)、そしてから知りたいことを調べる、そして再度読む、そしてまだ分からないことを調べる、脱線する、また読む、実装する。
みたいな感じでやることです(徹底というわりに適当w)。

そしてJavaScriptのObjectを分かりづらくしている原因のもうひとつは、サーバとクライアントでデータをやり取りするときにJSON形式でやり取りしたりします。
このJSONというフォーマットとJSのObjectリテラルが非常に似ているから混乱してしまうのです。
(だいたいキーがダブルクォーテーションなのがJSONです。)

そしてこのJSONはJSのObjectに変換ができます。

JSON.parse('{"p": 5}');

ObjectもJSONデータに変換できます。

JSON.stringify({ x: 5 });

ですね!

と、ここまでは前置きです。

これからが本題なのですが、Vue.jsを使うようになってからというかモダンJSを書くようになってから、Objectがいたるところに出てきます。
そんなObjectを配列にしたい!そんなふうに思うこともベル子にはあります。

調べてみたらObjectを配列にする方法にはいろいろあったので、またお得意のJS Binでご紹介しようと思います!!
一応コードも貼っておきます。

http://jsbin.com/zucigazori/edit?html,js,console


// ↓これはJS BinでES6使う用
/* jshint esnext: true */

var rgb = {r: 19, g: 79, b: 92};

// mapでやる方法
var rgb_1 = Object.keys(rgb).map(function (key) {
return rgb[key]; });
console.log(rgb_1);


// mapでキーの配列も作れる
var rgb_2 = Object.keys(rgb).map(function(key, index) {
return key;
});
console.log(rgb_2);


// ES7
// var rgb_3 = Object.values(rgb);
// console.log('rgb_3: ' + rgb_3);


// ES6のアローファンクション
var rgb_4 = Object.keys(rgb).map(key => rgb[key]);
console.log(rgb_4);


// Underscore.js
var rgb_5 = _.values(rgb);
console.log(rgb_5);


// jQuery
var rgb_6 = $.map(rgb, function(value, index) {
return [value];
});
console.log(rgb_6);






この中でベル子は、今回、最もスッキリ書けるES7記法で書いてみました。
というのも、Laravel ElixirでbrowserifyしてるいうことはBabelでトランスパイルしてるということでBabelがObject.valuesもPolyfillってくれるということです!!
OSSの恩恵ハンパないですね。

ではでは、突然おわりまする。
またねーーー★

拍手[2回]