2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

function式とarrow式の違いをサッカー選手を呼び出しながら説明するよ

Posted at

概要

function式とarrow式の違いをサッカー選手を使いながら軽く説明するよ

問題

早速、問題を出します。
aとbの違いわかりますか?

const a = function() {
  console.log('neymar')
}

const b = () => {
  console.log('neymar')
}

この場合だとbのほうが合計文字数がすくなくなったくらいですかね。

// let, const であればトップレベルで宣言してもwindowオブジェクトのプロパティを生成しませんが今回は問題の都合上varでいきます
// よほどのことがない限りこんな書き方をしないけどグローバルのnameに代入するために記載
// var宣言が怖い理由の一つもこれですね。

var name = '😈😈global-messhi😈😈'

const printName = function() {
  console.log('printName', this.name)
}

const arrowPrintName = () => {
  console.log('arrowPrintName', this.name)
}

const obj1 = {
  name: 'nagatomo',
  printName
}

const obj2 = {
  name: 'king-kazu',
  arrowPrintName
}

// 出力される名前はなんでしょう
obj1.printName()
obj2.arrowPrintName()
答え
obj1.printName() // nagatomo
obj2.arrowPrintName() // 😈😈global-messhi😈😈

アロー関数式とfunction式の違い

先程の問題の結果を見て、文字数が短くなっただけではないことはすでにわかったと思います。

thisの指している場所が違います。

通常のfunction式の中でthisを使うと、その呼び出し元のオブジェクトを指します。

var name = '😈😈global-messhi😈😈'

const printName = function() {
  console.log('printName', this.name)
}

const obj1 = {
  name: 'nagatomo',
  printName
}

const obj2 = {
  name: 'king-kazu',
  printName
}

obj1.printName() // 'nagatomo'
obj2.printName() // 'king-kazu'

なのでこの場合printName関数内で使っているthis.nameの値は呼び出し元がobj1とobj2なのかで値が異なってきます。
function式だとthisが呼ばれたタイミングで決定されちゃんと自立できていない人のような振る舞いです。
なのでfunction式は直感でわかりにくいので苦労します。

アロー関数は結論から言うとthisは宣言された時点で、thisを確定します。

var name = '😈😈global-messhi😈😈'

const arrowPrintName = () => {
  console.log('arrowPrintName', this.name)  // この時点でのthisはグローバルになる
}

const obj1 = {
  name: 'nagatomo',
  arrowPrintName
}

const obj2 = {
  name: 'king-kazu',
  arrowPrintName
}

obj1.arrowPrintName() // '😈😈global-messhi😈😈'
obj2.arrowPrintName() // '😈😈global-messhi😈😈'

なので、呼び出し元がobj1だろうが、obj2だろうが、関係なくて直感的にthisの指している場所がわかります。

今学んだことを再確認するために、もっかい問題出します。


class Person {
  constructor(name) {
    this.name = name
  }
  printName = function () {console.log('printName', this.name)}
  arrowPrintName = () => {console.log('arrowPrintName', this.name)}
}

const printName = function() {
  console.log('printName', this.name)
}

const arrowPrintName = () => {
  console.log('arrowPrintName', this.name)
}

class Person2 {
  constructor(name) {
      this.name = name
  }
  printName = printName
  arrowPrintName = arrowPrintName
}


const kakitani = new Person('kakitani')
const maezono = new Person2('maezono')

// 出力される名前はなんでしょう
kakitani.printName() 
kakitani.arrowPrintName()
maezono.printName()
maezono.arrowPrintName()
答え
kakitani.printName()  // kakitani
kakitani.arrowPrintName() // kakitani
maezono.printName() // maezono
maezono.arrowPrintName() // 😈😈global-messhi😈😈

const bindPrintName = function() {
  console.log(this.name)
}.bind(this) // function式をarrow関数と同じ感じでthisをつかえるようにするための魔法のコトバだよ

class Person3 {
  constructor(name) {
    this.name = name
  }
  bindPrintName = bindPrintName
}

// 出力される名前はなんでしょう
const zaccheroni = new Person3('zaccheroni')
zaccheroni.bindPrintName()
答え
const zaccheroni = new Person3('zaccheroni')
zaccheroni.bindPrintName() // 😈😈global-messhi😈😈

Reactでよくあるパターン

これだとボタンを押した時Uncaught TypeError: Cannot read property 'state' of undefinedが出ます。
理由わかりますか?

export default class Hoge extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'zico'
    };
  }

  handlePrintName() {
    const { name } = this.state;
    return name
  }

  render() {
    const { name } = this.state;

    return (
      <button onClick={this.handlePrintName}>
        print name
      </button>
    );
  }
}

解決方法

handlePrintName()をアロー関数にする

  // 変更前
  handlePrintName() { // これでthisが定まっていない
    const { name } = this.state;  
    return name
  }
  // 変更後
  handlePrintName = () => { // これでthisをHogeにbind
    const { name } = this.state;  
    return name
  }

ただReactの場合これには問題点があります。

レンダー内でアロー関数を利用するとコンポーネントがレンダーされるたびに新しい関数が作成されるため、子コンポーネントでReact.memoやPureComponentを使ってた場合に正しく比較されなくなる

ClassComponentではbind(this)を使う場合が多い


export default class Hoge extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'zico'
    };
  this.handlePrintName  = this.handlePrintName.bind(this)
  }

  handlePrintName() {
    const { name } = this.state;
    return name
  }

  render() {
    const { name } = this.state;

    return (
      <button onClick={this.handlePrintName}>
        print name
      </button>
    );
  }
}

参考記事

JavaScriptの「this」は「4種類」??
JavaScript の this を理解する多分一番分かりやすい説明

2
0
0

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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?