ES 6の矢印関数を深く理解する

6308 ワード

JavaScriptは、ES 6の矢印関数を深く理解する.
    矢印関数式の構文は関数式より短く、自身のthis、argments、superまたはnew.targetをバインドしない.さらに、矢印関数は、非方法関数で使用した方がよく、構造関数としては使用できません.
構文
基礎文法
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
//    :  => { return expression; } 

//         ,       :
(singleParam) => { statements }
singleParam => { statements }

//                          :
() => { statements } _ => { statements }
高度な文法
//           ,       ,            :
params => ({foo: bar})

//    Rest parameters   default parameters:
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements }

//             
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6
説明
    矢印関数の導入には二つの面の役割があります.一つはより短い関数で書き、もう一つはthisに対する語法解析です.
より短い関数
より短い関数は関数式プログラミングで人気があります.比較:
var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryl­lium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );
バインディングしない
    矢印関数が現れる前に、それぞれの新しい定義された関数にはそれぞれのthis値があります.例えば、構造関数のthisは新しいオブジェクトを指します.厳密なモードでの関数のthisはundefinedです.関数がオブジェクトとしての方法で呼び出された場合、そのオブジェクトにthisが呼び出されます.オブジェクト指向のプログラミングでは、多くの悩みがあります.
function Person() {
  //      Person()     `this`          
  this.age = 0;
  setInterval(function growUp() {
    //        ,growUp()           `this`     , 
             Person()     `this`
    this.age++; 
  }, 1000);
}

var p = new Person();

//   ECMAScript 3/5  ,       this      ,
//               。
function Person() {
  var self = this; //         `that`    `self`. 
                   //         .
  self.age = 0;

  setInterval(function growUp() {
    //       `self`               
    self.age++;
  }, 1000);
}
これ以外にも、Bind関数を使用して、所望のthis値をgrowUp()関数に伝達することができる.
矢印関数は、そのコンテキストのthis値をキャプチャします.自分のthis値として、下記のコードは予定通り実行されます.
function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // this      person  
  }, 1000);
}

var p = new Person();
厳しいモードとの関係
thisが語法的な面であることを考慮して、厳密なモードではthisに関する規則は無視されます.
var f = () => {'use strict'; return this};
f() === window; //      
厳格なモードのその他の規則は依然として不変です.
コールまたはアップルを使って呼び出します.
thisは語法レベルでバインディングを完了したので、関数をcall()またはapply()方法で呼び出すと、パラメータが入ってきただけで、thisには影響がありません.
var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         //    2
console.log(adder.addThruCall(1)); //      2(   3)
バインディングされていないパラメータ(argments)
    矢印関数は、その内部にパラメータ(argments):argments.length、argments[0]、argments[1]などを露出しません.矢印関数のargmentsを指すのではなく、矢印関数のある作用領域の一つを指しています.
var arguments = 42;
var arr = () => arguments;

arr(); // 42

function foo() {
  var f = (i) => arguments[0]+i; // foo         
  return f(2);
}

foo(1); // 3
//           arguments ,
//          ,rest            :

function foo() { 
  var f = (...args) => args[0]; 
  return f(2); 
}

foo(1); // 2
方法のように矢印関数を使います.
上記のように、矢印関数式は非方法関数に最適です.方法として試してみたときに何が起こったかを見てみましょう.
'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b(); // undefined, Window
obj.c(); // 10, Object {...}
//         this  。
//      Object.defineProperty():   :

'use strict';
var obj = {
  a: 10
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this);
    return this.a+10; 
    // represents global object 'Window', 
    // therefore 'this.a' returns 'undefined'
  }
});
new操作子を使う
矢印関数はコンストラクタとしては使えません.newと一緒に使うとエラーが発生します.
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
原型の属性を使う
矢印関数にはプロトタイプの属性がありません.
var Foo = () => {};
console.log(Foo.prototype); // undefined
キーワードを使う
yieldキーは通常、矢印関数では使用できません.使用可能な関数内に入れ子がない限り、矢印関数はジェネレータとして使用できません.
関数本体
矢印関数は、簡単な書き込みと一般的な作成をサポートします.
簡略化するには、1つの表式と1つの戻り値だけが必要です.通常の作成には、明確な戻り値が必要です.
var func = x => x * x;                  
//        return

var func = (x, y) => { return x + y; }; 
//           
テキスト表式を返します.params => {object:literal}という簡単な文法で文字式を返すのは無理だと覚えてください.
var func = () => {  foo: 1  };
// undefined!

var func = () => {  foo: function() {}  };
// SyntaxError: function statement requires a name(       )
//        (  {} )              
//(  , foo         ,             )。
だから、丸括弧で文字表現を包んでください.var func = () => ({ foo: 1 });行をかえる
矢印関数はパラメータと矢印の間で改行できません.
var func = ()
=> 1; // SyntaxError: expected expression, got '=>'
解析の順序
矢印関数の矢印は操作子ではないですが、矢印関数には特殊な解析規則があります.普通の関数に比べて操作者の優先度によって影響されます.
let callback;

callback = callback || function() {}; // ok

callback = callback || () => {};      
// SyntaxError:        

callback = callback || (() => {});    // ok
//        ,   undefined
let empty = () => {};

(() => "foobar")() //    "foobar" 

var simple = a => a > 15 ? 15 : a; 
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

//        (  filter  ),  (  map  ), ...

var arr = [5, 6, 13, 0, 1, 18, 23];

var sum = arr.reduce((a, b) => a + b);  // 66

var even = arr.filter(v => v % 2 == 0); 
// [6, 0, 18] 

var double = arr.map(v => v * 2);       
// [10, 12, 26, 0, 2, 36, 46] 

//      promise 
promise.then(a => {
  // ...
}).then(b => {
   // ...
});

//             
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    // deeper code
    console.log('I happen later');
  }, 1);
}, 1);
About
GitHub:https://github.com/microzz 個人サイト:https://microzz.com/