かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript `{}` なオブジェクトだけを判定したい

個人的に JavaScript の要素は全部 Object で、型の概念は実質存在しないという認識。(Stringも "foo".length とか使える訳だし)
ある程度は typeof() で判別することができるけど、Ruby とかで Hash とか Python で DIctionary (辞書) とかと呼ばれるような {a: 1} みたいなオブジェクトだけを判定したいと思ったのでやってみたメモ。

色々なオブジェクトの typeof()

// String
'String' // 'string'
new String('Foo') // 'object'
// Number
1    // 'number'
-1   // 'number'
1.5  // 'number'
-0.8 // 'number'
NaN  // 'number'
// Boolean
true  // 'boolean'
false // 'boolean'
null  // 'object'
// undefined
undefined // 'undefined'
// Array
[]      // 'object'
[1, 2]  // 'object'
// Object (hash)
{}           // 'object'
{a: 1, b: 2} // 'object'
// Function
function() {} // 'function'
// Date
new Date() // 'object'
// RegExp
/\w+/              // 'object'
new RegExp('\\w+') // 'object'
// Symbol
Symbol()     // 'symbol'
Symbol( {} ) // 'symbol'
// Class
class MyClass {
  constructor() {}
};
new MyClass() // 'object'

殆どのオブジェクトはそれぞれの型が表示されるが、{} なオブジェクト以外で typeof() した時に object と判定されるのは次の通り

Array の判別

配列に関しては Array.isArray() で判定が可能

cf. Array.isArray() - JavaScript | MDN

// 以下の呼び出しはすべてtrueを返す
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array('a', 'b', 'c', 'd'));
Array.isArray(new Array(3));
// あまり知られていないものの Array.prototype は配列
Array.isArray(Array.prototype); 

// 以下の呼び出しはすべて false を返す
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ __proto__: Array.prototype });

Array.isArray() で配列を除いてもまだ{} なオブジェクトと null, new String(), Date, RegExp, Class を判別することはできない

Object.prototype.toString.call() で型を

Every object has a toString() method that is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected. By default, the toString() method is inherited by every object descended from Object. If this method is not overridden in a custom object, toString() returns "[object type]", where type is the object type.
ref. Object.prototype.toString() - JavaScript | MDN

var o = new Object();
o.toString(); // returns [object Object]

toString() can be used with every object and allows you to get its class. To use the Object.prototype.toString() with every object, you need to call Function.prototype.call() or Function.prototype.apply() on it, passing the object you want to inspect as the first parameter called thisArg.
ref. Object.prototype.toString() - JavaScript | MDN

var toString = Object.prototype.toString;

toString.call(new Date);    // [object Date]
toString.call(new String);  // [object String]
toString.call(Math);        // [object Math]

// Since JavaScript 1.8.5
toString.call(undefined);   // [object Undefined]
toString.call(null);        // [object Null]

Object.toString()[object type] を返し、あらゆるオブジェクトは Object を継承しているので Object.prototype.toString()call()apply()this になるオブジェクトを変更して呼び出せば引数で渡した this の型を表示することができる。

null, new String(), Array Date, RegExp, Class の場合

typeof(), Array.isArray() で除外できなかった null, new String(), Date, RegExp, ClassArray でどうなるか試してみた

function showObjectType(val) {
  const type = Object.prototype.toString.call(val);
  console.log( type.slice(8, -1) );
}

showObjectType( null ); // 'Null'
showObjectType( new String('foo') ); // 'String'
showObjectType( new Date() ); // 'Date'
showObjectType( /\w+/ ); // 'RegExp'
showObjectType( new RegExp('\\w+') ); // 'RegExp'
showObjectType( new MyClass ); // 'Object'
showObjectType( [] ); // 'Array'

Class インスタンス以外はそれぞれの型が表示されるのでこれで除外することができる。
Array.isArray() は内部的にこんな感じで判定しているのかもしれない。

Object.prototype.constructor で判定

All objects (with the exception of objects created with Object.create(null)) will have a constructor property. Objects created without the explicit use of a constructor function (i.e. the object and array literals) will have a constructor property that points to the Fundamental Object constructor type for that object.
ref. Object.prototype.constructor - JavaScript | MDN

var o = {};
o.constructor === Object; // true
var o = new Object;
o.constructor === Object; // true
var a = [];
a.constructor === Array; // true
var a = new Array;
a.constructor === Array; // true
var n = new Number(3);
n.constructor === Number; // true
new String(), Array Date, RegExp, Class の場合

null.constructor はエラーになるので、null 以外でテストしてみる

function getConstructor(val) {
  if( val === null ) return false;
  const type = val.constructor;
  return type;
}

getConstructor( new String('foo') ); // [Function: String]
getConstructor( new Date() ); // [Function: Date]
getConstructor( /\w+/ ); // [Function: RegExp]
getConstructor( new RegExp('\\w+') ); // [Function: RegExp]
getConstructor( new MyClass ); // [Function: MyClass]
getConstructor( [] ); // [Function: Array]

constructor プロパティの場合 Class インスタンスは元のクラスがコンストラクターとして返されるので {} なオブジェクトと判別することができる

{} なオブジェクトを判別する関数

次の条件を満たしていれば {} なオブジェクトだと判断できそうです

  1. typeof()"object"
  2. null でない
  3. constructorObject
function isObject(val) {
  if( val !== null
   && typeof(val) === 'object'
   && val.constructor === Object ) {
    return true;
  }
  return false;
}

👇 TEST

isObject( "String" ); // => false
isObject( new String("Foo") ); // => false
isObject( 1 ); // => false
isObject( -1 ); // => false
isObject( 1.5 ); // => false
isObject( -0.8 ); // => false
isObject( NaN ); // => false
isObject( true ); // => false
isObject( false ); // => false
isObject( null ); // => false
isObject( undefined ); // => false
isObject( [] ); // => false
isObject( [ 1, 2 ] ); // => false
isObject( {} ); // => true
isObject( { a: 1, b: 2 } ); // => true
isObject( function() {} ); // => false
isObject( new Date() ); // => false
isObject( /\w+/ ); // => false
isObject( new RegExp('\\w+') ); // => false
isObject( Symbol() ); // => false
isObject( Symbol({}) ); // => false
isObject( new MyClass() ); // => false
isObject( new Object() ); // => true

判別できる npm パッケージを公開しました


[参考]

Google流 疲れない働き方

Google流 疲れない働き方

最近経年劣化で年中無休な疲労コンパイルなのでこれ読んでる。
鋼鉄の体が欲しいゼ…

鋼鉄の体の立体物あったのか…!! コトブキヤ素晴らしい!!!!