LoginSignup
6
12

More than 5 years have passed since last update.

JavaScriptの this での文字列と数値の扱い

Posted at

はじめに

JavaScriptの this は割と厄介だけど便利でもある存在ですが、この this に文字列や数値が入る場合、知らないと「あれ?」ってなる現象が起こります。

数値や文字列が 'object' 判定になる!?

jQueryの $.each() で配列やオブジェクトをループさせることはよくある事かと思います。
このメソッドでは this は配列やオブジェクトの値が入っているわけですが……

とある配列の値の型を調べる
var arr = [123, NaN, Infinity, 'abc'];

$.each(arr, function (i) {
  var type = typeof this;
  console.log(i + ': ' + this + ' is ' + type);
});
// 0: 123 is object
// 1: NaN is object
// 2: Infinity is object
// 3: abc is object

あれ? typeof で調べたら全部 'object' になってしまいました。

他にも、例えば Function.prototype.call()Function.prototype.bind()this に値を入れても同じ結果になります。

thisに入ってる値の型を調べる
function typeOfThis() {
  return typeof this;
}

typeOfThis.call(123);       // 'object'
typeOfThis.call(NaN);       // 'object'
typeOfThis.call(Infinity);  // 'object'
typeOfThis.call('abc');     // 'object'

typeOfThis.bind(123)();     // 'object'

this の値を見てみる

普通に計算にしたり文字列結合できたりするのに、なぜ typeof で調べると 'object' なのか。
console.dir()this がどうなっているか見てみます。

Chromeで実行した例
function thisTest() {
  console.dir(this);
}

thisTest.call(123);
/*
Number
  __proto__: Number
    constructor: function Number()
    ~省略~
  [[PrimitiveValue]]: 123
*/

thisTest.call(NaN);
/*
Number
  __proto__: Number
    constructor: function Number()
    ~省略~
  [[PrimitiveValue]]: NaN
*/

thisTest.call(Infinity);
/*
Number
  __proto__: Number
    constructor: function Number()
    ~省略~
  [[PrimitiveValue]]: Infinity
*/

thisTest.call('abc');
/*
String
  0: "a"
  1: "b"
  2: "c"
  length: 3
  __proto__: String
    constructor: function String()
    ~省略~
  [[PrimitiveValue]]: "abc"
*/

よくわからない部分もあるかと思いますが、数値リテラルや文字列リテラルではなく数値オブジェクトや文字列オブジェクトとして this に入っているのがわかります。
要するに、new Number(123)new String('abc') 等で生成したのと同じものになります。だから typeof では 'object' になってしまうんですね。

this にはオブジェクトが入るべきという感じでしょうか。(実はよく知らないですごめんなさい)

こうすれば解決できる

じゃあどうすればいいか、こういった方法があります。

jQueryであれば第2引数を使う

$.each() のコールバック関数の第2引数にも配列やオブジェクトの値を取ることができます。

とある配列の値の型を調べる
var arr = [123, NaN, Infinity, 'abc'];

$.each(arr, function (i, value) {
  var type = typeof value;
  console.log(i + ': ' + value + ' is ' + type);
});
// 0: 123 is number
// 1: NaN is number
// 2: Infinity is number
// 3: abc is string

this で扱うよりも自然な感じがしてわかりやすいので、こちらの使い方の方が個人的にはいいのではと思います。

Strictモードにしてしまう

'use strict'; をコードの頭に置くことにより、ブラウザがそのコードをStrictモードとして扱います。
Strictモードは厳格な処理を行い、this にはそのままの型で入るようになります。

とある配列の値の型を調べる
var arr = [123, NaN, Infinity, 'abc'];

$.each(arr, function (i) {
  'use strict';
  var type = typeof this;
  console.log(i + ': ' + this + ' is ' + type);
});
// 0: 123 is number
// 1: NaN is number
// 2: Infinity is number
// 3: abc is string

Function.prototype.call() でも同様に。

thisに入ってる値の型を調べる
function typeOfThis() {
  'use strict';
  return typeof this;
}

typeOfThis.call(123);       // 'number'
typeOfThis.call(NaN);       // 'number'
typeOfThis.call(Infinity);  // 'number'
typeOfThis.call('abc');     // 'string'

ただ、Strictモードに対応しているブラウザでないと意味がありません。
素直に引数として渡した方が確実な気もします……

普通に引数として渡す

ぶっちゃけ、this に入れる必要がないのであれば、引数として扱うべきです。

thisに入ってる値の型を調べる
function typeOf(obj) {
  return typeof obj;
}

typeOf(123);       // 'number'
typeOf(NaN);       // 'number'
typeOf(Infinity);  // 'number'
typeOf('abc');     // 'string'

おわりに

会社の同僚がこれの沼にどはまりしていたので、やはり知らない人もいるのだと思いメモとして書きました。
jQueryに限った事ではないですが、this を取り扱う場合はしっかりと把握しておく必要がありますね。

6
12
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
6
12