Thursday, March 26, 2015

Polyfilling Object.create in pre-ES5 Environments

As you might be aware, there are two primary ways of creating a [[prototype]] link between two objects in Javascript:

Using Object.create()

var obj1 = {
  a:2
};

var obj2 = Object.create(obj1);

console.log(obj2.a); //2

Using a function as a constructor

function Obj1(){
  this.a = 2;
}

var obj2 = new Obj1();

console.log(obj2.a); //2

In both cases, obj2.a will return a value of 2, although in the first case the property 'a' actually lives on obj1 and is found through [[prototype]] delegation, whereas in the second, "constructed" case, the property 'a' actually lives on obj2.

Generally speaking, it is somewhat cleaner to use Object.create() to link an object to another as opposed to making a function call as a construction (imagine if Obj1() had some sort of side effects in addition to just returning an object). However, in some older environments it is necessary to create a partial polyfill of Object.create(), which can be accomplished very easily with the following code snippet:

if( !Object.create ){
  Object.create = function(o){
    function F(){};
    F.prototype = o;
    return new F();
  };
}

In line one we check to see if the function already exists, if not, we begin defining it in line two by creating the 'create' property on Object and assigning it a function that takes an object as its argument. in line three, we create a throw-away function called F, and set its prototype object to the parameter 'o'. We then return a new instance of F(), which will be linked to 'o', since that is what we reassigned F's prototype object to reference.

Pretty simple polyfill hack. Some developers argue that this isn't a best practice since we haven't technically completely polyfilled Object.create. Object.create an also take an option object of arguments that will be assigned to the object that it creates, such as:

var obj2 = Object.create(obj1, { b:{},c:{} });

In this case, we have to define each of these properties by hand, such as setting b's enumerable, writable, configurable descriptors, etc. This is not supported in pre ES5 browsers, but this form is very rarely used, so most developers are okay with the partial polyfill, but if it bothers you, it's okay to simply create a different function that handles the partial polyfill, such as:

function object_create_link(o){
  function F(){};
  F.prototype = o;
  return new F();
}

var obj1 = {
  a:2
};

var obj2 = object_create_link(obj1);

obj2.a; //2

That's a wrap. I like this example because it provides a nice discussion of how Object.create() and functions as constructors differ in creating an object.

Thanks for reading!
-nate

No comments:

Post a Comment