Object.defineProperty or how to make the code a bit better

  • Tutorial
I want to devote this it got to such a wonderful function as Object.defineProperty (and Object.defineProperties). I have been actively using it for about two months, since support for old browsers (including IE8) in the project that I am currently implementing is not required (envy).

As an article on a habr should be, I will give a brief description of what it does. Object.defineProperty adds a new property that has some non-standard behavior for a regular property, and takes three arguments:
  • The object we are modifying by adding a new property
  • Property (string), which, in fact, we want to add
  • Descriptor: an object containing the "settings" of a new property, for example, acessors (getter, setter)

The descriptor may contain the following properties:
  • value (any value: string, function ...) - the value that the determined property of the object will receive (getter and setter in this case cannot be determined)
  • writable (true / false) - is it possible to overwrite the value of the property (accessors are also not available)
  • get (function) - getter (value and writable cannot be defined)
  • set (function) - setter (value and writable cannot be defined)
  • configurable (true / false) - is it possible to override the handle (use Object.defineProperty on the same property)
  • enumerable (true / false) - whether the property will be enumerated via for..in and is available in Object.keys (poor wording)

Example
Content
// Код сперт с MDN
var o = {};
Object.defineProperty(o, "a", {value : 37,
                               writable : true,
                               enumerable : true,
                               configurable : true});

 
var bValue;
Object.defineProperty(o, "b", {get : function(){ return bValue; },
                               set : function(newValue){ bValue = newValue; },
                               enumerable : true,
                               configurable : true});


MDN Object / defineProperty will explain me better . Fortunately, you don’t even need to know English, and so everything is clear.

If you need to define several properties at once, you can use Object.defineProperties, which takes two arguments: an object requiring changes and an object with defined keys.
MDN: Object / defineProperties .

Example
Content
// Код сперт с MDN
Object.defineProperties(obj, {
  "property1": {
    value: true,
    writable: true
  },
  "property2": {
    value: "Hello",
    writable: false
  }
  // etc. etc.
});



Now salt. What did I decide to post it?

Since in the project mentioned above I have to use defineProperty not only actively, but very actively, the code has become, to put it mildly, ugly. The simplest idea came to my mind (how could I not have thought of this before?), To extend the Object prototype, making the code much more compact. Bad tone, you say, litter the Object prototype with new methods.

Where did this opinion come from? Because all objects will inherit this property, which, with the usual modification of the prototype, becomes enumerated in for..in. My heart becomes warm when you recall what I described above, namely, the property of the enumerable descriptor. Indeed, expanding the prototype this way:

Object.defineProperty( Object.prototype, 'logOk' {
	value: function() { console.log('ok') },
	enumerable: false
});

all objects will receive this method, but at the same time it will be non-enumerable (you do not need to use hasOwnProperty every time to check if there is such a property):

var o = {a: 1, b: 2}
for( var i in o ) console.log( i, o[ i ] );
> a 1
> b 2
o.logOk();
> ok


Actually, for what I'm here graphomaniac.

First, we define the define method so that each time, in my opinion, the construction is not overloaded. Second, define an extendNotEnum method that extends an object with non-enumerable properties.

Object.defineProperties( Object.prototype, {
	define: {
		value: function( key, descriptor ) {
			if( descriptor ) {
				Object.defineProperty( this, key, descriptor );
			} else {
				Object.defineProperties( this, key );
			}
			return this;
		},
		enumerable: false
	},
	extendNotEnum: {
		value: function( key, property ) {
			if( property ) {
				this.define( key, {
					value: property,
					enumerable: false,
					configurable: true
				});
			} else {
				for( var prop in key ) if( key.hasOwnProperty( prop ) ){
					this.extendNotEnum( prop, key[ prop ] );
				}
			}
		},
		enumerable: false
	}
});


Using:
var o = { a: 1 };
o.define( 'randomInt', {
	get: function() {
		return 42;
	}
});

o.extendNotEnum({
	b: 2;
});

for( var i in o ) console.log( i, o[ i ] );
> a 1
> randomInt 42

console.log( o.b );
> 2


And she went ... Two more convenient methods:

Object.prototype.extendNotEnum({
	extend: function() {
		var args = Array.prototype.slice.call( arguments );
		args.unshift( this );
		return $.extend.apply( null, args ); // если jQuery надоест, можно просто переписать под себя
	},
	
	each: function( callback ) {
		return $.each( this, callback ); // аналогично
	}
});


o.extend({c: 3}); // тупо добавляет новые свойства в объект
o.each(function( key, value ) {
	// просто повторяет механизм $.each, перебирая все ключи и свойства
});


You can add new properties indefinitely, no one will bite your hand for this.

Output

Play Dandy, write in Javascript, which is getting better and better.

(If you notice a typo or inaccuracy, please contact in a personal)