1
1

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.

【JavaScript】JavaScriptの 「this」とは 2 (thisの問題点と解決策)

Last updated at Posted at 2020-12-13

※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。

こちらは、
【JavaScript】JavaScriptの 「 this」とは
の記事の続きとなります。

##thisの問題点について

メソッドを、関数化して、実行したらベースオブジェクトがなくなってしまいthisがundeifinedとなる。

どういうことかといいますと。

"use strict";

const person = {
  name: "Taro",
  sayName: function () {
    return this.name;
  }
};
console.log(person.sayName());

sayNameは呼び出し元のnameプロパティの値を返すメソッドです。

今、このメソッドを変数に格納して、実行してみます。

const say = person.sayName;
console.log(say());

//TypeError: Cannot read property 'name' of undefined

nameというプロパティーなど定義されていない。と返却されます。これは、呼び出し元がグローバルオブジェクトとなったため、グローバルオブジェクトにnameというプロパティーが定義されていないためです。

thisは定義したときではなく実行したときに決定されます。 そのため、関数にthisを含んでいる場合、その関数は意図した呼ばれ方がされないと間違った結果が発生してしまします。

##この問題の対処法

###メソッドを関数として呼ばない。
そもそも、メソッドとして定義している関数を、関数として呼び出さなければ、この問題は生じません。

###call,applyでthisを指定して呼び出し。

"use strict";
const person = {
  name: "Taro",
  sayName: function () {
    return this.name;
  }
};
const say = person.sayName;
console.log(say.call(person));

//Taro

call,applyメソッドについては、こちらをご覧ください。
【JavaScript】JavaScriptの 「 this」とは

###bindでthisを固定した関数を作り、呼び出す。
bindメソッドはthisの値を束縛(bind)した新しい関数を作成します。

"use strict";
const person = {
  name: "Taro",
  sayName: function () {
    return this.name;
  }
};
const say = person.sayName.bind(person);
//bindでthisをpersonオブジェクトに固定
console.log(say());

//Taro

##thisの問題点について 2

コールバック関数の中でthisを参照すると問題となる場合があります。

下記のように、mapメソッドを用いて、配列の各要素に the-という接頭語をつけて、新たな配列を作ることを意図したプログラムがあるとします。

"use strict";
const prefix = {
  pre: "the",
  addPre(strings) {
    return strings.map(function (str) {
      return this.pre + "-" + str;
    });
  }
};
prefix.addPre(["a", "b", "c"]); 

// =>TypeError: Cannot read property 'pre' of undefined

このように、preは定義されていないとなります。

これは、コールバック関数は、prefixオブジェクトのプロパティーとして呼び出されている訳ではなく、単純に、strings.mapの引数に匿名関数として呼び出されているからです。

##この問題の対処法

###thisを一時変数へ代入する。

"use strict";
const prefix = {
  pre: "the",
  addPre(strings) {
    let that = this; //thisをthatに一時格納する。
    return strings.map(function (str) {
      return that.pre + "-" + str; //thatを参照する。
    });
  }
};
let result = prefix.addPre(["a", "b", "c"]);
console.log(result);

//[ 'the-a', 'the-b', 'the-c' ]

一時的に、addPre内で 変数thatにthisを格納します。

prefix.addPre();

が実行された際、addPreの呼び出し元はprefixオブジェクトとなるため、thisは当然、呼び出し元であるprefixオブジェクトが格納されています。

return that.pre + "-" + str; 

が実行された際、thatはスコプーチェーンによりaddPre関数配下のthatを参照します。
↓ スコープチェーンについては、こちらをご覧ください。
【JavaScript】JavaScriptにおけるクロージャーとは

先ほども、述べた様にthatにはthis(prefixオブジェクト)が格納されているので、that.preで"the"が参照できる訳です。

###アロー関数をコールバック関数で使う。

これについては、少々長くなりそうなので別記事で。

【JavaScript】JavaScriptの 「this」とは 3 (thisの問題点とアロー関数による解決)

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?