オブジェクト関連のメソッド
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
に設定するため、a
は b
のプロパティを共有できます。
「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
上記のコードでは、o1
と o2
は両方とも 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.prototype
は p
を指していません。したがって、プロトタイプオブジェクトを変更する場合、通常は同時に 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
上記のコードでは、オブジェクト o2
の p2
属性は独自のものであり、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)
);
}
参考リンク
- Axel Rauschmayer 博士、JavaScript プロパティ: 継承と列挙可能性
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0