How to safely define a custom property on prototype in Javascript

·

4 min read

Hello world citizens,

I want to share some code snippets about defining properties in Javascript. But first thing first WHY? Why I want to do such a thing? Here comes your answer my friend :)

  • We may need a property to return our array's last element. Because we don't want to type array[array.length - 1 ] every time. We just want to get last element as array.last
  • We may need an orderBy or orderByDesc function that will get a function as parameter , sort according to this function and then return brand new array without changing my original array(Remember Javascript original sort method mutates the source array, we do not want this behavior.)

... and so on.

Sounds good right? Just like Ruby language. Let's see the code.

  • I want to a property called last to get my last element in a array
if(!Array.prototype.last){
    Object.defineProperty(Array.prototype,"last",{
        get(){
            return this.length > 0 ? this[this.length - 1] : undefined 
        }
    })
}

var numbers = [1,2,3,4,5,6,7,8,9,10];
console.log(numbers.last); // 10

Take a look to if check. It is known as Guard clause. Crucial to add, because we do not want to pollute global namespace if there is already defined a property called last(Maybe defined in a third party library, or a new shinny feature that will come with ES2099 :) )

  • Or we want a reject function which rejects elements that satisfies a given criteria (I am sure Rubyists will understand much better :)
if(!Array.prototype.reject){
   //With object define property writable,configurable,enumerable
  // props are set false by default
    Object.defineProperty(Array.prototype,"reject",{
        value(predicate){
            return this.filter(el => !predicate(el))
        }
    })
}

const users =  [
    {
        "id": 1,
        "username": "cambot",
        "favorite_color": "red"
    },
    {
        "id": 2,
        "username": "gypsy",
        "favorite_color": "purple"
    },
    {
        "id": 3,
        "username": "tom",
        "favorite_color": "red"
    },
    {
        "id": 4,
        "username": "crow",
        "favorite_color": "gold"
    },
    {
        "id": 5,
        "username": "joel",
        "favorite_color": "red"
    },
    {
        "id": 6,
        "username": "mike",
        "favorite_color": "blue"
    },
    {
        "id": 6,
        "username": "jonah",
        "favorite_color": "yellow"
    }
]

users.reject(u => u.favorite_color === "red");

/*-> [
    {
        "id": 2,
        "username": "gypsy",
        "favorite_color": "purple"
    },
    {
        "id": 4,
        "username": "crow",
        "favorite_color": "gold"
    },
    {
        "id": 6,
        "username": "mike",
        "favorite_color": "blue"
    },
    {
        "id": 6,
        "username": "jonah",
        "favorite_color": "yellow"
    }
]
*/
  • Or we want to add a where method which will do same job with filter. For some people(Hi there C#,LINQ) where method is much more convenient so lets add it to the array.
if(!Array.prototype.where){    
    Object.defineProperty(Array.prototype,"where", {
        //default to false. If false you can not change or 
        //delete this property from our prototype
        configurable: false, 

         //default to false. If false you can not assign our
         //property to a new value
        writable: false,

        //default to false. If false you will not see this prop in 
        //enumeration process(for, for in, for of ...)
        enumerable: false,
        value(predicate){
            return this.filter(predicate)
        }
    })
}


const numbers = [1,2,3,4,5,6,7,8,9,10]
console.log(numbers.where(num => num > 4)); // [5,6,7,8,9,10]

const isEven = n => n % 2 === 0 ;
console.log(numbers.where(isEven));  // [2,4,6,8,10]
  • Or we want to an orderBy function to order array according to the provided function without mutating original array
if(!Array.prototype.orderBy){
    Object.defineProperty(Array.prototype,"orderBy", {
         // we can assign our orderBy property to another
        // function later on if we want
        writable: true,
        value(predicate){
            //get copy array to prevent original array modification !!!
            const copyArray = this.slice();
            return copyArray.sort((val1,val2) => {
                const firstValue = predicate(val1);
                const secondValue = predicate(val2);
                if(firstValue == null || secondValue == null){
                    return 0;
                }
                if(firstValue === secondValue){
                    return 0;
                }
                return firstValue > secondValue ? 1 : -1;
            })
        }
    })
}
  • Or orderByDesc function which does the above job but in reverse order
if(!Array.prototype.orderByDesc){
    Object.defineProperty(Array.prototype,"orderByDesc", {
        writable: true, 
        value(predicate){
            //get copy array to prevent original array modification.
            const copyArray = this.slice();
            return copyArray.orderBy(predicate).reverse()
        }
    })
}


var people = [
        {name:"chety", salary: 20_000},
        {name:"rodik", salary: 5_000_000},
        {name:"mirko", salary: 500_000},
]
//sort people by their name ascending(From A to Z)
//console.log(people.orderBy(p => p.name)) 

//sort people by their salary ascending(From Smaller to Bigger)
//console.log(people.orderBy(p => p.salary)) 


//sort people by their salary descending
console.log(people.orderByDesc(p => p.salary)) 
console.log(people) ;

That is it for today everyone. Please do not hesitate comment, share ... Have a nice day with JS.

Did you find this article valuable?

Support Chety's Blog by becoming a sponsor. Any amount is appreciated!