LoginSignup
22
27

More than 5 years have passed since last update.

JavaScriptで引っ掛かりがちな箇所を凝縮して解説する

Last updated at Posted at 2017-05-19

JavaScriptで引っ掛かりがちな箇所を凝縮して解説する

オブジェクトすなわち連想配列

JavaScriptは基本型(string, number)以外のオブジェクトは全て連想配列として扱えます。
stringString, numberNumber の違いについてコメントで指摘頂き追記しました。

array = new Array(1, 2, 3)
// ええんやで
array.a = 'A'
array.a; // -> A

function func() {}
// ええんやで
func.b = 'B';
func.b; // -> B

オブジェクトとしての関数

JavaScriptでは関数は Function 型のオブジェクトです。(まあ型の概念はないのですが)
Javaの Consumer のように変数に格納もできますし、関数の引数として渡すこともできます。(よくコールバックとして使われる)

// 以下2つは同義
function myFunc() { console.log('Hi'); }
var myFunc = function() { console.log('Hi'); };

typeof myFunc; // -> function
myFunc.constructor === Function; // -> true
myFunc.__proto__ === Function.prototype; // -> true

var array = [];
array.push(myFunc);
array[0](); // -> Hi

function executeTwice(func) {
  func();
  func();
}
executeTwice(myFunc); // -> Hi Hi

プロトタイプチェーン

全てのオブジェクトは .__proto__ というプロパティを持っており、これは .constructor.prototype と同じです。(実は昔はなかった)
この __proto__ 直下にあるプロパティは全てオブジェクトから直接アクセスできます。(コードを見たほうが早い)

var obj = new Object();
Object.prototype.something = 'something';
obj.__proto__ === Object.prototype; // -> true
obj.__proto__.something; // -> something
obj.something; // -> something
obj.constructor === Object // -> true
// オマケ
Object.constructor === Function; // -> true

// やはり直接オブジェクトがもっているプロパティが優先される
({ something: 'another' }).something; // -> another

さて

function Class() {
  this.something = 'Something';
}
// どのオブジェクトにもObject型のプロパティ prototype は必ず存在する
// よって prototype の初期化は不要
Class.prototype.hello = {
  console.log('Hello!');
};
var instance = new Class();
// これらと同義
// -> var instance = new(Class);
// -> var instance = new Class;
instance.hello(); // -> Hello!

を実行すると何が起こるのでしょう。

  1. オブジェクトが生成されます。このオブジェクトを以降 object とします。
  2. object.__proto__ = Class.prototype とする。
  3. thisobject として Class 関数が実行される。
  4. Class の返り値があればそれを、なければ object を返す。
  5. instance に代入される。

前述の __proto__ と併せて分かることは、JavaScriptはなんちゃってクラス機構をもっているということです。

グローバルスコープ

グローバルスコープの変数は window オブジェクトのプロパティとしてアクセスできます。

// グローバルスコープ
var a = 'something';
a; // -> something
window.a; // -> something
var prop = 'a';
window[prop]; // -> something
this.a; // -> something

NaNnullundefined

  • NaN - Number 型のオブジェクトとして扱われているが、数字でないことを示す。NaN を含む計算式の答えは必ず NaN になる。
  • null - 明示的に何もないことを示すときに使う。
  • undefined - プロパティが存在しない場合に定義されていないことを伝えるもの。
(Number('1') + 2); // -> 3
Number('Oops!'); // -> NaN

var array = [1, 2, 3];
array[100]; // <- undefined
delete array[0];
array[0]; // <- undefined

参照エラー

以下の場合参照エラーが発生します。

  • スコープ直下の存在しない変数を参照。
  • undefined, null の存在しないプロパティを参照。
var obj = {};
// これらは全部 undefined
obj.undef;

// これらは全部参照エラー
undef;
obj.undef.undef;
(function() { undef; })();

// グローバル変数が存在するかどうか確認したい場合
if (window.jQuery) {}

比較演算子

基本形(number, string)以外のオブジェクト"同士"は、ポインタで比較されます。つまり左右の変数が同じ場所に格納しているデータを指し示している場合のみ真を返します。

var array1 = [1, 2, 3];
var array2 = [1, 2, 3];
var obj1 = { a: 'A' };
var obj2 = { a: 'A' };
array1 == array2; // -> false
obj1 == obj2; // -> false
obj1.a == obj2.a; // -> true
obj1 == "[object Object]"; // -> true
  • a == b - 型をどちらかに統一してから比較を実行します。
  • a === b - 型をどちらかに統一してから比較を実行します。
// string は Date に変換できない
// Date は string に変換できる
new Date() == "Fri May 19 2017 23:44:07 GMT+0900 (JST)";
// つまりこれは
// (new Date()).toString() === "Fri May 19 2017 23:44:07 GMT+0900 (JST)"
// と同義

// Date と文字列の比較、つまり偽
new Date() === "Fri May 19 2017 23:44:07 GMT+0900 (JST)"; // -> false

this について

まず、3つ例外があります。

  1. グローバルスコープでの thiswindow を示す。
  2. 関数を new した場合、その関数の中の thisnew で生成したオブジェクト。
  3. 関数内の this.call(context, ...args), .apply(context, args), .bind(context)context に変更できます。

ではそれ以外はというと、非常に伝え辛いのですが
ざっくり結論: context.method() という形で関数が呼ばれた場合は context、それ以外は undefined.

var context = {
  method: function() {
    return this;
  }
};

context.method(); // <- context と同義

var something = context.method;
// ただし、これは window.something() として扱われる
something(); // <- よってこれは window と同義

下3行をご覧いただくと、関数を別の変数に入れて実行すると this の内容が変わってしまっています。
要するに context.method 関数で context を取得するために this を使うのは、思わぬ副作用に繋がるので良くないということですね。

即時関数

関数を生成して、変数に代入する間もなくその場で実行するという手法です。よく変数を隠蔽(or グローバル汚染回避)するために使われます。

(function() {
  var text = 'Cannot access me outside of this function';
  console.log(text);
})()

並行処理

並"列"処理は「Web Worker」などでggってみてください。
setTimeoutsetInterval を使うと簡単に並行処理を実装できます。

window.setTimeout(function() {
  // 処理1
}, 10); // 0 でもいいが念の為 10

//処理2

コメント欄より: リテラルと Object の違い (Thanks to @mpyw)

文字列リテラル(string)と文字列オブジェクト(String)は違うという指摘を頂きました。今まで知らなかった...。
new String(str) で文字列を生成するとオブジェクト扱いのようです。(numberNumber も同様)

"a" == "a"; // -> true 
new String("a") == new String("a"); // -> false
typeof "a" // -> string
typeof new String("a") // -> object
"a" instanceof String // -> false
new String("a") instanceof String // -> false

String型はオブジェクトになるので、参照渡しになります、が、文字列オブジェクトには破壊的メソッドが存在しないので、確かめる方法といえば前述した連想配列として使うことくらいですかね。

objStr = new String("a");
objStr2 = objStr
objStr.a = 'A';
objStr2.a; // -> A

後書き

私が初めてJavaScriptを学んだのは小学生の頃で「とほほのWWW入門」がきっかけでした。
懐かしく思って最近見に行くとまだサイトは残っているばかりか、いつの間にかナウいコンテンツ(AngularJSなど)まで拡充していますね。

22
27
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
27