オブジェクト関連のメソッド

JavaScript は、オブジェクト指向プログラミングに関連する操作を処理するために、Object オブジェクトに関連するメソッドを多数提供します。この章では、これらの方法について説明します。

Object.getPrototypeOf()

Object.getPrototypeOf メソッドはパラメータ オブジェクトのプロトタイプを返します。これは、プロトタイプ オブジェクトを取得する標準的な方法です。

var F = 関数 () {};
var f = 新しい F();
Object.getPrototypeOf(f) === F.prototype // true

上記のコードでは、インスタンス オブジェクト f のプロトタイプは F.prototype です。

以下は、いくつかの特別なオブジェクトのプロトタイプです。

// 空のオブジェクトのプロトタイプは Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true

// Object.prototype のプロトタイプは null
Object.getPrototypeOf(Object.prototype) === null // true

// 関数のプロトタイプは Function.prototype です
関数 f() {}
Object.getPrototypeOf(f) === Function.prototype // true

Object.setPrototypeOf()

Object.setPrototypeOf メソッドは、パラメータ オブジェクトのプロトタイプを設定し、パラメータ オブジェクトを返します。 2 つのパラメータを受け入れます。1 つ目は既存のオブジェクト、2 つ目はプロトタイプ オブジェクトです。

var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);

Object.getPrototypeOf(a) === b // true
a.x // 1

上記のコードでは、Object.setPrototypeOf メソッドがオブジェクト a のプロトタイプをオブジェクト b に設定するため、ab のプロパティを共有できます。

「new」コマンドは、「Object.setPrototypeOf」メソッドを使用してシミュレートできます。

var F = 関数 () {
  this.foo = 'バー';
};

var f = 新しい F();
// と同等
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);

上記のコードでは、「new」コマンドによって新しいインスタンス オブジェクトが作成されますが、これは実際には 2 つのステップに分割できます。最初のステップは、空のオブジェクトのプロトタイプをコンストラクターの prototype 属性に設定することです (上記の例は F.prototype)。2 番目のステップは、コンストラクター内の this を空のオブジェクトにバインドすることです。コンストラクターにより、this (上の例は this.foo) で定義されたメソッドとプロパティがこの空のオブジェクトに転送されます。

##Object.create()

インスタンス オブジェクトを生成する一般的な方法は、「new」コマンドを使用してコンストラクターにインスタンスを返させることです。しかし、多くの場合、取得できるインスタンス オブジェクトは 1 つだけであり、構築関数によってまったく生成されない可能性があります。では、1 つのインスタンス オブジェクトから別のインスタンス オブジェクトを生成できるでしょうか。

JavaScript は、このニーズを満たすために Object.create() メソッドを提供します。このメソッドはオブジェクトをパラメータとして受け取り、それをプロトタイプとして使用してインスタンス オブジェクトを返します。インスタンスはプロトタイプ オブジェクトのプロパティを完全に継承します。

//プロトタイプオブジェクト
var A = {
  印刷: 関数 () {
    console.log('こんにちは');
  }
};

//インスタンスオブジェクト
var B = Object.create(A);

Object.getPrototypeOf(B) === A // true
B.print() // こんにちは
B.print === A.print // true

上記のコードでは、Object.create() メソッドが A オブジェクトをプロトタイプとして受け取り、B オブジェクトを生成します。 B は、A のすべてのプロパティとメソッドを継承します。

実際、Object.create() メソッドは次のコードで置き換えることができます。

if (typeof Object.create !== '関数') {
  Object.create = 関数 (obj) {
    関数 F() {}
    F.プロトタイプ = obj;
    新しい F() を返します。
  };
}

上記のコードは、Object.create() メソッドの本質は、新しい空のコンストラクター F を作成し、次に F.prototype 属性にパラメーター オブジェクト obj をポイントさせ、最後にF のインスタンスなので、obj のプロパティを継承するインスタンスを実装します。

次の 3 つの方法で生成された新しいオブジェクトは同等です。

var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = 新しいオブジェクト();

プロパティを継承しないオブジェクト (たとえば、toString() メソッドや valueOf() メソッドを持たないオブジェクト) を生成したい場合は、Object.create() のパラメータを に設定できます。ヌル

var obj = Object.create(null);

obj.valueOf()
// TypeError: オブジェクト [object Object] にはメソッド 'valueOf' がありません

上記のコードでは、オブジェクト obj のプロトタイプは null であり、valueOf() メソッドなどの Object.prototype オブジェクトで定義されたいくつかのプロパティがありません。

Object.create() メソッドを使用する場合は、オブジェクト プロトタイプを指定する必要があります。つまり、パラメータを空にしたり、オブジェクト以外にすることはできません。そうでない場合は、エラーが報告されます。

Object.create()
// TypeError: オブジェクト プロトタイプは Object または null のみです
オブジェクト.create(123)
// TypeError: オブジェクト プロトタイプは Object または null のみです

Object.create() メソッドによって生成された新しいオブジェクトは、プロトタイプを動的に継承します。プロトタイプにメソッドを追加または変更すると、新しいオブジェクトにすぐに反映されます。

var obj1 = { p: 1 };
var obj2 = Object.create(obj1);

obj1.p = 2;
obj2.p // 2

上記のコードでは、オブジェクト プロトタイプ obj1 を変更すると、インスタンス オブジェクト obj2 に影響します。

オブジェクトのプロトタイプに加えて、「Object.create()」メソッドは 2 番目のパラメータも受け入れることができます。このパラメータはプロパティ記述オブジェクトであり、記述されるオブジェクトのプロパティは、オブジェクト自体のプロパティとしてインスタンス オブジェクトに追加されます。

var obj = Object.create({}, {
  p1: {
    値: 123、
    列挙可能: true、
    構成可能: true、
    書き込み可能: true、
  }、
  p2: {
    値: 'abc'、
    列挙可能: true、
    構成可能: true、
    書き込み可能: true、
  }
});

// と同等
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

Object.create() メソッドによって生成されたオブジェクトは、そのプロトタイプ オブジェクトのコンストラクターを継承します。

関数 A() {}
var a = 新しい A();
var b = Object.create(a);

b.constructor === A // true
binstanceof A // true

上記のコードでは、b オブジェクトのプロトタイプは a オブジェクトであるため、a オブジェクトのコンストラクター A を継承します。

Object.prototype.isPrototypeOf()

インスタンス オブジェクトの isPrototypeOf メソッドは、オブジェクトがパラメータ オブジェクトのプロトタイプであるかどうかを判断するために使用されます。

var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);

o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true

上記のコードでは、o1o2 は両方とも o3 のプロトタイプです。これは、インスタンス オブジェクトがパラメータ オブジェクトのプロトタイプ チェーン上にある限り、「isPrototypeOf」メソッドは「true」を返すことを意味します。

Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false

上記のコードでは、「Object.prototype」がプロトタイプ チェーンの先頭にあるため、「null」から直接継承するオブジェクトを除くすべてのインスタンスに対して「true」が返されます。

Object.prototype.__proto__

インスタンス オブジェクトの __proto__ 属性 (前後 2 つのアンダースコア) は、オブジェクトのプロトタイプを返します。このプロパティは読み取りおよび書き込み可能です。

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true

上記のコードは、__proto__ 属性を通じて、p オブジェクトを obj オブジェクトのプロトタイプとして設定します。

言語標準によれば、__proto__ 属性はブラウザによって展開されるだけでよく、他の環境ではこの属性は必要ありません。前後の 2 つのアンダースコアは、これが本質的に内部プロパティであり、ユーザーに公開すべきではないことを示しています。したがって、このプロパティの使用はできる限り少なくし、代わりに Object.getPrototypeOf() および Object.setPrototypeOf() を使用してプロトタイプ オブジェクトの読み書きを行う必要があります。

プロトタイプチェーンは __proto__ で直感的に表現できます。

var A = {
  名前:「チャン・サン」
};
var B = {
  名前:「李思」
};

var プロト = {
  印刷: 関数 () {
    console.log(この名前);
  }
};

A.__proto__ = プロト;
B.__proto__ = プロト;

A.print() // チャン・サン
B.print() // ジョン・ドゥ

A.print === B.print // true
A.print === proto.print // true
B.print === proto.print // true

上記のコードでは、「A」オブジェクトと「B」オブジェクトのプロトタイプは両方とも「proto」オブジェクトであり、両方とも「proto」オブジェクトの「print」メソッドを共有します。つまり、「A」と「B」の「print」メソッドは、「proto」オブジェクトの「print」メソッドを呼び出しています。

プロトタイプ オブジェクト メソッドの比較を取得する

前に述べたように、__proto__ 属性は現在のオブジェクトのプロトタイプ オブジェクトを指します。これはコンストラクターの prototype 属性です。

var obj = 新しいオブジェクト();

obj.__proto__ === オブジェクト.プロトタイプ
// 真実
obj.__proto__ === obj.constructor.prototype
// 真実

上記のコードは、まず新しいオブジェクト obj を作成し、その __proto__ 属性はコンストラクター (Object または obj.constructor) の prototype 属性を指します。

したがって、インスタンスオブジェクト「obj」のプロトタイプオブジェクトを取得するには3つの方法がある。

  • obj.__proto__
  • obj.constructor.prototype
  • Object.getPrototypeOf(obj)

上記 3 つの方法のうち、最初の 2 つはあまり信頼できません。 __proto__ 属性はブラウザにデプロイするだけでよく、他の環境にデプロイする必要はありません。また、プロトタイプオブジェクトを手動で変更すると、obj.constructor.prototype が無効になる場合があります。

var P = function () {};
var p = 新しい P();

var C = function () {};
C.プロトタイプ = p;
var c = 新しい C();

c.constructor.prototype === p // false

上記のコードでは、コンストラクター C のプロトタイプ オブジェクトが p に変更されていますが、インスタンス オブジェクトの c.constructor.prototypep を指していません。したがって、プロトタイプオブジェクトを変更する場合、通常は同時に constructor 属性を設定する必要があります。

C.プロトタイプ = p;
C.prototype.constructor = C;

var c = 新しい C();
c.constructor.prototype === p // true

したがって、プロトタイプ オブジェクトを取得するには 3 番目の Object.getPrototypeOf メソッドを使用することをお勧めします。

Object.getOwnPropertyNames()

Object.getOwnPropertyNames メソッドは、継承されたプロパティ キーを除く、パラメーター オブジェクト自体のすべてのプロパティのキーをメンバーとする配列を返します。

Object.getOwnPropertyNames(日付)
// ["解析", "引数", "UTC", "呼び出し元", "名前", "プロトタイプ", "現在", "長さ"]

上記のコードでは、Object.getOwnPropertyNames メソッドは、Date 自体のすべてのプロパティ名を返します。

オブジェクト自体のプロパティの中には、トラバース可能な (列挙可能な) ものとそうでないものがあります。 Object.getOwnPropertyNames メソッドは、走査できるかどうかに関係なく、すべてのキー名を返します。走査可能なプロパティのみを取得するには、Object.keys メソッドを使用します。

Object.keys(日付) // []

上記のコードは、Date オブジェクトのすべてのプロパティを走査できないことを示しています。

Object.prototype.hasOwnProperty()

オブジェクト インスタンスの hasOwnProperty メソッドはブール値を返します。この値は、プロパティがオブジェクト自体に定義されているかプロトタイプ チェーンに定義されているかを判断するために使用されます。

Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false

上記のコードは、Date.length (コンストラクター Date が受け入れることができるパラメーターの数) が Date 自体のプロパティであり、Date.toString が継承されたプロパティであることを示しています。

さらに、hasOwnProperty メソッドは、オブジェクトのプロパティを処理するときにプロトタイプ チェーンを横断しない JavaScript の唯一のメソッドです。

in 演算子と for...in ループ

「in」演算子は、オブジェクトが特定の属性を持っているかどうかを示すブール値を返します。プロパティがオブジェクト自体のプロパティであるか、継承されたプロパティであるかは区別されません。

Date'length' // true
'toString' 日付の // true

「in」演算子は、属性が存在するかどうかを確認するためによく使用されます。

オブジェクト (独自のものか継承されたものであるかに関係なく) のすべての走査可能なプロパティを取得するには、for...in ループを使用できます。

var o1 = { p1: 123 };

var o2 = Object.create(o1, {
  p2: { 値: "abc"、列挙可能: true }
});

for (p in o2) {
  コンソール.info(p);
}
//p2
//p1

上記のコードでは、オブジェクト o2p2 属性は独自のものであり、p1 属性は継承されます。両方のプロパティは for...in ループによってトラバースされます。

for...in ループ内でオブジェクト自体のプロパティを取得するには、hasOwnProperty メソッドを使用して判断できます。

for (オブジェクト内の変数名) {
  if ( object.hasOwnProperty(name) ) {
    /* ループコード */
  }
}

オブジェクトのすべてのプロパティ (オブジェクトが独自のものか継承されたものであるか、列挙可能かどうか) を取得するには、次の関数を使用できます。

関数継承プロパティ名(obj) {
  var props = {};
  while(obj) {
    Object.getOwnPropertyNames(obj).forEach(function(p) {
      props[p] = true;
    });
    obj = Object.getPrototypeOf(obj);
  }
  戻り値 Object.getOwnPropertyNames(props);
}

上記のコードは、obj オブジェクトのプロトタイプ オブジェクトの各レベルの「独自の」プロパティを順番に取得し、それによって、走査可能かどうかに関係なく、obj オブジェクトの「すべての」プロパティを取得します。

以下は、Date オブジェクトのすべてのプロパティをリストする例です。

継承されたプロパティ名(日付)
//[
// "呼び出し元",
// "コンストラクター",
// "toString",
// "UTC"、
// ...
//]

オブジェクトのコピー

オブジェクトをコピーしたい場合は、次の 2 つのことを行う必要があります。

  • コピーされたオブジェクトが元のオブジェクトと同じプロトタイプを持つことを確認します。
  • コピーされたオブジェクトが元のオブジェクトと同じインスタンス プロパティを持つことを確認します。

以上の2点を踏まえて実装したオブジェクトコピー機能は以下の通りです。

関数 copyObject(orig) {
  var copy = Object.create(Object.getPrototypeOf(orig));
  copyOwnPropertiesFrom(コピー, orig);
  コピーを返却します。
}

関数 copyOwnPropertiesFrom(ターゲット, ソース) {
  物体
    .getOwnPropertyNames(ソース)
    .forEach(関数 (propKey) {
      var desc = Object.getOwnPropertyDescriptor(source, propKey);
      Object.defineProperty(target, propKey, desc);
    });
  リターンターゲット。
}

もう 1 つの簡単な記述方法は、ES2017 で導入された標準の Object.getOwnPropertyDescriptors メソッドを使用することです。

関数 copyObject(orig) {
  returnObject.create(
    Object.getPrototypeOf(orig)、
    Object.getOwnPropertyDescriptors(orig)
  );
}

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0