回答(全3件)
回答の評価を上げる
以下のような回答は評価を上げましょう。
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
6
この機能は開放されていません
評価を下げる条件を満たしてません
クラスパターン
クラスパターンは instanceof
演算子や Object.getPrototypeOf
で継承関係を確認することが出来ます。
users instanceof Users
を実行することで Users.prototype
配下のプロパティが存在することを保証できます。
class Users {
constructor(/* ...name */) {
this.names = Array.from(arguments).map(String);
}
add (name) {
var names = this.names;
name = String(name);
return names.includes(name) ? names.length : names.push(name);
}
remove (name) {
var names = this.names, i;
name = String(name);
i = names.indexOf(name);
if (i !== -1) {
names.splice(i, 1);
}
}
}
var users = new Users('Ken', 'Alice', 'John');
console.log(users instanceof Users); // true
console.log(Object.getPrototypeOf(users) === Users.prototype); // true
他に prototype
上にプロパティを定義することでメモリを節約することが出来ますが、これはシングルトンパターンを複数生成する場合でも実装することが出来ます。
シングルトンパターン
オブジェクト初期化子で一つのオブジェクトにプロパティ/メソッドをまとめる事をシングルトンパターンと呼びます。
var users = {
add: function add (name) {
var names = this.names;
name = String(name);
return names.includes(name) ? names.length : names.push(name);
},
remove: function remove (name) {
var names = this.names, i;
name = String(name);
i = names.indexOf(name);
if (i !== -1) {
names.splice(i, 1);
}
}
};
ご質問のコードではクロージャを使ってインスタンスを生成しているかのような設計にしていますが、基本的にはこの形の派生だと思います。
function createUsers (/* ...name */) {
return {
names: Array.from(arguments).map(String),
add: function add (name) {
var names = this.names;
name = String(name);
return names.includes(name) ? names.length : names.push(name);
},
remove: function remove (name) {
var names = this.names, i;
name = String(name);
i = names.indexOf(name);
if (i !== -1) {
names.splice(i, 1);
}
}
};
}
var array = [createUsers('Ken', 'Alice', 'John'), createUsers('Ken', 'Alice', 'John'), createUsers('Ken', 'Alice', 'John')]; // 3つのオブジェクトを生成したので3倍メモリを消費する
ご質問のコードを含めて素直にこの実装をすると生成したオブジェクトの数だけ add
, remove
関数オブジェクトを生成するという問題があります。
この問題はコードを少し書き換えることで解消することが可能です。
var createUsers = (function () {
function add (name) {
var names = this.names;
name = String(name);
return names.includes(name) ? names.length : names.push(name);
}
function remove (name) {
var names = this.names, i;
name = String(name);
i = names.indexOf(name);
if (i !== -1) {
names.splice(i, 1);
}
}
return function createUsers (/* ...name */) {
return {
names: Array.from(arguments).map(String),
add: add,
remove: remove
};
}
}());
ただし、苦労に見合う対価がないのでここまで苦労するならクラスパターンで書いた方がスマートだと私は思います。
Re: mit0223 さん
2016/10/05 17:40 投稿
コメント(4)
2016/10/05 19:05
> その名の通り、シングルトンにしか使わなければ、関数オブジェクトの数の問題は無いと考えて良いでしょうか?
問題ないと思います。
> あと、this を使って自由変数を避けておられますが、自由変数を避けたほうが良い理由というのはありますでしょうか?
this は実行コンテキストに入るときに決定する為、Funcion#call や Function#bind を使って this 値を変更出来るようになり、他のインスタンスメソッドに移植することも容易になります。
例えば、Array.prototype.forEach.call(document.querySelector('p>.hoge')) は比較的よく知られていますね。
2016/10/05 19:27
あー、「関数はオブジェクトだよー」、「メソッドもオブジェクトだよー」と言いながら、持って回るんですね。勉強になります。
ただ、クロージャがいいなと思ったのは this を使わなくてすむからというのもあるんです。Javascript の this って、 callback をインラインで書いてたりすると、ぱっとみて何を指してるのかわかりにくいですよね。Promise の then に渡す関数も受け渡すデータを this にしてくれるという手もあったと思うのですが、第一引数に渡されるのは、マニアックコーディングを避ける方向ではないでしょうか。
miyabi-san との議論にもありますが、Javascript のマニアックコーディングは避けられて、排除されていくのではないでしょうか。
クロージャを使わずに class で書く。引数を受け取って返却値で返す。他の言語から来た人に対してハードルをさげる大事なことかもしれません。と思い始めました。
2016/10/05 20:46 編集
> Javascript の this って、 callback をインラインで書いてたりすると、ぱっとみて何を指してるのかわかりにくいですよね。
他言語習得者がJavaScriptを学び始めた時に同様の感想はよく聞きます。
ただ、「分かりにくいから this を使わないようにしよう」は可能性の幅を狭めるだけで「JavaScript の this も使いこなそう」とするのが真の JavaScripter だと思っています。
先述の Array.prototype.forEach も含めて既存のビルトイン関数の多くは内部的に this を使っており、this を学ばずして「JavaScript を理解できた」とはいえないとも思います。
JavaScript は基本的に後方互換性を捨てない方向で進化を遂げてきたので「ある時、いきなり this を使わない言語になる事」はまずありません。
ES6 で this を束縛しないアロー関数が出てきましたが、アロー関数とて上位スコープの this を参照する仕組みがあります。
> Promise の then に渡す関数も受け渡すデータを this にしてくれるという手もあったと思うのですが
Promise はコールバック関数なので this 値に束縛する理由がなかったのだと思います。
コールバック関数の this 値は原則として束縛されません(addEventListener 等の例外はあります)。
また、this は Function#callやFunction#bind で書き換え可能という点に注意が必要です。
isset($replyData['Comments']["total_count"]) ? $replyData['Comments']["total_count"] ?>
ベストアンサー
回答の評価を上げる
以下のような回答は評価を上げましょう。
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
4
この機能は開放されていません
評価を下げる条件を満たしてません
結論からいうと、ES6ならclassを使った方が良いでしょうね。
質問1
これがそのままクロージャーだと思ってます(あまり自信ない)
即時実行関数で変数を閉じ込めてプライベートなプロパティを宣言してますしね。
質問2
実質クラスとクロージャーは実行される内容は同じです。
(JavaScriptはクラスベースではなく、プロトタイプベースのオブジェクト指向言語なので)
さて、C言語等からの流れでリストをfor文で扱う事はバグの温床になっているので、
モダンな言語では「for文使うのやめい!foreachを使え!」という流れが広がっています。
クロージャーを使って書くやり方も人の数だけ実装があり、
同じくバグの温床になっているのでES6で書く場合は出来るだけClassで統一するべきでしょう。
ES5ではクラスが無いのが問題となりますが、
CoffeeScriptやTypeScriptなどのAltJSでは、Class構文が自動的にクロージャーに展開されるのでプログラマーがクロージャーを書く必要がなくなります。
表現も簡素でキレイになりますので、ES5なら出来るだけAltJSを使うべきでしょう。
下記はCoffeeScriptから派生して生まれたLiveScriptの簡単なスクリプトです。
公式サイトの右上のテキストエリアにソースコードを貼り付けて、
CompileボタンやRunボタンをクリックするとソースや結果が見れますので、ES5的な解決策の一つとして答えになるのではないかと思います。
class User
({@name, @age})->
is_over_20: -> @age >= 20
UserSeeds =
* name: \miyabi
age: 17 # 永遠の的な意味で
* name: \mit-san
age: 21
* name: \teratail
age: 2
|> map -> new User it
|> filter (.is_over_20!)
# [User {name: \mit-san, age: 21}]
2016/10/05 18:11 投稿
コメント(2)
2016/10/05 18:48
なるほど、マニアックなコーディングを避ける意味があるのですね。「クロージャこそ Javascript の真髄だー!」とか言っていてはだめなんですね。
LiveScript は難しいですが、なんとなく雰囲気はわかりました。(そんなに若いわけないじゃないですか・・・)
2016/10/05 19:04
クロージャーかっこいいですよね。百戦錬磨の戦士のような渋さを感じます。
でも、実際問題ES6でClassが採用された背景には、誰もクロージャーで書ききれなかった(言い過ぎ)ことがあるのでしょうね。
いずれ三項演算子と共にコードゴルフ勢の嗜みになるんじゃないかなと思ってます。
isset($replyData['Comments']["total_count"]) ? $replyData['Comments']["total_count"] ?>
回答の評価を上げる
以下のような回答は評価を上げましょう。
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
2
この機能は開放されていません
評価を下げる条件を満たしてません
大きな違いとして、class
構文ではプロトタイプにメソッドをセットする、ということがあります。そのため、同じ種類のインスタンスを多数作っても、メソッドになる関数オブジェクトは1つで済みます。
オブジェクトに直接関数オブジェクトを割り当てていくと、同じようなオブジェクトを大量に作っても、そのたびごとに関数オブジェクトが生成されてしまう、ということになりかねません(優秀な処理系なら最適化してくれるかもしれませんが)。
2016/10/05 17:06 投稿
コメント(1)
2016/10/05 17:16
なるほど!
クロージャを大量に生成すると、メモリとJITコンパイラに負担がありそうですね。考えがいたってませんでした。ありがとうございます。
isset($replyData['Comments']["total_count"]) ? $replyData['Comments']["total_count"] ?>
15分調べてもわからないことは、teratailで質問しよう!
92.50%
関連した質問
-
受付中
[Milkcocoa] send() メソッドのデータ量上限はいくつでしょうか?
現在、Milkcocoaにて任意の画像データをsend()にて送りたいと思っております。 

send()によるデータの通信はできているのですが、画像データ(base64形式,
-
受付中
Selenium2 javascript 値の取得方法
Selenium2をjavascriptから使っています。 
(MacOS上でnode.js + selenium-webdriver、ブラウザはChromeを使用) 

画面上
-
解決済
つNodejsによるStack Overflow APIからのデータ取得
var request = require('request');
var API = "https://api.stackexchange.com/2.2/users?page=
-
解決済
DynamoDBに複数の項目を追加するプログラムでcontext.done()をコールするタイミング...
AWSのLambdaでS3にアップロードされたJSONファイルを読み込んで 
その内容をDynamoDBへ書き込むプログラムを作成しています。 

JSONファイルには複数のデー
同じタグがついた質問を見る
-
JavaScript
5445questions
JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。
-
オブジェクト指向
95questions
オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。
-
ECMAScript
40questions
ECMAScriptとは、JavaScript類の標準を定めるために作られたスクリプト言語です。
-
関数型プログラミング
9questions
関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。
2016/10/05 18:15
なるほど、シングルトンパターンですか。
その名の通り、シングルトンにしか使わなければ、関数オブジェクトの数の問題は無いと考えて良いでしょうか?
あと、this を使って自由変数を避けておられますが、自由変数を避けたほうが良い理由というのはありますでしょうか?
→質問の方を編集しておきます。