[javascript] how to use javascript Object.defineProperty

I looked around for how to use the Object.defineProperty method, but couldn't find anything decent.

Someone gave me this snippet of code:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
})

But I don't understand it. Mainly, the get is what I can't get (pun intended). How does it work?

This question is related to javascript object defineproperty

The answer is


get is a function that is called when you try to read the value player.health, like in:

console.log(player.health);

It's effectively not much different than:

player.getHealth = function(){
  return 10 + this.level*15;
}
console.log(player.getHealth());

The opposite of get is set, which would be used when you assign to the value. Since there is no setter, it seems that assigning to the player's health is not intended:

player.health = 5; // Doesn't do anything, since there is no set function defined

A very simple example:

_x000D_
_x000D_
var player = {_x000D_
  level: 5_x000D_
};_x000D_
_x000D_
Object.defineProperty(player, "health", {_x000D_
  get: function() {_x000D_
    return 10 + (player.level * 15);_x000D_
  }_x000D_
});_x000D_
_x000D_
console.log(player.health); // 85_x000D_
player.level++;_x000D_
console.log(player.health); // 100_x000D_
_x000D_
player.health = 5; // Does nothing_x000D_
console.log(player.health); // 100
_x000D_
_x000D_
_x000D_


Defines a new property directly on an object, or modifies an existing property on an object, and return the object.

Note: You call this method directly on the Object constructor rather than on an instance of type Object.

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: false, //If its false can't modify value using equal symbol
      enumerable: false, // If its false can't able to get value in Object.keys and for in loop
      configurable: false //if its false, can't able to modify value using defineproperty while writable in false
   });

enter image description here

Simple explanation about define Property.

Example code: https://jsfiddle.net/manoj_antony32/pu5n61fs/


Basically, defineProperty is a method that takes in 3 parameters - an object, a property, and a descriptor. What is happening in this particular call is the "health" property of the player object is getting assigned to 10 plus 15 times that player object's level.


Summary:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
});

Object.defineProperty is used in order to make a new property on the player object. Object.defineProperty is a function which is natively present in the JS runtime environemnt and takes the following arguments:

Object.defineProperty(obj, prop, descriptor)

  1. The object on which we want to define a new property
  2. The name of the new property we want to define
  3. descriptor object

The descriptor object is the interesting part. In here we can define the following things:

  1. configurable <boolean>: If true the property descriptor may be changed and the property may be deleted from the object. If configurable is false the descriptor properties which are passed in Object.defineProperty cannot be changed.
  2. Writable <boolean>: If true the property may be overwritten using the assignment operator.
  3. Enumerable <boolean>: If true the property can be iterated over in a for...in loop. Also when using the Object.keys function the key will be present. If the property is false they will not be iterated over using a for..in loop and not show up when using Object.keys.
  4. get <function> : A function which is called whenever is the property is required. Instead of giving the direct value this function is called and the returned value is given as the value of the property
  5. set <function> : A function which is called whenever is the property is assigned. Instead of setting the direct value this function is called and the returned value is used to set the value of the property.

Example:

_x000D_
_x000D_
const player = {_x000D_
  level: 10_x000D_
};_x000D_
_x000D_
Object.defineProperty(player, "health", {_x000D_
  configurable: true,_x000D_
  enumerable: false,_x000D_
  get: function() {_x000D_
    console.log('Inside the get function');_x000D_
    return 10 + (player.level * 15);_x000D_
  }_x000D_
});_x000D_
_x000D_
console.log(player.health);_x000D_
// the get function is called and the return value is returned as a value_x000D_
_x000D_
for (let prop in player) {_x000D_
  console.log(prop);_x000D_
  // only prop is logged here, health is not logged because is not an iterable property._x000D_
  // This is because we set the enumerable to false when defining the property_x000D_
}
_x000D_
_x000D_
_x000D_


_x000D_
_x000D_
Object.defineProperty(Array.prototype, "last", {_x000D_
  get: function() {_x000D_
    if (this[this.length -1] == undefined) { return [] }_x000D_
    else { return this[this.length -1] }_x000D_
  }_x000D_
});_x000D_
_x000D_
console.log([1,2,3,4].last) //returns 4
_x000D_
_x000D_
_x000D_


Object.defineProperty() is a global function..Its not available inside the function which declares the object otherwise.You'll have to use it statically...


_x000D_
_x000D_
import { CSSProperties } from 'react'_x000D_
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'_x000D_
_x000D_
export const COLOR_ACCENT = BLUE_x000D_
export const COLOR_DEFAULT = BLACK_x000D_
export const FAMILY = "'Segoe UI', sans-serif"_x000D_
export const SIZE_LARGE = '26px'_x000D_
export const SIZE_MEDIUM = '20px'_x000D_
export const WEIGHT = 400_x000D_
_x000D_
type Font = {_x000D_
  color: string,_x000D_
  size: string,_x000D_
  accent: Font,_x000D_
  default: Font,_x000D_
  light: Font,_x000D_
  neutral: Font,_x000D_
  xsmall: Font,_x000D_
  small: Font,_x000D_
  medium: Font,_x000D_
  large: Font,_x000D_
  xlarge: Font,_x000D_
  xxlarge: Font_x000D_
} & (() => CSSProperties)_x000D_
_x000D_
function font (this: Font): CSSProperties {_x000D_
  const css = {_x000D_
    color: this.color,_x000D_
    fontFamily: FAMILY,_x000D_
    fontSize: this.size,_x000D_
    fontWeight: WEIGHT_x000D_
  }_x000D_
  delete this.color_x000D_
  delete this.size_x000D_
  return css_x000D_
}_x000D_
_x000D_
const dp = (type: 'color' | 'size', name: string, value: string) => {_x000D_
  Object.defineProperty(font, name, { get () {_x000D_
    this[type] = value_x000D_
    return this_x000D_
  }})_x000D_
}_x000D_
_x000D_
dp('color', 'accent', COLOR_ACCENT)_x000D_
dp('color', 'default', COLOR_DEFAULT)_x000D_
dp('color', 'light', COLOR_LIGHT)_x000D_
dp('color', 'neutral', COLOR_NEUTRAL)_x000D_
dp('size', 'xsmall', SIZE_XSMALL)_x000D_
dp('size', 'small', SIZE_SMALL)_x000D_
dp('size', 'medium', SIZE_MEDIUM)_x000D_
_x000D_
export default font as Font
_x000D_
_x000D_
_x000D_


defineProperty is a method on Object which allow you to configure the properties to meet some criterias. Here is a simple example with an employee object with two properties firstName & lastName and append the two properties by overriding the toString method on the object.

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
employee.toString=function () {
    return this.firstName + " " + this.lastName;
};
console.log(employee.toString());

You will get Output as : Jameel Moideen

I am going to change the same code by using defineProperty on the object

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: true,
    enumerable: true,
    configurable: true
});
console.log(employee.toString());

The first parameter is the name of the object and then second parameter is name of the property we are adding , in our case it’s toString and then the last parameter is json object which have a value going to be a function and three parameters writable,enumerable and configurable.Right now I just declared everything as true.

If u run the example you will get Output as : Jameel Moideen

Let’s understand why we need the three properties such as writable,enumerable and configurable.

writable

One of the very annoying part of the javascript is , if you change the toString property to something else for example

enter image description here

if you run this again , everything gets breaks. Let’s change writable to false. If run the same again you will get the correct output as ‘Jameel Moideen’ . This property will prevent overwrite this property later.

enumerable

if you print all the keys inside the object , you can see all the properties including toString.

console.log(Object.keys(employee));

enter image description here

if you set enumerable to false , you can hide toString property from everybody else. If run this again you will get firstName,lastName

configurable

if someone later redefined the object on later for example enumerable to true and run it. You can see toString property came again.

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: false,
    enumerable: false,
    configurable: true
});

//change enumerable to false
Object.defineProperty(employee, 'toString', {

    enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));

enter image description here

you can restrict this behavior by set configurable to false.

Orginal reference of this information is from my personal Blog


yes no more function extending for setup setter & getter this is my example Object.defineProperty(obj,name,func)

var obj = {};
['data', 'name'].forEach(function(name) {
    Object.defineProperty(obj, name, {
        get : function() {
            return 'setter & getter';
        }
    });
});


console.log(obj.data);
console.log(obj.name);