Object related methods
JavaScript provides many related methods on the Object
object to handle related operations of object-oriented programming. This chapter introduces these methods.
Object.getPrototypeOf()
The Object.getPrototypeOf
method returns the prototype of the parameter object. This is the standard way to obtain prototype objects.
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype; // true
In the above code, the prototype of the instance object f
is F.prototype
.
The following are the prototypes of several special objects.
// The prototype of the empty object is Object.prototype
Object.getPrototypeOf({}) === Object.prototype; // true
// The prototype of Object.prototype is null
Object.getPrototypeOf(Object.prototype) === null; // true
// The prototype of the function is Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype; // true
Object.setPrototypeOf()
The Object.setPrototypeOf
method sets the prototype for the parameter object and returns the parameter object. It accepts two parameters, the first is an existing object, and the second is a prototype object.
var a = {};
var b = { x: 1 };
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b; // true
ax; // 1
In the above code, the Object.setPrototypeOf
method sets the prototype of the object a
to the object b
, so a
can share the properties of b
.
The new
command can be simulated using the Object.setPrototypeOf
method.
var F = function () {
this.foo = "bar";
};
var f = new F();
// Equivalent to
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
In the above code, the new
command creates a new instance object, in fact, it can be divided into two steps. The first step is to set the prototype of an empty object to the prototype
property of the constructor (the above example is F.prototype
); the second step is to bind the this
inside the constructor to this empty object, and then execute The constructor makes the methods and properties defined on this
(the above example is this.foo
) are transferred to this empty object.
Object.create()
The common way to generate instance objects is to use the new
command to make the constructor return an instance. But in many cases, only one instance object can be obtained. It may not be generated by the constructor at all. Can one instance object be generated from another instance object?
JavaScript provides the Object.create()
method to meet this need. This method accepts an object as a parameter, and then uses it as a prototype to return an instance object. This instance completely inherits the properties of the prototype object.
// Prototype object
var A = {
print: function () {
console.log("hello");
},
};
// instance object
var B = Object.create(A);
Object.getPrototypeOf(B) === A; // true
B.print(); // hello
B.print === A.print; // true
In the above code, the Object.create()
method uses the A
object as the prototype to generate the B
object. B
inherits all the attributes and methods of A
.
In fact, the Object.create()
method can be replaced with the following code.
if (typeof Object.create !== "function") {
Object.create = function (obj) {
function F() {}
F.prototype = obj;
return new F();
};
}
The above code shows that the essence of the Object.create()
method is to create an empty constructor F
, then let the F.prototype
property point to the parameter object obj
, and finally return an instance of F
, thus Implement the instance to inherit the attributes of obj
.
The new objects generated in the following three ways are equivalent.
var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = new Object();
If you want to generate an object that does not inherit any properties (for example, there is no toString()
and valueOf()
methods), you can set the parameter of Object.create()
to null
.
var obj = Object.create(null);
obj.valueOf();
// TypeError: Object [object Object] has no method'valueOf'
In the above code, the prototype of the object obj
is null
, it does not have some properties defined on the Object.prototype
object, such as the valueOf()
method.
When using the Object.create()
method, the object prototype must be provided, that is, the parameter cannot be empty or not an object, otherwise an error will be reported.
Object.create();
// TypeError: Object prototype may only be an Object or null
Object.create(123);
// TypeError: Object prototype may only be an Object or null
The new object generated by the Object.create()
method dynamically inherits the prototype. Any method added or modified on the prototype will be immediately reflected on the new object.
var obj1 = { p: 1 };
var obj2 = Object.create(obj1);
obj1.p = 2;
obj2.p; // 2
In the above code, modifying the object prototype obj1
will affect the instance object obj2
.
In addition to the prototype of the object, the Object.create()
method can also accept a second parameter. This parameter is an attribute description object, and the object attribute it describes will be added to the instance object as its own attribute.
var obj = Object.create(
{},
{
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: "abc",
enumerable: true,
configurable: true,
writable: true,
},
}
);
// Equivalent to
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = "abc";
The object generated by the Object.create()
method inherits the constructor of its prototype object.
function A() {}
var a = new A();
var b = Object.create(a);
b.constructor === A; // true
b instanceof A; // true
In the above code, the prototype of the b
object is the a
object, so it inherits the constructor A
of the a
object.
Object.prototype.isPrototypeOf()
The isPrototypeOf
method of the instance object is used to determine whether the object is the prototype of the parameter object.
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3); // true
o1.isPrototypeOf(o3); // true
In the above code, o1
and o2
are both prototypes of o3
. This means that as long as the instance object is on the prototype chain of the parameter object, the isPrototypeOf
method returns true
.
Object.prototype.isPrototypeOf({}); // true
Object.prototype.isPrototypeOf([]); // true
Object.prototype.isPrototypeOf(/xyz/); // true
Object.prototype.isPrototypeOf(Object.create(null)); // false
In the above code, because Object.prototype
is at the top of the prototype chain, it returns true
for various instances, except for objects that directly inherit from null
.
Object.prototype.__proto__
The __proto__
property of the instance object (two underscores before and after) returns the prototype of the object. This attribute can be read and written.
var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p; // true
The above code uses the __proto__
property to set the p
object as the prototype of the obj
object.
According to the language standard, the __proto__
property only needs to be deployed by the browser, and other environments may not have this property. The two underscores before and after it indicate that it is essentially an internal attribute and should not be exposed to users. Therefore, this property should be used as little as possible, and instead use Object.getPrototypeOf()
and Object.setPrototypeOf()
to read and write prototype objects.
The prototype chain can be expressed intuitively with __proto__
.
var A = {
name: "Zhang San",
};
var B = {
name: "Li Si",
};
var proto = {
print: function () {
console.log(this.name);
},
};
A.__proto__ = proto;
B.__proto__ = proto;
A.print(); // Zhang San
B.print(); // Li Si
A.print === B.print; // true
A.print === proto.print; // true
B.print === proto.print; // true
In the above code, the prototypes of the A
object and the B
object are both proto
objects, and they both share the print
method of the proto
object. In other words, the print
methods of A
and B
are both calling the print
method of the proto
object.
Comparison of methods for obtaining prototype objects
As mentioned earlier, the __proto__
property points to the prototype object of the current object, that is, the prototype
property of the constructor.
var obj = new Object();
obj.__proto__ === Object.prototype;
// true
obj.__proto__ === obj.constructor.prototype;
// true
The above code first creates a new object obj
, and its __proto__
property points to the prototype
property of the constructor (Object
or obj.constructor
).
Therefore, there are three ways to obtain the prototype object of the instance object obj
.
-obj.__proto__
-obj.constructor.prototype
-Object.getPrototypeOf(obj)
Of the above three methods, the first two are not very reliable. The __proto__
property only needs to be deployed in the browser, and it is not necessary to deploy it in other environments. And obj.constructor.prototype
may become invalid when the prototype object is manually changed.
var P = function () {};
var p = new P();
var C = function () {};
C.prototype = p;
var c = new C();
c.constructor.prototype === p; // false
In the above code, the prototype object of the constructor C
is changed to p
, but the c.constructor.prototype
of the instance object does not point to p
. Therefore, when changing the prototype object, the constructor
property is generally set at the same time.
C.prototype = p;
C.prototype.constructor = C;
var c = new C();
c.constructor.prototype === p; // true
Therefore, it is recommended to use the third method Object.getPrototypeOf
to get the prototype object.
Object.getOwnPropertyNames()
The Object.getOwnPropertyNames
method returns an array. The members are the key names of all the properties of the parameter object itself, excluding the inherited property keys.
Object.getOwnPropertyNames(Date);
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]
In the above code, the Object.getOwnPropertyNames
method returns all the property names of Date
itself.
Among the properties of the object itself, some are enumerable and some are not. The Object.getOwnPropertyNames
method returns all key names, regardless of whether they can be traversed. To get only those properties that can be traversed, use the Object.keys
method.
Object.keys(Date); // []
The above code shows that all the properties of the Date
object cannot be traversed.
Object.prototype.hasOwnProperty()
The hasOwnProperty
method of an object instance returns a boolean value, which is used to determine whether a property is defined in the object itself or in the prototype chain.
Date.hasOwnProperty("length"); // true
Date.hasOwnProperty("toString"); // false
The above code shows that Date.length
(how many parameters the constructor Date
can accept) is a property of Date
itself, and Date.toString
is an inherited property.
In addition, the hasOwnProperty
method is the only method in JavaScript that does not traverse the prototype chain when dealing with object properties.
in operator and for...in loop
The in
operator returns a boolean value indicating whether an object has a certain property. It does not distinguish whether the attribute is an attribute of the object itself or an inherited attribute.
"length" in Date; // true
"toString" in Date; // true
The in
operator is often used to check whether an attribute exists.
To get all the traversable properties of an object (whether it's own or inherited), you can use the for...in
loop.
var o1 = { p1: 123 };
var o2 = Object.create(o1, {
p2: { value: "abc", enumerable: true },
});
for (p in o2) {
console.info(p);
}
// p2
// p1
In the above code, the p2
property of the object o2
is its own, and the p1
property is inherited. Both properties will be traversed by for...in
loop.
In order to obtain the properties of the object itself in the for...in
loop, you can use the hasOwnProperty
method to judge.
for (var name in object) {
if (object.hasOwnProperty(name)) {
/* loop code */
}
}
To obtain all the properties of an object (whether it is self or inherited, and whether it is enumerable or not), you can use the following function.
function inheritedPropertyNames(obj) {
var props = {};
while (obj) {
Object.getOwnPropertyNames(obj).forEach(function (p) {
props[p] = true;
});
obj = Object.getPrototypeOf(obj);
}
return Object.getOwnPropertyNames(props);
}
The above code sequentially obtains the "self" properties of each level of the prototype object of the obj
object, thereby obtaining the "all" properties of the obj
object, regardless of whether it can be traversed.
The following is an example that lists all the attributes of the Date
object.
inheritedPropertyNames(Date);
// [
// "caller",
// "constructor",
// "toString",
// "UTC",
// ...
//]
Copy of object
If you want to copy an object, you need to do the following two things.
-Ensure that the copied object has the same prototype as the original object. -Ensure that the copied object has the same instance attributes as the original object.
The following is the object copy function implemented based on the above two points.
function copyObject(orig) {
var copy = Object.create(Object.getPrototypeOf(orig));
copyOwnPropertiesFrom(copy, orig);
return copy;
}
function copyOwnPropertiesFrom(target, source) {
Object.getOwnPropertyNames(source).forEach(function (propKey) {
var desc = Object.getOwnPropertyDescriptor(source, propKey);
Object.defineProperty(target, propKey, desc);
});
return target;
}
Another simpler way is to use ES2017 to introduce the standard Object.getOwnPropertyDescriptors
method.
function copyObject(orig) {
return Object.create(
Object.getPrototypeOf(orig),
Object.getOwnPropertyDescriptors(orig)
);
}
Reference link
- Dr. Axel Rauschmayer, JavaScript properties: inheritance and enumerability