JavaScript

Javascriptの関数、変数について

More than 2 years have passed since last update.

概要

Javascriptの関数と変数の基礎をおさらいした内容になります。
scriptの実行確認にchromeのdeveloper toolsのコンソールを使用しました。(一部はNode.jsのreplを使用)

参考

1. 関数 (function)

関数宣言 (function declaration)

関数宣言ではfunctionキーワードで関数を定義します。
宣言型の関数はスクリプトがロードされるタイミングで構文解析されます。

declaration
function double(x) {
  return x * 2;
}

double(2);
// ⇒ 4
double;
// ⇒ function double(x) { return x * 2;}
double.name;
// ⇒ "double"
typeof double;
// ⇒ "function"

関数式 (function expression)

関数式では関数コンストラクタまたは関数リテラル(function literal)で関数を定義します。
関数式は式が評価されたタイミングで構文解析され変数へ代入されます。

関数コンストラクタ

関数コンストラクタはFunction()を使用して関数を定義しますが、この方法はあまり使用されません。
関数コンストラクタは無名(匿名)関数 (anonymous function)を生成します。

var f = new Function("x", "return x * 2;");

f(2);
// ⇒ 4
f;
// ⇒ function anonymous(x
//    /**/) {
//    return x * 2;
//    }
f.name;
// ⇒ ""
typeof f;
// ⇒ "function"

無名(匿名)関数式 (anonymous function expression)

関数リテラルは関数を定義する関数式です。

expression
var f = function(x) {
  return x * 2;
};

f(2);
// ⇒ 4
f;
// ⇒ function(x) {
//      return x * 2;
//    }
f.name;
// ⇒ ""
typeof f;
// ⇒ "function"

名前付き関数式 (named function expression)

関数名を付けることもできます。

expression
var f = function double(x) {
  return x * 2;
};

f(2);
// ⇒ 4
f;
// ⇒ function double(x) { return x * 2;}
f.name;
// ⇒ "double"
typeof f;
// ⇒ "function"

double(2);
// ⇒ Uncaught ReferenceError: double is not defined(...)

コールバック関数 (callback function)

ある文脈で使用される関数式は、コールバック関数といいます。

callback
var a = [1,2,3];
a.forEach(function(element, index, array){
  console.log(element * 10);
});
// ⇒ 10
// ⇒ 20
// ⇒ 30

クロージャ (closure)

入れ子型の関数をクロージャといいます。下記の例ではup関数がクロージャになります。
クロージャは外側の関数で定義された変数にアクセスすることができます。

closure
function counter() {
  var count = 0;
  return function up() {
    return ++count;
  }
}

var c1 = counter();
c1.name:
// ⇒ "up"
c1();
// ⇒ 1
c1();
// ⇒ 2
c1();
// ⇒ 3

var c2 = counter();
c2.name;
// ⇒ "up"
c2();
// ⇒ 1
c2();
// ⇒ 2
c2();
// ⇒ 3

アロー関数式 (arrow function)

ファットアロー関数 (fat arrow function)ともいいます。
この記法はECMAScript6の仕様です。Chrome V46で動作しました。

arrow
((x,y) => {return x + y;})(2,4);
// ⇒ 6

引数が1つの場合は、引数を囲む()を省略することができます。

arrow
(x => {return x * 2;})(10);
// ⇒ 20

さらに式が1つしかない場合は{}を省略することができます。

arrow
(x => x * 2)(10);
// ⇒ 20

アロー関数式は無名(匿名)関数なので、関数名を付けることはできません。

SyntaxError
(double(x) => {
  return x * 2;
})(10);
// ⇒ Uncaught SyntaxError: Unexpected token ((...)

コンストラクタ関数 (constructor function)

オブジェクトを初期化する関数をコンストラクタ関数といい、new演算子とともに使用します。
new演算子はオブジェクトの生成を行い、その次にコンストラクタ関数を呼び出してオブジェクトを初期化します。

constructor
function Double(x) {
  this.x = x * 2;
}

var d = new Double(10);
d.x;
// ⇒ 20

d;
// ⇒ Double {x: 20}
typeof d;
// ⇒ "object"

new演算子を使わず関数を呼び出した場合は単に関数の実行となります。
この例の関数ではreturn文が存在しないので戻り値はUndefinedになります。

var e = Double(10);

e;
// ⇒ Undefined

e.x;
// ⇒ Uncaught TypeError: Cannot read property 'x' of undefined(...)

関数として実行すると関数内のthisはグローバルオブジェクトを指し(ブラウザの場合はwindow)、xはそのプロパティとなります。

Double(10);

window.x;
// ⇒ 20

メソッド (method)

オブジェクトのプロパティに格納する関数をメソッドといいます。
メソッドとして実行される関数内のthisはメソッドを呼び出すときのオブジェクトを参照します。

下記の関数のthis.hiはグローバル変数のhiを参照しています。

method
hi = "Hello";    //global変数
function say(m) {
  return this.hi + " " +  m;
}

say("World");
// ⇒ Hello World

上記の関数をオブジェクトのメソッドとするとthisはobjを参照し、this.hiはobjのプロパティhiを指します。

method
var obj = {
  hi: "ハロー",
}
obj.message = say;    //say関数をオブジェクトのメソッドとする

obj.message("ワールド");
// ⇒ ハロー ワールド

関数呼び出し演算子 (function call operator)

()を関数呼び出し演算子といいます。この演算子の前には関数オブジェクトを参照する変数(関数名)か関数式を記述します。

call
function say(m) {
  console.log("Hello " + m);
}

var hi = say;  //say関数オブジェクトを参照する変数hiを定義

hi("World");   //変数hiが指す関数を呼び出す
// ⇒ Hello World

2重の()演算子 (double brackets)

英語では"Double brackets"、"Double parentheses"、"Two sets of parentheses"などと表記されているのを見ました。

function say() {
  return function(x) {
    console.log("Hello " + x);
  };
}

var hi = say();
hi("World");
// ⇒ Hello World

上記の関数呼び出しは下記のように記述することができます。

say()("World");
// ⇒ Hello World

即時関数式 (IIFE / Immediately invoked function expression)

関数式を()で囲み、関数呼び出し演算子の()を付けることで名前空間に関数名を追加せずに実行することできます。
これを即時関数式といいます。

iife
(function() {
  console.log("IIFE");
})();
// ⇒ IIFE

下記のようにも記述できます。

iife
(function() {
  console.log("IIFE");
}());

即時関数式の結果を受け取ることもできます。

iife
var r = (function(x) {
  return x * 2;
})(10);
console.log(r);
// ⇒ 20

上記のようにfunctionキーワードが文の先頭にない場合は、関数式を()で囲まなくても実行することができます。

iife
var r = function(x) {
  return x * 2;
}(10);
console.log(r);
// ⇒ 20

構文上、関数名を付けることも可能です。

var r = (function double(x){
  return x * 2;
})(10);
console.log(r);
// ⇒ 20

2. 変数 (variable)

スコープ (scope)

グローバルとローカルの2種類のスコープがあります。
関数外で宣言された変数はグローバルスコープに配置されプログラム全体からアクセスできます。

関数内で宣言された変数はその関数内でアクセスできるローカルスコープに配置されます。関数の引数もローカルスコープに属します。
ただしvarキーワードで宣言しなかった変数は関数内であってもグローバルスコープに配置されます。

scope
var f = function (){
  g = "foo";
  var l = "bar";
  console.log("global:" + g + " local:" + l);
}
f();
// ⇒ global:foo local:bar
console.log(g);
// ⇒ foo
console.log(l);
// ⇒ Uncaught ReferenceError: l is not defined(…)

ECMAScript6よりブロックレベルのスコープを宣言することができます。
ブロックスコープの変数を宣言するにはletキーワードを使用します。

(function(){
  "use strict";
  let i = 0;
  {
    let i = 1;
    console.log("inner i: " + i);
  }
  console.log("outer i: " + i);
})();
// ⇒ inner i: 1
// ⇒ outer i: 0

宣言 (declared)

変数を宣言するにはvarキーワードを使用します。

オブジェクト (Object)

オブジェクトを定義するオブジェクトリテラル (object literal)。
オブジェクトイニシャライザともいいます。

var obj = {};
// ⇒ Object {}

Object()はオブジェクトを生成するコンストラクタ関数です。

var obj = new Object();
// ⇒ Object {}
独自のオブジェクト

独自のオブジェクトを定義するオブジェクトリテラル。

var car = {
  make: "Mustang",
  model: "Convertible",
  manufacturer: "Ford"
};
// ⇒ Object {make: "Mustang", model: "Convertible", manufacturer: "Ford"}

Car()は独自のCarオブジェクトを生成するコンストラクタ関数です。

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

var car = new Car("Mustang", "Convertible", "Ford");
// ⇒ Car {make: "Mustang", model: "Convertible", manufacturer: "Ford"}

文字列 (String)

プリミティブな文字列を定義する文字列リテラル (string literal)。

var s = "javascript";
s;
// ⇒ "javascript"
typeof s;
// ⇒ "string"

new演算子を伴わないString()はプリミティブな文字列を生成します。

var s = String("javascript");
s;
// ⇒ "javascript"
typeof s;
// ⇒ "string"

new演算子を伴うString()は文字列オブジェクトを生成するコンストラクタ関数です。

var s = new String("javascript");
s;
// ⇒ String {0: "j", 1: "a", 2: "v", 3: "a", 4: "s", 5: "c", 6: "r", 7: "i", 8: "p", 9: "t", length: 10, [[PrimitiveValue]]: "javascript"}
typeof s;
// ⇒ "object"
エスケープシーケンス

エスケープシーケンスを文字列リテラルで表現するには\と特定の文字を組み合わせます。

es description
\t タブ
\n 改行
\" ダブルクォート
\' シングルクォート
\ バックスラッシュ

数値 (Number)

プリミティブな整数を定義する整数リテラル。

var n = 100;
n;
// ⇒ 100
typeof n;
// ⇒ "number"

プリミティブな浮動小数点を定義する浮動小数点リテラル。

var f = 3.14;
f;
// ⇒ 3.14
typeof f;
// ⇒ "number"

new演算子を伴わないNumber()はプリミティブな数値を生成します。

var n = Number(100);
n;
// ⇒ 100
typeof n;
// ⇒ "number"

new演算子を伴うNumber()は数値オブジェクトを生成するコンストラクタ関数です。

var n = new Number(100);
n;
// ⇒ Number {[[PrimitiveValue]]: 100}
typeof n;
// ⇒ "object"

真偽値 (Boolean)

プリミティブな真偽値を定義する真偽値リテラル。

var b = true;
b;
// ⇒ true
typeof b;
// ⇒ "boolean"

new演算子を伴わないBoolean()はプリミティブな真偽値を生成します。

var b = Boolean(true);
b;
// ⇒ true
typeof b;
// ⇒ "boolean"

new演算子を伴うBoolean()は真偽値オブジェクトを生成するコンストラクタ関数です。

var b = new Boolean(true);
b;
// ⇒ Boolean {[[PrimitiveValue]]: true}
typeof b;
// ⇒ "object"

配列 (Array)

配列を定義する配列リテラル (array literal)。

var a = [];
a;
// ⇒ []
typeof a;
// ⇒ "object"
a instanceof Array;
// ⇒ true
var a = ["a","b","c"];
a;
// ⇒ ["a","b","c"]

Array()は配列を生成するコンストラクタ関数です。

var a = new Array();
a;
// ⇒ []
typeof a;
// ⇒ "object"
a instanceof Array;
// ⇒ true
var a = new Array("a","b","c");
// ⇒ ["a","b","c"]
var a = new Array(1,2,3,4);
// ⇒ [1, 2, 3, 4]

下記のように引数に数値を1つだけ指定すると、その要素数を持つ配列が生成されます。

var a = new Array(5);
// ⇒ undefined * 5
a.length;
// ⇒ 5
a[0] ? "truthy" : "falsy";
// ⇒ "falsy"

配列かどうかの判定にはArray.isArray()を使用することができます。

var a = [1,2,3];
Array.isArray(a);
// ⇒ true

日付 (Date)

日付を定義する日付リテラルはありません。

Date()は日付を生成するコンストラクタ関数です。

var d = new Date();
d;
// ⇒ Sat Nov 21 2015 18:26:40 GMT+0900 (東京 (標準時))
typeof d;
// ⇒ "object"
d instanceof Date;
// ⇒ true

正規表現 (Regular Expression)

正規表現を定義する正規表現リテラル (regular expression literal)。
文字列リテラルをスラッシュで囲むことで正規表現リテラルを表現します。

var r = /javascript/i;
r;
// ⇒ /javascript/i
typeof r;
// ⇒ "object"
r instanceof RegExp
// ⇒ true

RegExp()は正規表現を生成するコンストラクタ関数です。

var r = new RegExp("javascript","i");
r;
// ⇒ /javascript/i
typeof r;
// ⇒ "object"
r instanceof RegExp
// ⇒ true 

正規表現リテラルに\が含まれる場合はエスケープする必要があります。

var str = "java\tscript";
var reg = new RegExp("java\\tscript");
reg.exec(str);
// ⇒ ["java   script"]

関数 (Function)

関数も他のデータ型と同じように変数を定義することができます。

関数を定義する関数リテラル (function literal)

var f = function(x) {
  return x * 2;
}

Function()は関数を生成するコンストラクタ関数です。

var f = new Function("x", "return x * 2;");

巻き上げ (hoisting)

巻き上げとは関数内の変数の宣言が関数ブロックの先頭へ移されることをいいます。
Javascriptにはブロックスコープは無いため(ECMAScript6ではブロックスコープはあります)、関数内で宣言された変数は関数ブロックの先頭で宣言された事と同じになります。
なお、変数の宣言と初期化を同時に行っている場合は宣言のみが移動し初期化はその場所のままになります。

変数

1つ目のconsole.logは"Undefined"と出力し、2つ目のconsole.logは"Hello World"と表示します。
1つ目の出力が"Undefined"となるのはmが宣言される前だからではなく、初期化されていないためです。

(function(){
  console.log(m);
  // ⇒ Undefined
  var m = "Hello World";
  console.log(m);
  // ⇒ "Hello World"
})();

試しに変数mの宣言をコメントアウトして実行すると、エラーが発生します。

(function(){
  console.log(m);
  // ⇒ Uncaught ReferenceError: m is not defined(…)
  /*var m = "Hello World";*/
  console.log(m);
})();

実際に実行されるコードは下記のようなものと思います。(確認手段を知らないため推測レベルです。)

(function(){
  var m;
  console.log(m);
  // ⇒ Undefined
  m = "Hello World";
  console.log(m);
  // ⇒ "Hello World"
})();

関数

関数も他のデータ型の変数宣言と同様に巻き上げが行われます。

関数式の場合

(function() {
  console.log(double);
  // ⇒ Undefined
  var x = double(10);
  // ⇒ Uncaught TypeError: double is not a function(...)
  console.log(x);
  var double = function(x) {
    return x * 2;
  }
})();

しかし、関数宣言の場合は関数名と関数本体が巻き上げられるのでエラーは発生しません。

(function() {
  console.log(double);
  // ⇒ function double(x) {
  //      return x * 2;
  //    }
  var x = double(10);
  console.log(x);
  // ⇒ 20
  function double(x) {
    return x * 2;
  }
})();

グローバル変数

グローバル変数とはグローバルオブジェクトがバインドされる変数のとこです。実行環境によって名前が異なります。
Javascriptの標準組み込みオブジェクトのJSONがプロパティに存在するかどうかで確認しました。

ブラウザの場合

  • window
  • this (グローバルスコープの場合)
chrome
window.JSON.parse('{"foot":"bar"}');

chrome.png

IE
window.JSON.parse('{"foo":"bar"}');

ie.png

Node.jsの場合

Node.jsのv4.2.2で確認しました。

  • global
  • GLOBAL
  • root
  • this (グローバルスコープの場合)
Node.js
global.JSON.parse('{"foo","bar"}');
// { foo: 'bar' }

Truthy & Falsy

Truthy、Falsy(Falsey)とは真偽値が期待される評価(if文など)でboolean値として振舞う値のことです。

Truthy - Glossary | MDN
Falsy - Glossary | MDN

Truthy

  • true
true ? "truthy" : "falsy"
// ⇒ "truthy"
1 ? "truthy" : "falsy"
// ⇒ "truthy"
-1 ? "truthy" : "falsy"
// ⇒ "truthy"
"not empty" ? "truthy" : "falsy"
// ⇒ "truthy"

Falsy

  • false
  • 0
  • ""
  • null
  • undefined
  • NaN
false ? "truthy" : "falsy"
// ⇒ "falsy"
0 ? "truthy" : "falsy"
// ⇒ "falsy"
"" ? "truthy" : "falsy"
// ⇒ "falsy"
null ? "truthy" : "falsy"
// ⇒ "falsy"
undefined ? "truthy" : "falsy"
// ⇒ "falsy"
NaN ? "truthy" : "falsy"
// ⇒ "falsy"

短絡評価 (Short Circuit Evaluation)

下記のような論理式(&&||)を用いた評価を短絡評価といいます。

var a = "A";
var b = "";

console.log(a && b);
// ⇒ ""
console.log(a || b);
// ⇒ A
var a = "";
var b = "B";

console.log(a && b);
// ⇒ ""
console.log(a || b);
// ⇒ B
var a = "A";
var b = "B";

console.log(a && b);
// ⇒ B
console.log(a || b);
// ⇒ A

メモ

変数型のラッパーオブジェクト

下記のラッパーオブジェクトは、"JavaScript: The Good Parts"の"付録B.悪いパーツ"で利用を避けるべきとされている。

  • new Boolean
  • new Number
  • new String
  • new Object
  • new Array