0
Help us understand the problem. What are the problem?

posted at

updated at

[JavaScript] Class の継承元をたどって property を全て列挙する

はじめに

JSのクラスのメソッドは列挙属性がないので、for...in では列挙できません。結構不便。

getOwnPropertyNames では自分自身のプロパティは、列挙属性がないプロパティでも列挙できるので、それをprototypeをたどって、すべて列挙するようにできる関数を作りました。

次のページを大変参考にさせていただきました。

JavaScript Classのpropertyとmethodを列挙する - Qiita
https://qiita.com/BlueSilverCat/items/fc530e096a40beeb2a43

リンク先記事では、getAllMethodNames と getAllStaticMethodNames と getAllPropertyNames とがそれぞれ別関数になっていますが、

JSの場合結局は、Object.prototype が根っこにあり関数でもオブジェクトでも同じ扱いができるので、自分のコードでは統合しました。

JSではメソッドもプロパティの一部なので、メソッドなのかプロパティなのかは、そのプロパティ値が関数かどうかで判断するとよいです。

コードと動作確認

const propertyNames = (object) => {
  const result = [];
  let obj = object;
  while (true) {
    result.push(Object.getOwnPropertyNames(obj));
    obj = Object.getPrototypeOf(obj);
    if (obj === null) { break; }
  }
  return result;
}

class First {
  constructor() {
    this.dataFirst = "first";
  }
  funcFirst() { console.log("funcFirst"); }
  static staticFuncFirst() { console.log("static funcFirst") }
}

class Second extends First {
  constructor() {
    super();
    this.dataSecond = "second";
  }
  funcSecond() { console.log("funcSecond"); }
  static staticFuncSecond() { console.log("static funcSecond") }
}

class Third extends Second {
  constructor() {
    super();
    this.dataThird = "third";
  }
  funcThird() { console.log("funcThird"); }
  static staticFuncThird() { console.log("static funcThird") }
}

let props;
props = propertyNames({});
console.log(props);
// [
//   [],
//   [
//     'constructor',
//     '__defineGetter__',
//     '__defineSetter__',
//     'hasOwnProperty',
//     '__lookupGetter__',
//     '__lookupSetter__',
//     'isPrototypeOf',
//     'propertyIsEnumerable',
//     'toString',
//     'valueOf',
//     '__proto__',
//     'toLocaleString'
//   ]
// ]

props = propertyNames(new First());
props.pop();
console.log(props);
// [ [ 'dataFirst' ], [ 'constructor', 'funcFirst' ] ]

props = propertyNames(new Second());
props.pop();
console.log(props);
// [
//   [ 'dataFirst', 'dataSecond' ],
//   [ 'constructor', 'funcSecond' ],
//   [ 'constructor', 'funcFirst' ]
// ]

props = propertyNames(new Third());
props.pop();
console.log(props);
// [
//   [ 'dataFirst', 'dataSecond', 'dataThird' ],
//   [ 'constructor', 'funcThird' ],
//   [ 'constructor', 'funcSecond' ],
//   [ 'constructor', 'funcFirst' ]
// ]

props = propertyNames(() => {});
console.log(props);
// [
//   [ 'length', 'name' ],
//   [
//     'length',      'name',
//     'arguments',   'caller',
//     'constructor', 'apply',
//     'bind',        'call',
//     'toString'
//   ],
//   [
//     'constructor',
//     '__defineGetter__',
//     '__defineSetter__',
//     'hasOwnProperty',
//     '__lookupGetter__',
//     '__lookupSetter__',
//     'isPrototypeOf',
//     'propertyIsEnumerable',
//     'toString',
//     'valueOf',
//     '__proto__',
//     'toLocaleString'
//   ]
// ]

props = propertyNames(First);
props.pop(); props.pop();
console.log(props);
// [ [ 'length', 'prototype', 'staticFuncFirst', 'name' ] ]

props = propertyNames(Second);
props.pop(); props.pop();
console.log(props);
// [
//   [ 'length', 'prototype', 'staticFuncSecond', 'name' ],
//   [ 'length', 'prototype', 'staticFuncFirst', 'name' ]
// ]

props = propertyNames(Third);
props.pop(); props.pop();
console.log(props);
// [
//   [ 'length', 'prototype', 'staticFuncThird', 'name' ],
//   [ 'length', 'prototype', 'staticFuncSecond', 'name' ],
//   [ 'length', 'prototype', 'staticFuncFirst', 'name' ]
// ]

説明

戻り値が文字列配列の配列になっています。

動作確認でも行っていますが、結果の重複になるので継承元の最後の、Object.prototype が不要な場合は出力前に pop をしています。Class関数を渡している場合には Function.prototype の情報も不要かと思ったので、2回の pop をしています。

戻り値の文字列配列の配列をユニーク文字列配列にする場合は、propertyNames の戻り値に対して flat して uniq するとよいです。

flatは、Array.prototype.flat() 使い
uniq は下記のようにすると手軽でよいと思います。

const a = ['A', 'B', 'B', 'A'];
console.log([...(new Set(a))]);
// ["A", "B"]

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?