preface

Object.defineproperty () is an API specified in the ES standard that defines the characteristics of Object attributes. It comes to mind because in the last article that explored instanceof in detail, I want to redefine the Symbol. HasInstance property of the function on the right, but it doesn’t work either way. The specific code is as follows:

var A = function() {};
var a = new A();

console.log('Symbol. HasInstance before modification ', a instanceof A)

A[Symbol.hasInstance] = function() {
    return false;
};

console.log('Symbol. HasInstance modified ', a instanceof A)
Copy the code

Code portal

If you are not sure of the printed result, if you have any doubt about the detection result of the keyword instanceof can be changed by setting Symbol. HasInstance of A, please refer to my other article for details that may be missed in the use of instanceof. If you only have questions about the second print you can read on.

Answer the above question first

We can make it clear that the code printed in the preamble is true both times. If we define the Symbol. HasInstance property of an object, we can specify the result of instanceof in the function. In the above code I set it to return false directly, which means that the second print should be false as long as the function’s property Settings are in effect.

console.log(Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance));   
/ / {
// configurable: false,
// enumerable: false,
// value: function [Symbol.hasInstance]() { [native code] },
// writable: false
// }

var a = function() {}
console.log(Object.getOwnPropertyDescriptor(a, Symbol.hasInstance)) // undefined

a[Symbol.hasInstance] = function() {
    return false;
};
console.log(Object.getOwnPropertyDescriptor(a, Symbol.hasInstance)) // undefined
Copy the code

Code portal

At first I was a little confused, but then I realized that the function object’s prototype had set the writable of the symbol.hasinstance property to false, which was well verified by the code above. DefineProperty () is an attribute attribute set on a stereotype, but objects that inherit from the stereotype cannot set this attribute.

The attribute characteristic value that can be used

The following list of attributes that might be used:

  1. Signals: Specifies whether the characteristic values of this property can be changed, and the default is false
  2. Enumerable: Describes whether the property is enumerable, as in for… In the for… Of whether the property can be traversed. The default is false
  3. Value: Describes the value returned when obtaining the value of this attribute. Default is undefined
  4. Writable: Describes whether the value of this property can be reassigned by =. The default is false
  5. Get: Describes the function called to get the value of this property and returns the result of the function. Default is undefined
  6. Set: Describes the function to be called when this property is assigned a value. The default is undefined

The properties of objects can be divided into two categories. One is the properties that directly set values, called data descriptors, which we use most of the time. The other is access descriptors, which are properties described by setter-getter pairs. In fact, these two types of attributes are generated because the characteristic values of the attributes used are different.

General eigenvalues

The above 1,2 eigenvalues are eigenvalues that can be used by both object attributes

configurable

By default, the value is false. That is to say, if the value is not set to true, the characteristic value of this attribute cannot be defined any more, and an error will be reported if it is defined again. For details, please refer to the following code and the running result.

var a = {};
Object.defineProperty(a, 'confName', {
    configurable: true.value: 3
});
console.log('a.confName characteristic description value '.Object.getOwnPropertyDescriptor(a, 'confName'));
Object.defineProperty(a, 'confName', {
    value: 5
})
console.log(The value of 'a.c onfName', a.confName);  / / 5

Object.defineProperty(a, 'name', {
    value: 3
});
console.log('A.name's characteristic description value'.Object.getOwnPropertyDescriptor(a, 'name'));

Object.defineProperty(a, 'name', {
    value: 5
}); // If no accident happens, this sentence will return an error

Copy the code

Code portal

Tips: There is a small detail about Writable in this code, which is explained in the data Descriptors section.

enumerable

The eigenvalue does not say much more than define whether the attribute is enumerable. To put it crudely, when we use for… Of, for… Whether the property can be obtained by means of iterating object properties, such as in. If true, it is enumerable; if false, it is not enumerable.

Data descriptor

An object property is a data descriptor if its eigenvalue uses value or writable.

value

Value describes what value the property is, and its value can be any data type specified in JS.

writable

This is an interesting eigenvalue, which can describe whether the value of the data descriptor can be used. If the value is true, it can be changed, and if the value is false, it cannot be changed. The default value is false in the data descriptor.

Setting writable to false disables any modification of the property and works without any additional information. The writable is configured with a different information system and works with false. The above points out that the value can be modified using a. Assignment, which works without any additional code. In addition to this, we can change the value of the property using the different code example. Please refer to the following code:

var b = {};
Object.defineProperty(b, 'name', {
    configurable: true.value: 3
});
b.name = 9;
console.log('reassign b.name with =', b.name);   // 3 The object attribute is not available. No error is reported when the assignment value is changed, but the value does not change

Object.defineProperty(b, 'name', {
    value: 9
});
console.log('Reassign b.name using definePeoperty', b.name);    // 9 was assigned successfully
Copy the code

Code portal

Access descriptor

An object property is an access descriptor if its eigenvalue uses get or set.

This in get and set

It is worth noting that when we use this in get and set, this binds to the object that is currently using the property. It is not necessarily the object that sets the value, that is, the name of one object a sets get and set. This object is a prototype of another object B, so when the name property is set on b, the “this” in the set function is bound, not the “A” that sets the eigenvalue. Here’s the code:

var c = {};
Object.defineProperty(c, 'name', {
	get: function() {
		return this.test
	},
	set: function(value) {
		this.test = value; }});var b = {};
Object.setPrototypeOf(b, c);
b.name = 'b';   // The set function of the name attribute is called
                // Assigns 'b' to this.test, and this binds to b
                // Set the test property on b and assign it to 'b'

console.log('b.name', b.name);  // b this result occurs because the get function with the name attribute is called
                                // Get returns this.test and this is bound to b
console.log('b.test', b.test);  // b The test property is set on the b object
console.log('c.name', c.name);  // undefined
console.log('c.test', c.test);  // undefined

var s = 's';
var d = {};
Object.defineProperty(d, 'name', {
	get: function() {
		return s
	},
	set: function(value) {
		s = value
	}
});
var e = {};
Object.setPrototypeOf(e, d);
e.name = 'e';   // The set function is called, which assigns s to 'e'.

console.log(The value of 's', s);    // The value of the 'e' variable s is set to 'e' because of the call to set.
console.log('e.name', e.name);  // 'e' where the name attribute is not on object e
                                // It is the value returned by the get call on the property of its prototype object D queried by the delegate
console.log('d.name', d.name);  // 'e' where the name attribute is real on object d
Copy the code

Code portal

Set.call (b, value); set.call(b, value); set.call(b, value); But the function called is still the set function of the property on the object prototype.

Cannot be used simultaneously

A descriptor is considered a data descriptor if it does not have any of the keywords value,writable, GET, and set. If a descriptor has both (value or writable) and (get or set) keywords, an exception will be raised.

Inheritance of eigenvalues

The eigenvalues of an object’s property Settings are likely to be inherited by the object from which they are modeled, and may affect both assignment and retrieval.

Value and Writable inheritance

Inheritance means that when an object sets a property, the setting of that property is affected by the eigenvalue of the property set on its prototype object.

When writable is set to false, objects modeled after the object on which this property is set will not be able to set this value. This will not generate an error, but the property setting will remain invalid.

var f = {};
Object.defineProperty(f, 'name', {
	value: 'f'
});
var g = {};
Object.setPrototypeOf(g, f);

console.log('g.name', g.name);  // 'f'
g.name = 'g';
console.log('g.name', g.name); // 'g'
Copy the code

Code portal

Only when writable is set to false, writable behaves inheritance-and when writable is true, it has no effect on the objects it is modeled on. Usually we directly set the object property writable is true, recall that there is no normal use of object property cannot be set.

var a = {
	name: 'a'
}
a.id = 'a';
console.log(Object.getOwnPropertyDescriptor(a, 'name'))
//configurable: true
//enumerable: true
//value: "a"
//writable: true
console.log(Object.getOwnPropertyDescriptor(a, 'id'))
//configurable: true
//enumerable: true
//value: "a"
//writable: true
var b = {};
Object.setPrototypeOf(b, a);
console.log(b.name);    // 'a'
b.name = 'b';
console.log(b.name);    // 'b'
console.log(a.name);    // 'a'

Copy the code

The inheritance of value is mainly reflected in the inheritance of prototype chain (that is, prototype inheritance, you can refer to my other article understanding prototype is actually understanding prototype chain).

Inheritance of set and GET

The set and GET eigenvalues of a property are also inheritable, and unlike writable’s inheritance, set and GET are always inherited. As long as the object is modeled after the object with the property value set, the object will call the get and set functions on the prototype when setting or obtaining the value of the property value.

var hValue;
var h = {};
Object.defineProperty(h, 'name', {
	set: function(value) {
	    console.log('h set call ');
	    hValue = value;
	},
	get: function() {
	    console.log('h get call ')
	    returnhValue; }});var i = {};
Object.setPrototypeOf(i, h);
i.name = 'i';   // call set on h print 'h set call '
i.test = 'testi';   
console.log('hValue', hValue);  // 'i'
console.log('i.name', i.name);  // 'I' will call get on h print 'h get call '
console.log('h.name', h.name);  // 'I' will call get on h print 'h get call '

var jValue;
var j = {};
Object.defineProperty(j, 'name', {
	get: function() {
	    console.log('j get call ')
	    returnjValue; }});var k = {};
Object.setPrototypeOf(k, j);
k.name = 'k';   // call set on h print 'h set call '
k.test = 'testk';   
console.log('jValue', jValue);  // 'k'
console.log('k.name', k.name);  // 'k' will call get on h print 'j get call '
console.log('j.name', j.name);  // 'k' will call get on h print 'j get call '

Copy the code

Code portal

H is the prototype object of I. The name property of H sets the get and set functions, which will make it unusable on the object of I. When assigning a value to the name attribute, only the name on H will be found through the prototype delegate when obtaining the value of the name attribute. If you define a property on I using Object.defineProperty(), then the property can be defined on Object I.

var hValue;
var h = {};
Object.defineProperty(h, 'name', {
	set: function(value) {
	    console.log('h set call ');
	    hValue = value;
	},
	get: function() {
	    console.log('h get call ')
	    returnhValue; }});var i = {};
Object.setPrototypeOf(i, h);
var iValue;
Object.defineProperty(i, 'name', {
	set: function(value) {
	    console.log('I set call');
	    iValue = value;
	},
	get: function() {
	    console.log('I get call')
	    returniValue; }}); i.name ='i';   // call set on I, print 'I set call'
console.log('iValue', iValue);  // 'i'
console.log('i.name', i.name);  // 'I' will call get on I print 'I get call'
console.log('h.name', h.name);  // 'I' will call get on h print 'h get call '
Copy the code

Code portal

Object attributes set with Object.defineProperty() are still subject to stereotype inheritance rules. Attribute values are searched from the Object itself first, and if not, they are searched up the stereotype chain until they reach the top of the stereotype chain, while Object attributes can only be set to the Object itself. Except that the stereotype inheritance is based on the attributes of the child objects that are also defined by Object.defineProperty().

conclusion

Object.defineproperty () defines attributes in a way that provides an interface for programs to define the behavior of attribute accessors.

Value can define the value obtained when using the property accessor to obtain an object property. Writable can define whether it is allowed to use the property accessor to set a property value. Get can define additional operations to be performed when an object property is obtained using a property accessor, and set can define additional operations to be performed when an object property is assigned using a property accessor, such as the use of bidirectional binding in VUE2.

Actually attribute inheritance of characteristic value gets an attribute value in the performance and the prototype inheritance, unlike the prototype prototype inheritance in the use of accessor to object attribute assignment, a property on a prototype object of writable object to false will be their inheritance cause child objects can’t use property accessor for the attribute assignment again, Properties cannot be set on child objects either; The set of a property on the stereotype is inherited by its children, who use the property accessor to set the property on the object and call the set function on the stereotype to complete the assignment. Finally, when the attribute Settings of both the Object stereotype and its children are defined with Object.defineProperty(), the attribute’s eigenvalue is not inherited.