Objecte. DefindPropertype method

The objecte.defindProperType method can directly define a new property on an object, or modify existing properties of an object, and eventually return the object.

grammar

Object.defineProperty(obj, prop, descriptor)

Parameters:

Obj The object whose attributes are defined or modified;

Prop The name of the property to define or modify;

Descriptor description of a property;

The return value

The obj function returns the object itself that was passed to it.

Descriptor description

This method allows developers to define and modify object attributes precisely. Properties built by adding properties through normal assignment are used by enumerator methods (such as for.. In loop or the object.keys method), causing the property value to be changed or deleted by an external method. Object.defineproperty () avoids the situation described above. By default, attributes added through Object.defineProperty() are immutable by default. Attribute descriptor parameters consist of two parts: data descriptor and accessor descriptor. A data descriptor is an object that contains a value for an attribute and states that the value is readable or unreadable; An accessor descriptor is an object that contains a pair of getter-setter methods for that property. A complete property descriptor must be one of the two, and not both.

Data descriptors and accessor descriptors are each objects, and they must contain the following key-value pairs:

The specified property is set to true only when the descriptor of the specified property needs to be modified or deleted by delete, and works without any additional control system. The default is false.

Enumerable only sets a property that requires an enumerable (such as for.. In) is set to true when accessed. The default is false.

Data descriptors can contain the following optional key-value pairs:

Value Sets the value of the property, which can be any JavaScript value type (number,object,function, etc.). The default is undefined.

Writable is set to true only if the value of a property can be modified by an assignment operation. The default is false.

Accessor descriptors can contain the following optional key-value pairs:

Getter method for get property, undefined if property has no getter method. This method returns the value of the property. The default is undefined.

Setter method for a set property, or undefined if the property has no setter method. This method accepts a unique parameter as a new value for the property. The default is undefined.

Keep in mind that the attributes of these descriptors are not required, and attributes inherited from the stereotype chain can also be filled in. To ensure that these descriptor properties are filled with default values, you might use methods such as pre-freezing Object.prototype, explicitly setting the value of each descriptor property, and using Object.create(null) to get empty objects.

// using __proto__var obj = {}; var descriptor = Object.create(null); // No Inherited Properties // The properties of all descriptors are set to the default descriptors.value ='static'; Object.defineProperty(obj,'key', descriptor); // Explicitly set the attribute object.defineProperty (obj,'key', {
  enumerable: false,
  configurable: false,
  writable: false,
  value: 'static'}); // Reuse the same object as a descriptorfunction withValue(value) {
  var d = withValue.d || (
    withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value: null
    }
  );
  d.value = value;  return d;
}Object.defineProperty(obj, 'key', withValue('static')); / / if the Object. The freeze method is available, use it to prevent to modify the attributes of the Object (Object. The freeze | | Object) (Object. The prototype);Copy the code

Use the sample

Create a property

If the current Object does not have the property we want to set, object.defineProperty () creates a new property for the Object based on the method setting. If the descriptor argument is missing, it is set to the default value. All Boolean descriptor properties are set to false by default. Value,get, and set are set to undefined by default. A property that is not set to get/set/value/writable is called a “generic property”, and its descriptor is “classified” as a data descriptor.

var o = {}; // Create an Object // Use the data descriptor to add the attribute Object.defineProperty(o,'a', {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true}); // Use accessor descriptor to add attribute var bValue = 38; Object.defineProperty(o,'b', {
  get: function() { return bValue; },
  set: function(newValue) { bValue = newValue; },
  enumerable: true,
  configurable: true}); o.b; // the property "b" is set to object O with a value of 38. // Now the value of o.b points to the bValue variable, unless O.b is redefined // You can't try to mix the data and accessor descriptors object.defineProperty (o,'conflict', {
  value: 0x9f91102,
  get: function() { return0xdeadbeef; }}); // Throws a type error: value appears onlyin data descriptors, get appears only inAccessor Descriptors (value only in data descriptors, get only in accessor descriptors)Copy the code

Modifying a property

When a property already exists, Object.defineProperty() attempts to modify the property based on the Object’s property configuration and the newly set value. If the 64x property is set to false, it cannot be modified without any additional information. In this case, if the writable is set to true, the writable can be set to false, and any descriptor property becomes unsettable. If the property is set to false, the descriptor of the property cannot be converted between the data descriptor and the accessor descriptor. If the new property is different from the configured property, and the additional property is set to false, a TypeError will be thrown (except for the additional cases mentioned in the preceding paragraph). If the old and new properties are exactly the same, nothing happens.

Writable feature -writable

When writable is set to false, a property becomes “non-writable”. This property cannot be reassigned.

var o = {}; // Create an object.defineProperty (o,'a', {
  value: 37,
  writable: false}); console.log(o.a); // 37o.a = 25; // No errors are thrown // Error console.log(o.a) is thrown in strict mode; // Still 37, assignment is invalidCopy the code

As described in the code above, trying to override a “non-writable” property does not change anything and does not throw an error.

Enumerable features – Enumerable

An attribute’s Enumerable defines whether an object’s property appears in the enumerator (for.. In loop and object.keys ()).

var o = {}; Object.defineProperty(o,'a', {
  value: 1,
  enumerable: true}); Object.defineProperty(o,'b', {
 value: 2,
 enumerable: false}); Object.defineProperty(o,'c', { value: 3}); // Enumerable defaults to falseo.d = 4; // Enumerable is set to Truefor (var I) by setting the property directlyino) { console.log(i); }// Print out 'a' and 'd 'object.keys (o); / / /'a'.'d']o.propertyIsEnumerable('a'); // trueo.propertyIsEnumerable('b'); // falseo.propertyIsEnumerable('c'); // falseCopy the code

Configurable features – Signals

The 64x property controls the implementation of any new object without any additional information, and controls the additional information provided by the 64X and 64X. You can still make a change to change writable to false.

var o = {}; Object.defineProperty(o,'a', {
  get: function() { return 1; },
  configurable: false}); Object.defineProperty(o,'a', {
  configurable: true}); // Throw the object.defineProperty (o,'a', {
  enumerable: true}); // Throw the object.defineProperty (o,'a', {
  set: function() {}}); // Throw an error (setUndefined) object.defineProperty (o,'a', {
  get: function() { return1; }}); // Throws an error (even though the new GET does the same thing, but the method references before and after are different)Object.defineProperty(o,'a', { value: 12}); // Throw error console.log(o.a); // 1delete o.a; // Nothing happens console.log(o.a); / / 1Copy the code

If the O.A property is configured with true, no errors will be thrown and the O.A will be deleted in the final delete operation.

Default value when adding attributes

It is important to consider how the default values for the descriptor properties are applied. As shown in the following example, simply use “. There is a big difference between setting a property using the symbol and using Object.defineProperty().

var o = {}; o.a = 1; // Equivalent to: object.defineProperty (o,'a', {  value: 1,
  writable: true,
  configurable: true,
  enumerable: true}); // On the other hand, object-defineProperty (o,'a', { value: 1 }); // Equivalent to: object.defineProperty (o,'a', {  value: 1,
  writable: false,
  configurable: false,
  enumerable: false});Copy the code

Custom Setters and Getters

The following example shows how to implement a “self-archiving” object. When temperature is set, a log record is added to the Archive array.

function Archiver() {
  var temperature = null;  var archive = [];

  Object.defineProperty(this, 'temperature', {    get: function() {
      console.log('get! ');      return temperature;
    },    set: function(value) { temperature = value; archive.push({ val: temperature }); }}); this.getArchive =function() { return archive; };
}var arc = new Archiver();
arc.temperature; // 'get! 'arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]Copy the code

Or the following would have the same effect:

var pattern = {    get: function () {
        return 'I always return this string, whatever you have assigned';
    },    set: function () {
        this.myname = 'this is my name string'; }};function TestDefineSetAndGet() {
    Object.defineProperty(this, 'myproperty', pattern);
}var instance = new TestDefineSetAndGet();
instance.myproperty = 'test'; console.log(instance.myproperty); // I alwaysreturn this string, whatever you have assignedconsole.log(instance.myname); // this is my name stringCopy the code

.

The object.defineProperty () method is used by many modern front-end frameworks (e.g. vue.js, react.js) to implement two-way data binding. When we set data in the framework Model layer, The framework will bind all the data through the object.defineProperty () method and modify the virtual nodes as the data changes, ultimately modifying the Dom structure of the page. There are a few things to note in this process:

Delay change

Modern frameworks, in order to avoid intensive Dom modification operations, modify bound data with a minimal (typically 1ms) setTimeout delay to reapply the change. That is, there is an idle period between changes in the virtual node and page Dom tree and changes in data. Developers who notice this will realize that if we want to implement a feature where the page changes immediately after a data change and the next developer gets the Dom of that change… This is not possible with a modern front-end framework. Of course, those frameworks also provide a number of solutions, such as Vue’s nextTick() method.

Array changes

Let’s take a look at how Object.defineProperty() tracks array changes:

var a={};
bValue=1;
Object.defineProperty(a,"b", {set:function(value){
        bValue=value;
        console.log("setted");
    },    get:function() {returnbValue; }}); a.b; //1a.b=[]; / / setteda. B = [1, 2, 3]. //setteda.b[1]=10; // no output a.b.ush (4); // No output a.b.length=5; // no output a.b; / / [1,10,3,4, undefined];Copy the code

As you can see, when a.b is set to an array, any changes to the interior of the array that do not reassign a new array object do not trigger the execution of setter methods. This is important because the two-way data binding implemented by modern front-end frameworks based on the object.defineProperty () method also fails to recognize such array changes. So first, if we want to trigger two-way data binding, we don’t use arr[1]=newValue; Such a statement to implement; Second, the framework also provides many ways to bind arrays in both directions. In most cases, the framework overrides the array.prototype. push method and generates a new Array assignment to the data so that two-way binding is triggered. As framework users, we need to know that the array changes implemented in this way consume more memory.

Configurable and writable

The writable, which is set to true, is the only property that can be changed if the 64x is set to false. I tested the following (the following code runs the argument in Chrome and IE, with the same output) :

var a={};
Object.defineProperty(a,"o",{
    configurable:false,    value:10,
    writable:true}); console.log(a.o); //10a.o=12; Console. log(a.o); //12Object.defineProperty(a,"o",{
    configurable:false,    value:14,
    writable:true}); console.log(a.o); //14Object.defineProperty(a,"o",{
    configurable:false,    value:14,
    writable:false}); a.o=16; Console. log(a.o); 14 / / / / an error Object. DefineProperty (a,"o",{
    configurable:false,    value:16,
    writable:false});Copy the code

From the above code, it can be concluded that, in the case of descriptor is data descriptor: 1. Use “.” Operator to set the value of a property is never an error, only if writable is false. The writable, regardless of whether the writable is false, can modify the value of any additional information through object.defineProperty (). Therefore, the object-oriented defineProperty() and standard description implemented by different browser operators are different from each other. Changes in values when descriptors are data descriptors are controlled only by writable.

About this article

The original address: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty