JavaScript

Javascriptの関数など

オブジェクトとは?

JavaScriptのオブジェクトとは、名前をキーにアクセスできる配列― 要は、連想配列(ハッシュ)であると説明しました。
これは、JavaScript のオブジェクトを実装的な視点からとらえる といった意味では正しいのですが、オブジェクトという概念そのものを説明するには不足です。
JavaScript では構文的には連想配列もオブジェクトも同じ名前付きの配列。しかし、 言葉として使い分ける場合、それぞれの意味合いはまったく違うもの
スクリーンショット 2017-08-22 17.50.40.png

オブジェクトはプロパティとメソッドとから構成されます。

関数

function命令で定義する

javascript
function getTriangle(base, height) {
    return base * height / 2;
}

Functionコンストラクター経由で定義する

javascript
var getTriangle = new Function('base', 'height', 'return base * height / 2;');

それは、「Function コンストラクターでは、引数や関数本体を文字列として定義できる」という点です。
つまり、Function コンストラクターを利用すれば、以下のようなコードを記述することも可能です。
ここでは単純化のため、変数 param / formula をそれぞれ固定値で指定していますが、
スクリプト上で文字列を生成して、引数/関数本体を動的に生成することもできます。
Functionコンストラクターは、実行時に呼び出されるたびに、コードの解析から関数オブジェクトの生成までを行うため、実行パフォーマンス低下の一因となる可能性がある

javascript
var param = 'height, width';
var formula = 'return height * width / 2;';
var diamond = new Function(param, formula); console.log('菱形の面積:' + diamond(5, 2));

関数リテラル表現で定義する

javascript
var getTriangle = function(base, height) {
    return base * height / 2;
};

アロー関数で定義する

javascript
var getTriangle = (base, height) => {
    return base * height / 2;
};

引数が1個の場合には、引数をくくるカッコも省略できます。

javascript
var double = val => val*2

引数がない場合は、カッコを省略することはできません。

javascript
var show = () => console.log('こんにちは、世界! ');

関数の注意点(読み込まれるタイミングが違う)

動作する
「functon 命令はコードを解析/コンパイルするタイミングで、関数を登録している」ということです。

javascript
console.log('三角形の面積:' + getTriangle(5, 2));
function getTriangle(base, height) {
    return base * height / 2;
}

動作しない
関数リテラル/ Function コンストラクターは実行時(代入時)に評価される

javascript
console.log('三角形の面積:' + getTriangle(5, 2));
var getTriangle = function(base, height) {
    return base * height / 2;
};

関数の注意点(変数の巻き上げ)

変数の巻き上げのため、ローカル変数は関数の先頭で宣言する
理屈を説明すると、まずJavaScriptではローカル変数は「関数全体で有効」なので、
1の時点ではすでにローカル変数scopeが有効になっています。
しかし、1の時点ではまだローカル変数が確保されているだけで、var命令は実行されていません。
つまり、ローカル変数scopeの中身は未定義(undefined)であるというわけです。

javascript
var scope = 'Global Variable';
function getValue() {
  console.log(scope);//1
  var scope = 'Local Variable';
  return scope;
}

console.log(getValue());
// undefined
// Local Variable

仮引数のスコープ - 基本型と参照型の違いに注意する -

javascript
var value = [1, 2, 4, 8, 16];
function deleteElement(value) {
  value.pop(); // 末尾の要素を削除
  return value;
}
console.log(deleteElement(value)); //[1, 2, 4, 8]
console.log(value); //[1, 2, 4, 8]

この辺りはrubyと同じ

ruby
val = [1, 2, 3, 4]
def test(v)
  v.pop
end
test(val) //4

val //[1, 2, 3]

ブロックレベルのスコープは存在しない(ES2015以前)

JavaScriptでは、ブロックレベルのスコープが存在せず、ブロック(ここではif ブロック)を抜けたあとも変数iが有効であり続けるためです。

javascript
if (true) {
    var i = 5;
}
console.log(i);

即時関数で変数名の競合を防ぐ

javascript
(function() {
   var i = 5;
   console.log(i);
}).call(this);
console.log(i);

ブロックスコープに対応したlet

javascript
{
  let i= 5;
  console.log(i);
}

console.log(i);

関数の引数のdefault値

javascript
function getTriangle(base = 1, height = 1) {
    return base * height / 2;
}
console.log(getTriangle(5));//2.5
getTriangle(5, null); //0
getTriangle(5, ''); //0
getTriangle(5, false); //0
getTriangle(5, undefined); //2.5

デフォルト値が適用されるのは、引数が明示的に渡されなかった場合だけです。
よって、例えばnull / false / 0 /空文字列など、
意味的に空を表すような値でも、それらが明示的に渡された場合は、default値が適用されることはありません。
ただし、undefinedだけは例外です。undefined(未定義)を引数に渡した場合には、引数は渡されなかったものと見なされ、デフォルト値が適用されます。

可変長引数の関数を定義する

javascript
function sum(...nums) {
    let result = 0;
    for (let num of nums) {
      if (typeof num !== 'number') {
        throw new Error('指定値が数値ではありません:' + num);
    }
      result += num;
    }
    return result;
}

try {
  console.log(sum(1, 3, 5, 7, 9));
} catch(e) {
  window.alert(e.message);
}

関数の引数も関数 - 高階関数、コールバック関数

「関数を引数、戻り値として扱う関数」のことを高階関数と呼びます。
showElement関数のように、呼び出し先の関数の中で呼び出される関数のことをコールバック関数といいます。
あとで呼び出される(=コールバックされる)べき処理、という意味です。

```: javascript
//高階関数arrayWalk
function arrayWalk(data, f) {
for (var key in data) {
f(key, data[key]);
}
}

function showElement(key, value) {
console.log(key + ':' + value);
}

var ary = [1, 2, 4, 8, 16];
arrayWalk(ary, showElement);
```

匿名関数でも呼び出せる

javascript
//高階関数arrayWalk
function arrayWalk(data, f) {
  for (var key in data) {
    f(key, data[key]);
  }
}

var ary = [1, 2, 4, 8, 16];
arrayWalk(ary,
          function(key, value){
            console.log(key + ':' + value);
          }
          );

テンプレート文字列

javascript
let name = '鈴木';
let str = `こんにちは、${name}さん。 今日も良い天気ですね! `; console.log(str);

scope chain

javascript
var y = 'Global';
function outerFunc() {
  var y = 'Local Outer';

  function innerFunc() {
    var z = 'Local Inner';
    console.log(z);//Local Inner
    console.log(y);//Local Inner
    console.log(x);
  }
  innerFunc();
}
outerFunc();

スクリーンショット 2017-08-23 17.06.01.png

クロージャ

通常、関数の中で使われたローカル変数(ここでは変数counter)は、関数の処理が終了した時点で破棄されるはずです。
しかし、リスト4-50 のケースではclosure関数から返された「匿名関数がローカル関数counterを参照し続けている」ので、
closure関数の終了後もローカル変数counterは保持され続けるというわけなのです。

javascript
function closure(init) {
   var counter = init;
   return function() {
     return ++counter;
   }
}

var myClosure1 = closure(1);
var myClosure2 = closure(100);
console.log(myClosure1());//2
console.log(myClosure2());//101
console.log(myClosure1());//3
console.log(myClosure2());//102

スクリーンショット 2017-08-23 17.27.42.png