プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。
■運営サービス一部
http://crazy-wp.com/
http://webukatu.com/
新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。
Qiita内にそれ系の記事も書いてます。
エンジニアで稼ぐために大切な13のコト
WEBサービスで起業したい人に読んで欲しい18のコト
1.変数と型
1.1.変数はvarをつけなくても宣言出来る。
宣言時に初期化していない場合は、「undefined」という特殊な値が入る。(nullとは別もの)
var age = 12;
//もしくは
age = 12;
・変数名は大文字小文字が区別される
・変数名には「if」「for」「class」などの「予約語」は使えない
※関数内でvarをつけずに新たな名前の変数を作るとグローバルスコープに変数が作られるため、変数は基本的にvarを使って宣言すること
1.2.jsで表現される全ての値はオブジェクトだ!
配列も何もかもオブジェクト。
文字列、数値、真偽値だけはプリミティブ値(プリミティブ型)だが、ラッパーオブジェクトで、オブジェクトとして使える。
プリミティブ値というのは、オブジェクトではないもの。
myObj[0] = 'p';
myObj[0] = 'a';
myObj[0] = 'm';
myObj[0] = 'u';
console.log(myObj);
myStr = new String('pamu');
console.log(myStr);
//どちらも同じ結果になる
1.2.データ型について
データ(値)の型には「プリミティブ型」と「リファレンス型」がある。
プリミティブ型
基本的で単純(これ以上細かくできない)なデータの型のこと。
null、undefined、Boolean、Number、String 型
リファレンス型
複合的なデータ型のこと。
プリミティブ型の値(プリミティブ値)をいくつも組み合わせたりしているもの。
bject、Array、Function 型など、すべてのオブジェクトが該当します。
1.2.オブジェクトにはメソッドやプロパティが既にある
1.2.プリミティブ型とは?
プリミティブ値とはこれ以上小さくならない最小の値のこと。(後で出てくる、「オブジェクト(オブジェクト型)」ではない)
オブジェクトや配列などはプリミティブ値などを複数組み合わされて構成されている。
プリミティブ型はオブジェクトではないので、メソッドやプロパティを持たない。
(でも、実際はメソッドが使える。メソッド使う時にラッパーオブジェクトで包まれ、メソッドを使い終わったら破棄しているので、オブジェクトのように扱える)
プリミティブ値は5つある
・文字列
・数値
・真偽値
・null
・undefined
※プリミティブ値のコピーはオブジェクトのコピーと違って値をそのままコピーするのでメモリを使う
プリミティブ値の例
myStr = 'sam'; // リテラルで記述した場合には、オブジェクトではなくプリミティブ型のプリミティブ値となる
myStr2 = new String('sam'); // こうするとオブジェクト型
1.3.プリミティブ値とオブジェクトの比較
プリミティブ値同士は値を比較している。
===で比較する場合、オブジェクトは参照を比較するので、中身が同じ値であっても、参照先が違えばfalse。
※==で比較した場合は中身が同じ値であればtrueになる
オブジェクトというのは、中身はメモリにある値を参照するための参照先のアドレスが入っているだけ。
だから、実際はアドレス同士を比較しているということ
2.オブジェクト
2.1.typeOf演算子でオブジェクトの型が分かる。
2.2.オブジェクトは動的なプロパティを持てる
オブジェクトを生成した後にプロパティをつけられる。
ネイティブオブジェクトも同様。
全てはオブジェクトだということ。でも、ネイティブを改造するのはやめよう。
myStr = new String('sam');
myStr.myProp = 30;
2.2.1.プロパティの追加方法
var obj = {};
// プロパティが定義されていないとundefinedが返される
console.log(obj.prop); // => undefined
// プロパティに値を設定することで、プロパティ定義も一緒に行える
obj.prop = 1;
console.log(obj.prop); // => 1
オブジェクトの作成時に、プロパティを定義、設定することもできます。
// プロパティを定義、設定することができる
var obj2 = {
prop: 1,
prop2: 'foo'
};
console.log(obj2.prop); // => 1
console.log(obj2.prop2); // => 'foo'
2.2.2.プロパティの継承
applyメソッドやcallメソッドを使うことで、擬似的に指定したオブジェクトのメソッドとして呼び出せる。
function SuperMsg(msg){
this.msg = msg;
}
function ErrorMsg(msg, errLevel){
this.errLevel = errLevel;
SuperMsg.apply(this,[msg]); //applyメソッド。第一引数にthisを指定することでサブクラスのプロパティにスーパークラスのプロパティを定義できる。第二引数には、渡すべき引数を渡す。
}
var errMsg1 = new ErrorMsg('エラーです','error');
alert(errMsg1.msg);
2.2.3.プロパティの削除
var obj = {};
obj.prop = 1;
// プロパティの作成
delete obj.prop;
// プロパティを参照すると、undefinedを返す
console.log(obj.prop); // => undefined
2.2.3.アクセサプロパティ(セッター、ゲッター)
var circle = {
redius : 1, // 半径
get diameter() { return this.radius * 2; }, // 直径は半径から算出
set diameter(value) { this.radius = value / 2; } // 直径から半径を算出
};
circle.diameter = 5; // set diameter が呼ばれる
console.log(circle.radius); // => 2.5
console.log(circle.diameter); // => 5 (get diameterが呼ばれる)
2.3.全てのコンストラクタのインスタンスはコンストラクタにポイントするconstructorプロパティをもつ
console.log(myObj.constructor === Object) // true と表示される
console.log(myObj.constructor); //コンストラクタ関数が表示される
2.4.instanceOf演算子でコンストラクターを特定できる
myCon = new MyConstructor();
console.log( instanceof MyConstructor ); // true と表示される
2.5.オブジェクトのプロパティにはどんなものでも入れられる。
オブジェクトでも配列でもプリミティブ値でもなんでも入る。
2.6.オブジェクトは多次元配列にできる
オブジェクトの中にオブジェクト入れて、さらにオブジェクト入れてと何層構造にも出来る
2.7.プロパティのアクセスにはドット記法とブラケット記法がある
ブラケットだとキーに変数を指定できる。
myObj.myname = 'sam';
myObj['myname'] = 'sam';
2.8.プロパティの削除にはdelete演算子
プロパティのみを削除するので、プロトタイプチェーンで継承したプロパティは削除されない。
deleteで配列の要素も削除できるが、要素自体は残り中身がundefinedになる。
delete myObj.myname;
2.9.オブジェクトをリテラルで書くには{}を使う
※最後にカンマを入れると環境によっては動かないのでダメ!
2.10.ホストオブジェクトとグローバルオブジェクト
jsで用意されているネイティブオブジェクトとは別に、
jsの実行環境(ホスト環境)では「ホストオブジェクト」というものが複数用意されている。
ブラウザであれば、windowオブジェクトとwindowオブジェクトにすべて保持されているオブジェクトが「ホストオブジェクト」
DOMもホストオブジェクト。
js言語仕様のECMAScriptとは全く関係がない。(そんなものは定義されていないということ)
グローバルオブジェクトは、そのwindowオブジェクトのこと。
jsのコードは必ずグローバルオブジェクト内に格納される。
var str = 'ぱみゅぱみゅ';
var func = function(){
return 'もがみもが';
};
console.log('str' in window); // true
console.log('str' in window); // true
console.log(window.str); // ぱみゅぱみゅ | strはwindowオブジェクトのプロパティになっているということ
console.log(window.func()); // もがみもが | funcはwindowオブジェクトのメソッドになっているということ
console.log(str); // ぱみゅぱみゅ | グローバルオブジェクトはオブジェクト名を省略できる
//jsで記述したコードはグローバルオブジェクトのコンテクストに記述しているということ
// コンテクストとは「エリア」とか「中身」のような意味。グローバルオブジェクト内に記述しているということ。
2.11.for-inで列挙できる
for-inでオブジェクト内のプロパティを列挙できるが、プロトタイプチェーンのプロパティも列挙されるので注意!
for( key in myObj ){
myObj[key];
}
3.関数について
3.1.関数はnewかリテラルで書く
関数もnewかリテラルで書けます
Functionコンストラクタを使ってはだめ。eval()で関数本体を解析する必要があるのでオーバーヘッドになる。
myFunc = new Function('num1', 'num2', 'return num1+num2'); // 引数の最後は関数本体でなければいけない
myFunc = new Function('num1, num2', 'return num1+num2'); // 引数はカンマで区切って一つで書ける
myFunc = Function('num1', 'num2', 'return num1+num2'); // new使わなくても関数として使えるが、クロージャが発生しない
myFunc = function( num1, num2){ return num1+num2; }; // こっちが一般的
function myFunc ( num1, num2){ return num1+num2; }; // こっちはあまり使わない(ホスティングの問題があるため)
3.2.関数の定義方法
関数宣言、式、コンストラクタの3つの書き方がある
// 関数コンストラクタ
var func1 = new Function('str','return str');
// 関数宣言
function func2(str){
return str;
}
// 関数式
var func2 = function(str){
return str;
}
// 関数式(関数に名前をつける場合)
var func3 = myFunc(str){
return str;
}
3.3.関数は常に値を返す
returnで返すか、何もなければundefinedが自動で返ってくる。
var func1 = function(){
var a= 1;
return a;
}
var func2 = function(){
var a= 1;
}
console.log(func1()); // 1 が表示される
console.log(func2()); // returnしていないので、undefined が表示される
3.4.関数の呼び出し方(実行方法)
1.メソッドとして→オブジェクトのプロパティに関数を入れて呼び出すとメソッド呼び出しになる。
メソッド内でthisを使うとメソッドを呼び出しているオブジェクト本体を指す。
2.コンストラクタとして → newをつけるとコンストラクタ呼び出しになる。
3.apply/callを使って
4.それ以外(関数として)→グローバルコンテキストとして呼び出される(windowオブジェクトってこと)thisはwindowオブジェクトを指す。
// 関数として実行
var myFunc = function(){
return 'ぱみゅぱみゅ';
}
myFunc(1,2);
// メソッドとして実行
var myObj = {
myFunc : function(){ return 'ぱみゅぱみゅ';}
}
myObj.myFunc(1,2);
// コンストラクタとして実行
var Func = function(str){
this.name = str;
}
var myObj = new Func('ぱみゅぱみゅ');
// apply,callを使って実行
var human = {
doGreet : function(){
return console.log(this.name + arguments[0] + arguments[1]);
}
}
var pamupamu = { name: 'きゃりー' }
var moga = { name: 'もがみ' }
// apply使って実行した場合
human.doGreet.apply(moga, ['もが', 'だよ']); //mogaオブジェクトから呼び出しているかのうように実行
// callを使って実行した場合
human.doGreet.call(pamupamu, 'ぱみゅぱみゅ', 'だよ'); //pamupamuオブジェクトから呼び出しているかのうように実行
// ※call()とapply()で呼び出しのパラメータの渡し方が違うのに注意
3.5.無名関数
名前を与えられていない関数のこと。
プロパティに名前が入っていない。
主に下記の場合には無名関数を使うが、名前はないけど後でも使えるものになる。
1.イベントハンドラにセット時
2.オブジェクトのプロパティにセット時
3.setTimeoutなどのコールバック時
var mumeiFunc = function(){
}
var namaeFunc = function namaeAri(){
}
console.log(mumeiFunc.name); // Chromeの場合、空文字
console.log(namaeFunc.name); // Chromeの場合、namaeAri と表示される
※無名関数のnameプロパティはchromeでは''(空文字)、safariでは'anonymouse'となる。IEではnameプロパティは非対応
3.6.インライン関数
無名関数として使えばいい場所であえて関数に名前をつけたもの。
関数に名前をつけることで自分自身を関数内で呼べるので、他のオブジェクトのプロパティに設定しても使いまわせる関数になる。
ただし、関数の名前はその関数内でしか使えない。(変数やプロパティに格納したら、その関数名では外から呼べなくなる。)
3.6.関数は即時実行できる
関数の後ろに()をつければ即時実行になる。
var func1 = funciton(){ return 'ぱみゅぱみゅ'; }(); //関数がすぐ実行され、func1には関数自体ではなく実行後の'ぱみゅぱみゅ'が格納される
// 無名関数の即時実行方法
(function(str){
console.log(str);
})('ぱみゅぱみゅ'); // ぱみゅぱみゅ と表示される。()の中で引数を渡せる。
// 他にもいくつか無名関数の即時実行方法があるが、上記が一般的
3.7.関数は入れ子可能
関数の中に関数を書いて何階層にもできる。
var func = function(){
var func2 = function(){
var func3 = function(){
}();
}();
}(); //この例では、即時実行もさせている
3.8.関数内でreturnすれば、いつでも関数の処理を終わらせられる
var func = funciton(num){
if(a === 1){
return;
}
return num++;
};
console.log(func(1)); // undefined | if内の処理に入り、returnで処理終了させ何も返していないのでundefinedが返る
console.log(func(2)); // 3 | if文に入らずにnum++して返す
3.9.関数の巻き上げ(ホイスティング)
関数は宣言する前で使える。
関数はjsが実行される前に「実行スタック・コンテクスト(準備部屋のようなエリア)」に関数が追加される。
なので、実行し始めた段階には既にコード内の関数は呼び出せる状態になっている。
この動作のことを「巻き上げ(ホイスティング)」と呼ぶ。
※関数宣言で定義されたもののみ巻き上げられるので、関数式は巻き上げられない
func(); // この時点では変数funcは定義されていないためエラーになる
var func = function(){
};
func1(); // この時点ではfunc1()は定義されていないが、使える。
function func1(){
}
3.10.関数は自分自身を呼び出せる(再帰)
あまり使う機会はないので省略。
3.11.functionオブジェクト自体はpropertyプロパティをもつ
3.12.functionオブジェクトで生成されたオブジェクトは以下のプロパティをもつ
constructor
arguments
length
3.13.関数もオブジェクト。必ず、applyメソッドとcallメソッドを持っている。
3.14.関数名の後に()をつけると関数実行後の結果だけが渡される。
3.15.関数が戻り値を指定していない場合、undefinedが返る
3.16.関数も「値」なので、関数を引数に渡したり、関数を関数の戻り値にもできる
3.17.関数はオブジェクトなので、関数自体にプロパティやメソッドをもたせられる
3.18.引数とパラメータ
引数とパラメータの数が違っててもOK
パラメータが多い場合は、undefinedが入る。
引数が多い場合は、余った引数は無視される。(取り出す方法もある)
パラメータには暗黙的にargumentsとthisが渡る。
argumentsは配列形式でパラメータが全て入ってる(配列みたいに見えるけど配列と同じように扱おうとすると痛い目を見る)
thisは関数を呼び出した元(親オブジェクトの参照)を指す。
3.19.argumentsを変えることもできる
引数の変数と連動しているので、どちらも書き換わる。
var func = function(str){
arguments[0] = '武田';
console.log(str); // 武田
str = 'じろーらも';
console.log(str); // じろーらも
};
func('じろーらも');
4.プロトタイプチェーン
オブジェクトはみなprototypeというプロパティを持ち、プロパティには元のオブジェクト参照先が入っている。
4.1.hasOwnProperty()で自身のプロパティのみを検索
in演算子でプロパティがあるか検索できるが、プロトタイプチェーンも含まれる。
hasOwnProperty()はプロトタイプチェーンのプロパティを含まずに自身のオブジェクト内だけ検索する。
console.log(myObj.hasOwnProperty('myname')); //true
console.log( 'myname' in myObj ); //true
5.コンストラクタ
5.1.コンストラクタ関数はオブジェクトインスタンスを返す
コンストラクタ用の関数は、実際は関数とは微妙に違う。
なぜなら、関数はreturnの値を返す(return文がなければfalseが返る)ものだが、「new」することで「コンストラクタの関数」として
「オブジェクト」が返るようになる。
そのオブジェクトのことを「インスタンス」と呼んでいる。
コンストラクタ関数をnewを使って呼ぶと、thisはコンストラクタ関数で生成された新たなオブジェクトを指す。
new使わずに呼ぶとthisはコンストラクタ関数を内包する親オブジェクトになる。
5.1.1.コンストラクタの書き方
function Dog(name, cry) {
this.name = name;
this.bark = function() {
console.log(cry);
};
}
var dog = new Dog('きなこ', 'わんわん');
console.log(dog.name); // きなこ
dog.bark(); // わんわん
※newを使わないと関数呼び出しになる
5.1.2.newの挙動
実は暗黙のルールで二行加わっている。
function Dog(name, cry) {
// var this = {};
this.name = name;
this.bark = function() {
console.log(cry);
};
// return this;
}
ちなみに、明示的に書けばnewなしでもOK
function Dog(name, cry) {
var self = {};
self.name = name;
self.bark = function() {
console.log(cry);
};
return self;
}
上と同じで、下の書き方も出来る。
function Dog(name, cry) {
return {
name: name,
bark: function() {
console.log(cry);
}
}
5.1.3.コンストラクタ呼び出しの仕様
newすると
1.空のオブジェクトが返る
2.thisは返されたオブジェクト自身になる
3.関数内に戻り値がない場合は、そのオブジェクト自身が返る
※コンストラクタ用関数の名前は先頭大文字にするのが通例。
5.2.ネイティブ、ビルトインオブジェクトのコンストラクタ
jsのネイティブオブジェクト(ビルトインオブジェクトともいい、最初から用意されているものという意味)は9つある。
※String、Number、Booleanはnewしなければプリミティブ値を返す
Number()
String()
Boolean()
Object()
Array()
Function()
Date()
RegExp()
Error()
※Mathオブジェクトは静的オブジェクトなのでnewして使えない代わりにそのまま、Math.max()などのメソッドが使える
※文字列、数値、真偽値をリテラルで記述した場合には裏でコンストラクタが実行されている
5.3.コンストラクタを使わず「リテラル」を使って値を生成できる
コンストラクタをnewで呼ばずに実際はリテラル(ショートカットな書き方)で書く。
しかし、リテラルというのはただ内部でコンストラクタを呼んでくれているだけなので、newしているのと全く変わらない。
※文字列、数値、真偽値はプリミティブ値として生成されるので、オブジェクトのように扱われた時にラッパーオブジェクトを生成し、
メソッドを呼び終わった時点でオブジェクトを破棄して、プリミティブ型に戻っている。
myNum = 30;
myStr = 'sam';
myArr = ['sam', 30];
5.4.コンストラクタの引数省略
JavaScriptでは、関数呼び出し時に引数が省略できます。コンストラクタも関数であるため、コンストラクタ引数も省略できます。
つまり、引数が省略されてコンストラクタが呼び出されてインスタンスが作成されると、プロパティが未定義(undefined)のままになってしまい、予期せぬエラーが発生する可能性がでてきてしまいます。
そのため、対処法として、
プロパティを特定の値で初期化する
エラーを発生させプログラムを中断する
という方法が考えられます。
var Person = function(name, age) {
// 特定の値で初期化する
this.name = name || 'No name';
this.age = age || 20;
}
// コンストラクタ引数を指定しないと「特定の値」で初期化される
var satoshi = new Person();
console.log(satoshi.name); // => 'No name'
console.log(satoshi.age); // => 20
// コンストラクタ引数を渡すと「渡した値」で初期化される
var satoshi = new Person('サトシ', 28);
console.log(satoshi.name); // => 'サトシ'
console.log(satoshi.age); // => 28
エラーを発生させる場合
var Person = function(name, age) {
// 引数をチェックする
if (name == undefined) { throw new Error("引数'name'を指定してください。"); }
if (age == undefined) { throw new Error("引数'age'を指定してください。"); }
this.name = name;
this.age = age;
}
// コンストラクタ引数を指定しないとエラーが発生する
var satoshi = new Person(); // => Error: 引数'name'を指定してください。
6.スコープ
スコープというのは変数や関数などを呼び出せる「範囲」のこと。
jsのスコープには3種類ある。
・グローバルスコープ
・ローカルスコープ
・evalスコープ
6.1.jsはブロックスコープがない
javaやphpなどと違い、jsはif文やfor文などのブロック({})内だけのスコープ(ブロックスコープという)は持たない。
6.2.varをつけないとグローバルスコープになる
6.3.スコープチェーン
階層になっている場合、呼び出し階層から上の階層へ辿って呼び出す変数やプロパティを探す。
6.3.1.callオブジェクト
関数を呼び出すたびにcallオブジェクトが生成される。
callオブジェクトにはその関数内で定義された変数を管理している。
変数解決はこのcallオブジェクトの中の変数を見ている。
なければ、さらに上のcallオブジェクトを見る。
6.4.スコープは関数定義時に決まる
7.this
thisはオブジェクトの参照先を保持している。
thisのいいところは、オブジェクト志向だと自身がオブジェクトごとに違うため、thisを使うことでわざわざインスタンスごとにメソッドを作り直す必要がない!
var pamu = {
name : 'ぱみゅぱみゅ',
sayName : function(){ return this.name; }
};
console.log(pamu.sayName()); //ぱみゅぱみゅ
// この場合、thisはpamuオブジェクトを指すので、this.nameはpamuオブジェクト内のnameプロパティを取得している。
7.1.thisの値
thisの値は関数実行時のコンテクストによって決まる。
var name = 'ぱみゅぱみゅ';
var mogaObj = { name : 'もがみもが' };
var sayName = function(){
console.log(this.name);
}
mogaObj.sayName = sayName;
mogaObj.sayName(); // もがみもが | mogaObj.sayName()内にあるthisがある関数を呼び出しているのはmogaObjオブジェクトなので、そのnameである'もがみもが'が表示される
sayName(); // ぱみゅぱみゅ | sayName()を呼び出しているのはグローバルオブジェクトのwindowオブジェクトなので、そのnameプロパティである'ぱみゅぱみゅ'が表示される
7.2.プロトタイプメソッド内のthisはインスタンスを参照する
var Human = function(str){ this.name = str; };
Human.prototype.sayName = function(){
console.log(this.name); //thisはHumanオブジェクトではなく、生成されたインスタンスを指す
}
var pamu = new Human('ぱみゅぱみゅ'); //ぱみゅぱみゅ
var moga = new Human('もがみもが'); //もがみもが
7.3.call()やapply()でthisの値を変えられる
7.4.コンストラクタ関数内でthisを使う
この場合のthisの値は新しく生成されるインスタンスオブジェクトを指す。
8.null
変数や関数など定義されているが、中身は今のところ「未定」の場合に「中身は空だ」と明示するために使うもの。
''(空文字)を入れるやり方もあるが、厳密には空文字という文字が入っているということになるので、nullを使った方がいいが、現場や人によってどちらを使うかは様々。
var pamu = null;
console.log(pamu); //null
console.log(typeof pamu); // object | object型となるため、使いものにならない
console.log(pamu == null); // true
console.log(pamu == undefined); // true //==演算子はundefinedとnullの区別がつかない
console.log(pamu === null); // true
console.log(pamu === undefined); // false | ===演算子なら、きちんとundefinedはfalseになる
9.undefined
値を代入せずに宣言した変数を呼び出した場合や、オブジェクトのプロパティがない場合にjs側からundefinedと返ってくる。
変数やプロパティが呼び出した時点で「利用不可能」ということを示すのがundefinedというもの。
自分でundefinedを代入せず、js側が使うだけにしておくこと。
var pamu; // 値を入れずに定義
console.log(pamu); //undefined
console.log(typeof pamu) //undefined | undefined型
var moga = {};
console.log(moga.name); //undefined
console.log(typeof moga.name) //undefined型
var moga = undefined; //undefinedを入れたりしないこと!使うけど未定なだけなら、nullや空文字を入れておくこと!
10.クロージャ
関数の中に関数があり、その関数内で上の関数のローカル変数を使っているもの。
変数というのは参照されなくなったら破棄される。
ローカル変数も関数が使い終わったら破棄される。
でも、クロージャは関数を返す。
関数はグローバルオブジェクト内にあれば、ずっと参照されているので破棄されない。
さらにその関数内でローカル変数を使っているなら、そのローカル変数も破棄されない。
だから、関数を呼び出すたびにローカル変数をインクリメントできる。
もっというと、callオブジェクトも参照されたままなので破棄されず、その中に入っている変数も破棄されないので、残っているということ。
10.1.クロージャを使ったボタンのオブジェクトを保持する書き方
1.ボタンオブジェクトを作成
2.プロパティclickをセット
3.メソッドclickをセットし、clickプロパティをtrue、falseにする
4.イベントハンドラにclickイベントをセットし、イベント時のアクションにボタンオブジェクトのclickメソッドをセットすればOK
11.プロトタイプ
prototypeはインスタンス生成後でもいい
インスタンス生成後にメソッド追加しても、生成したインスタンスでも使えるようになる。
なぜなら、コンストラクタのprototypeに追加し、インスタンスのprototypeはコンストラクタを参照しているので。
メソッドは必ずプロトタイプで管理
プロトタイプ定義はリテラルで書く
obj.prototype = {
func1 : function(){ },
func2 : function(){ }
}
11.1.プロトタイプチェーン
継承のように使えるもの。
プロトタイプの中に継承したいインスタンスを入れてしまう。
var Human = function(name){ this.name = name; };
Human.prototype = { sayName : function(){ console.log(name); } };
var Pamu = function(){};
Pamu.prototype = new Human();
var pamupamu = new Pamu();
pamupamu.sayName();
12.jsの注意点
use strictを使おう
"use strict";をコード内に記述すると、そのコードは「strictモード」で実行されるようになる。
「strict」モードとは、より的確なエラーチェックが行われるようになり、コード内に存在する潜在的なバグや非推奨のスタイルを事前に排除しようというメリットがある。
ECMAScript Ver.5 より使用できるようになった。JavaScriptのテストツールで有名なJSLintやESLintも"use strict";の使用を推奨している。
要は、 「ミスをエラーに」 ということ。
エラーとなる文法には以下の様なもの。
・宣言されていない変数への代入が(グローバル変数の自動生成ではなく)構文エラーに
・書き込み不可・削除不可の変数やプロパティへの操作が(何も起きないのではなく)構文エラーに
・プロパティ名の重複時に(上書きされるのではなく)構文エラーに
・with文の使用は構文エラーに
・delete nameを構文エラーに
・evalやargumentsというキーワードに対するバインドや代入は構文エラーに
12.1.名前空間
プログラムが膨大になってくるとグローバルスコープで定義した変数や関数がバッティングする危険がある。
もしバッティングしても、上書きされるだけでエラーが出ないので原因特定がとても大変。
なので、パソコンのファイルの仕組みみたいに名前を区切って変数や関数を指定してその空間を作ってあげる。
12.1.1.名前空間の作り方
var jp;
if(!jp){ //変数jpが定義されてなければ、空オブジェクト作成
jp = {};
}
if(!jp.sample){ //変数jp.sampleが定義されてなければ、空オブジェクト作成
jp.sample = {};
}
jp.sample.ErrMsg = function(msg){
this.msg = msg;
}
var errMsg1 = new jp.sample.ErrMsg('エラーです'); //new ErrMsg はダメ
alert(errMsg1.msg);
12.1.2.グローバル汚染を防ぐ
方法1:名前空間を利用する
//唯一のグローバルオブジェクトを作る
var glob = {};
//そのオブジェクトに変数や関数を定義してやる
glob.name = "なまえ";
方法2:クロージャを使う
変数のスコープは関数内なので、関数内に変数定義すれば、全てローカル変数になる。
(function(){
glob = "グローバル"; // varを忘れるとグローバルになるので注意!!
var local = "ろーかる";
})();
12.2.argumentsで関数の引数チェック
jsでは関数で定義した引数の数より少なくも多くも渡せてしまうので、
関数内でarguments.lengthでチェックして例外出してやる。
関数の引数の型をチェック
jsの関数は型関係なく引数に渡せるので、型チェックを行うこと!
引数が多い場合には引数に名前をつけて渡す
function func(args){
var name = args.name;
var age = args.age;
}
func({ name : 'pamupamu', age : 70 });
12.3.foreachのようなものがない
for-inはあるが、キーを取り出すだけ。
キーを元にfor-in内で自分で取り出さないといけない。
そのため、underscore.jsを使ったほうがいい。
ES6ではfor-of文というのが新しく出来て、値が取り出せるようになった!
12.4.ファイルサイズを出来るだけ小さく!
jsはブラウザで読み込まれるので、出来るだけファイルサイズを小さくすることが求められる。
ファイル数は気にしなくていい。
型判定 (instanceof 演算子と isPrototypeOfメソッド)
オブジェクトの型をconstructorプロパティで判定するのは1つの方法
他にも方法がある = instanceof
これ演算子 (===, !== みたいなやつと同じ使い方)
左辺にオブジェクト、右辺にコンストラクタを指定する
オブジェクトが右辺のコンストラクタで生成されている場合は演算結果が真になる
プロトタイプチェーンで拡張継承相当の度差をしている場合もinstanceof演算で判定可能
下の例だとdはDateのインスタンスだよね?って聞いてる感じ
var d = new Date();
console.log(d instanceof Date);
console.log(d instanceof Object);
function Derived() {}
function Base() {}
Derived.prototype = new Base();
var obj = new Derived();
console.log(obj instanceof Derived);
console.log(obj instanceof Base);
console.log(obj instanceof Object);
プロトタイプオブジェクトの確認にはObjectクラスのisPrototypeOfメソッドも使える
isPrototypeOfメソッドはプロトタイプチェーンをたどる
下の例だとobjからプロトタイプをたどってる感じ
console.log(Derived.prototype.isPrototypeOf(obj));
console.log(Base.prototype.isPrototypeOf(obj));
console.log(Object.prototype.isPrototypeOf(obj));
型判定 (ダックタイピング)
JSは動的にプロパティが変わることがよくある
var obj = {};
obj.doit = function() {
console.log('Do it');
}
var obj = {
doit: function() {
console.log("Yo! Do it boy! Who's you Daddy!?");
}
}
obj.doit();
オブジェクトのメソッドの存在はconstructorプロパティやinstanceof演算では判定できない
オブジェクトにどんなプロパティがあるかを判定する方法が汎用的
直接プロパティを見てオブジェクトの振る舞いを判定する方法をダックタイピングと言う
ダックタイピングに使える1つの方法がin
左辺にプロパティ名の文字列、右辺にオブジェクト参照を指定
オブジェクトが指定プロパティを持つ場合真を返す
var obj = {doit: function() { console.log('Do it!'); }}
console.log('doit' in obj);
console.log('toString' in obj);
for文を早くする
配列など長さでforする時には下記のように長さをキャッシュしておく。(ループの度に長さを取得する処理が入ってしまうので)
for ( var i = 0, len = myarray.length; i < len; i++){
console.log(i);
}
色々な監視
Ajax通信している最中には以下4つのイベントを監視できる。
progress //データを受信しているとき
load //要求が完了して結果が返ってきたとき
error //要求に失敗したとき
abort //要求が中断されて失敗したとき
受信進捗状況を表示する方法
Ajaxオブジェクトのprogressプロパティを使って、データ受信があとどれくらいで終わるか計算できる。
ajax.onprogress = function(e){
if(typeof e == 'undefined') e = window.event;
var pct = e.loaded/e.total;
}
//loadプロパティには現在受信完了済みデータ合計バイト数が入っている
//totalプロパティにはデータ全体のバイト数が入っている
名前の付け方
イベントが何の仕事をするのかイメージしやすい名前をつける
onSubmitForm(),onMouseOverImage() などなど
イベントハンドラがあるかどうかは下記のように調べられる
if(typeof window.onlodad == 'function'){ //... }
デフォルトイベントのキャンセル
addEventListener()メソッドをサポートしているブラウザでは、preventDefault()を使う。
if(typeof e == 'undefined') e = window.event;
if(e.preventDefault){
e.preventDefault();
} else {
e.returnValue = false;
}
return false; //万一のため最後にもfalseを返す
歯抜けが許される
jsの配列は値をまばらに入れることができる。
var arr = [1,,3,,5]; //配列内は、「1,undefined,3,undefined,5」という内容になる
delete arr[3]; //配列内は、「1,undefined,undefined,undefined,5」という内容になる
+=での文字連結よりjoin
+=での文字連結は連結のたびにStringを作成するので効率が悪い。
文字連結にはStringの配列を作って、最終的にjoinを使って連結すること。
//パフォーマンスが悪い例
var str = 'あいう';
str += 'えお';
str += 'かきくけこ';
//パフォーマンスの良い例
var str = ['あいう','えお','かきくけこ'];
var str2 = str.join(); // 「あいう,えお,かきくけこ」という文字列になる
※join()は引数に何も指定しないと「,」でくっつけてくれる
Date型について
・クライアントマシンの日付と時刻が利用される
配列について
インデックスを指定しないと新たに配列への追加は出来ない。
var arr = [1,2,3];
arr[] = 4; //NG
arr[3] = 4; //OK
arr[arr.length] = 4; //OK
条件式による代入
var x = y || 1
左のyを評価し、trueならその中の値が返される。
この場合、yの中身を判定し、中身が入っていれば(かつ、0とかじゃなければ)trueなので、中身の値が変数xへ代入される。
そうでない場合は、1が代入される。
||の場合、最初の値がtrueならそれが返される。
&&の場合、最初の値がfalseならそれが返される。
FALSEなもの
・false
・NaN
・0
・null
・空の文字列(""や'')
・undefined
上記以外はTRUE
TRUEになる条件式
・null == undefined
・null !== undefined
・1 == true
・1 !+== true
※判定は必ず型まで判定する「===」や「!==」を使うこと。
※&&や||などの論理演算の場合、左から判定され、最初に判定にマッチすれば後続の判定は行われない