トップ画像イメージ

ES6での変数の宣言

前回の記事でES6での便利だと思ったメソッドについてまとめてました。(主に配列操作系)

今回はES6での変数の宣言方法と、アロー関数についてまとめておきたいと思います。

筆者の職場でも、ES6の書き方を学習しておらず、未だに変数は全てvarで宣言しているコードが結構あります。筆者が目指す10年後も読みやすいコードを目指すためにも、製作者の意図を伝えられるように、変数一つにも意味を込められるようにします!

変数の宣言方法

varの場合

ES6以前の変数の宣言といえば、varだったと思います。

ES6では、letconstという変数の宣言が出来るようになっています。

//varの場合

function var_func(){
  var member = '飯塚';
  member = '豊本'; //変数の再代入可
  console.log(member);
  if (member === '豊本'){
  		var member = '角田'; //変数の再宣言が可能
  }
  console.log(member);//スコープが関数内で有効なので、ブロックを抜けても変数の値を保持
}
var_func()
=>飯塚
豊本
角田

varの場合、変数の変数の再代入、再宣言が可能。

また、スコープ(変数が有効な範囲)は関数内であるので、ifブロックを抜けても、ifブロック内で再代入・再宣言した変数の値もそのまま保持しますので、ifを抜けた後も、memberの値は角田となっています。

varによる変数の宣言は、変数の巻き上げと呼ばれる問題を引き起こすようです。

var name = "飯塚グローバル";
 
function func() {
    console.log(name);    
    var name = "豊本local";
    console.log(name);  
}
 
func();
=>undefined
豊本local

この現象は、こちらのサイトによると

JavaScriptでは、関数内のどこにでもvar文を使用して変数を宣言することができます。そして、これらの変数は関数内のいかなる場所で宣言されたとしても、その関数の先頭で宣言されたのと同じように動作します。

とのこと。 つまり、func()のなかで、グローバル変数のnameと同じ名前でvarによる宣言を行なっているため、func()内の先頭でnameを宣言し直したけど、値がまだ代入されていない扱いになっているようです。

下のコードと同じことを実行していると見なされます。

var name = "飯塚グローバル";
 
function func() {
    var name;
    console.log(name);    
    name = "豊本local";
    console.log(name);  
}
 
func();
=>undefined
豊本local

このように、varでは意図しない変数への再代入・再宣言が発生してしまう可能性があり、よろしくないです。

製作者の意図しない形で、変数の値が変わったり、初期化されてしまうかもしれません。

それに対してletconstでは製作者の意図を変数に込めることができます。

letの場合

let変数の再代入は可能で、変数の再宣言ができません。

以下のような形です。


let_func(){
  let member = '飯塚';
  console.log(member) //飯塚
  member = '豊本'
  console.log(member); //豊本。(再代入は可能) 
  let member = '角田';//SyntaxError: Identifier 'member' has already been declared(再宣言はできない)
  console.log(member);
}

let_func();

また、letのスコープ(有効範囲)はブロックの中であり、ブロックの中で宣言された変数はそのブロックの中でしか使えません。

function let_func() {
    let member = '飯塚';
    console.log(member) //飯塚
    if (member === '飯塚'){
        member = '豊本';
        let member2 = '角田'
        console.log(member2); //角田
    }
    console.log(member); //飯塚
    console.log(member2);//ReferenceError: member2 is not defined
}
  
  let_func();

上の場合、member2はifブロックの中で宣言しているので、そのブロックの外側からは無効です。

Rubyよろしく、ブロックの内側から、外側の変数を見ることは出来るので、あらかじめletで宣言されているmemberに対する再代入は可能です

constの場合

constは、再代入、再宣言共にできません。

ですので、値を変えて欲しくないものを最初に宣言しておくことで、意図しない値が代入されたり、変数自体で再宣言されて初期化がされることを防ぐことができます。

const FIRST_MEMBER = '飯塚' //値を変えて欲しくない

function const_func(){
  console.log(FIRST_MEMBER);
  FIRST_MEMBER = '豊本';
  console.log(FIRST_MEMBER); //TypeError: Assignment to constant variable.
}

const_func();

このように、FIRST_MEMBERの値を再代入しようとすると、エラーが起こります。

ただし、グローバルスコープとは別で、ローカルスコープ内で同じconst変数(定数)を宣言することはできます。

const FIRST_MEMBER = '飯塚' //値を変えて欲しくない

function const_func(){
  const FIRST_MEMBER = '豊本'
  console.log(FIRST_MEMBER); //豊本
}

const_func();

constletと同じく、スコープ(有効範囲が)ブロック内であるため、const_func()で宣言されたFIRST_MEMBERはブロック外のFIRST_MEMBER(飯塚)とは別のものと解釈できるからです。

また、こちらの記事(JavaScript var/let/constによる変数宣言))にもある通り、

constを使うと、再宣言再代入が不可という特徴から、constで定義されたものは絶対に変化しないと思いがちになりますが、オブジェクトを操作することは可能です。具体的には以下のコードが書けるということです。

具体的には以下の通りです。(こちらも記事から抜粋させていただいております。)

function (){
    const like = {fruit: "apple", movie:"GODZILLA", food:"soba"};
    console.log(like); //{fruit: "apple", movie: "GODZILLA", food: "soba"}
    like.food = "rice";
    console.log(like); //{fruit: "apple", movie: "GODZILLA", food: "rice"}
    like = {fruit: "grape", movie: "GODZILLA", food: "rice"}; //これはNG
}

こちらもRubyよろしく、宣言したlikeを変更しているわけではなく、likeの要素の参照先を変えているだけだから。というところでしょうか

アロー関数について

とりあえず、一例

//functionの場合
let members = [
  { name: '飯塚',role:'ツッコミ',megane: false }, 
  { name: '豊本',role:'小ボケ',megane:true }, 
  { name:'角田',role:'大ボケ',megane:true }
];

let names = function(){
	return members.map(function(member){
  	return member.name;
  });
}

names();
=>["飯塚","豊本","角田"]

//アロー関数の場合
//functionの場合
let members = [
  { name: '飯塚',role:'ツッコミ',megane: false }, 
  { name: '豊本',role:'小ボケ',megane:true }, 
  { name:'角田',role:'大ボケ',megane:true }
];

let names = () => {
  return members.map(
	(member) => {return member.name}
)}

names();

こんな感じで直感的にかけます。 関数に渡す引数が一つの場合()を省略でき、実行する処理が1行ならreturnも不要とのこと。

//引数一つで、処理も1行
let allow_func1 =  n => n * 2;
allow_func1(1);
=>2
//引数がなしで、処理は1行
let allow_func2 = () => 1 * 2;
allow_func2();
=>2
//引数なしで、処理が複数行

let allow_func3 = () => {
	let res = 200 - 100;
  return res;
}
allow_func3();
=>100

こんな感じで処理を短く、直感的にかけます。

ただし、今までのfunctionアロー関数ではthisの意味合いが異なります。

functionでは、関数を呼び出したスコープでthisが決まりますが、アロー関数では、アロー関数が定義された時点で、thisは定義された時のスコープで決まります。

//functionの場合
let name = 'globalな飯塚';

function getname(){
  console.log(this.name);
}

let member1 = {
  name: '角田',
  func: getname
}
let member2 = {
  name: '豊本',
  func: getname
}

member1.func();
member2.func();
=>角田
豊本 //呼び出し元のスコープでthisが決まる


//allow関数の場合
this.name = 'グローバルな飯塚';

let getName = () => {
  console.log(this.param);
}

let member1 = {
  name: '豊本',
  func: getaName
}
let member2 = {
  name: '角田',
  func: getName
}

object.func();
object2.func();
=>グローバルな飯塚
グローバルな飯塚//アロー関数が定義されたスコープでthisが決まる。

アロー関数便利ですが、今までのfunctionの完全なる置き換えという訳にはいかないですね。 thisの取り扱いには要注意です。

まとめ

  • varではなくletとconstを使って、コードを見る人のために、変数に情報を込めてあげる
    • constだから定数的な使い方だな・・・
    • letだから再代入はされるんだな・・・
  • アロー関数で直感的に処理をかける
    • 短い処理なら、1行でfunctionをかける
    • thisの取り扱いには要注意