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 1 year has passed since last update.

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

Last updated at Posted at 2021-12-27

はじめに

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"]
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?