Literal inherit from a literal?

I know this can be cheated in some browsers by setting proto, but what is the best way to setup one literal to be the prototype for another?

So far I can only think of making a copy into a constructor. Is there a more efficient way?

var meths = { 
  // creates a constructor function populated with a shallow copy of child
  // the prototype for the new instance is set to the parent.  
  extend : function(parent, child){
    var F = function(){
      for (var prop in child){
	    if (child.hasOwnProperty(prop)){
		  this[prop] = child[prop];
		}
	  }
    }
    F.prototype = parent;
    return new F();
  },
  
  bind : function(func, context){
    return function(){
      func.apply(context, arguments);
    }
  }
};

Test:

var timerTest = meths.extend( meths, { 
  bindFN : function(func, context){
	return this.bind(func, context)
  },
  
  setTimer : function(){
	var o = this, x = 0, timer;
    var boundFN = o.bindFN(timerGo, this);
	// a very simple setTimeout counter.
	function timerGo(){
	  console.log(x++);
	  timer = setTimeout(boundFN, 500);
	  if (x > 5) { clearTimeout(timer); timer = null; }
	}
	boundFN();
  }
});

console.log (timerTest);
timerTest.setTimer();

Thanks

RLM

I think that it may be about the only way, but there are ways to encapsulate such techniques in to first-class functions that help to automate the process.

Crockford was talking about this recently in this video:
Crockford on JavaScript – Act III: Function the Ultimate ([url=“http://www.slideshare.net/douglascrockford/crockford-on-javascript-act-iii-function-the-ultimate”]slides)

Page 53 of his slides starts off the protypal inheritence part, with page 54 of the slide showing the new_constructor function.

That new_constructor() function only works though in the highly rarified atmosphere of JavaScript version 1.85.

I’ll post shortly with a solution that works across todays web browsers.

Here’s how to get a new_constructor function working on todays modern web browsers.

First, you’ll need some compatibility code for Object.create() and [url=“https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach”]Array.forEach()


if (typeof Object.create != "function") {
  Object.create = function (o) {
    if (arguments.length > 1) { 
      throw Error('second argument is not supported'); 
    }
    function F(){}
    F.prototype = o;
    return new F;
  };
}
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fun /*, thisp */) {
        "use strict";
        if (this === void 0 || this === null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== "function") {
            throw new TypeError();
        }
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in t) {
                fun.call(thisp, t[i], i, t);
            }
        }
    };
}

The compatibilty code for Object.key() I wasn’t able to get working on the methods, so I’ve updated new_constructor() to use for…in instead, with a hasOwnProperty check.

This works on all modern web browsers. Internet Explorer has trouble where the method replaces a pre-existing method, such as toString. I don’t know how to fix that for IE yet.



function new_constructor(extend, initializer, methods) {
    var key,
        func,
        prototype = Object.create(extend && extend.prototype);
    if (methods) {
        for (key in methods) {
            if (methods.hasOwnProperty(key)) {
                prototype[key] = methods[key];
            }
        }
    }
    func = function () {
        var that = Object.create(prototype);
        if (typeof initializer === 'function') {
            initializer.apply(that, arguments);
        }
        return that;
    };
    func.prototype = prototype;
    prototype.constructor = func;
    return func;
}

Now you can create new objects, the gizmo, or create other ones that inherit from other objets, the hoozit.



var gizmo = new_constructor(Object, function (id) {
    this.id = id;
}, {
    toString: function () {
        return "gizmo " + this.id;
    }
});

var hoozit = new_constructor(gizmo, function (id) {
    this.id = id;
}, {
    test: function (id) {
        return this.id === id;
    }
});
var giz = gizmo('giz');
var hoo = hoozit('hoo');
alert(hoo.test('hoo'));

Well done again pwm, fair play to you.

A lot to work through there:D

I actually ended up on John Resig’s page http://ejohn.org/blog/ecmascript-5-objects-and-properties/ trying to figure out the internals of object.create etc.

One implementation he gives is this

Object.create = function( proto, props ) {
  var ctor = function( ps ) {
    if ( ps )
      Object.defineProperties( this, ps );
  };
  ctor.prototype = proto;
  return new ctor( props );
};

Object.defineProperties is interesting. However if that newer method is available then wouldn’t the generic Object.create also be available.

The other thing that stood out to me from Crockfords code is

(extend && extend.prototype)

extend can’t be an object literal then?

Will have another look at this in the morning.

Thank you very much

RLM

The compatibilty code for Object.key() I wasn’t able to get working on the methods, so I’ve updated new_constructor() to use for…in instead, with a hasOwnProperty check.

That seems right to me.

The compatibility code I found uses a for in loop anyway and pushes it into an array. You then loop through that array with for each. So basically 2 loops.

RLM

That’s right it would. It doesn’t make much sense that code form John Rezig, which is a shame because normally I hold him in high esteem.

No it can’t. The purpose of Crockfords new_constructor is to construct a new object by prototyping from either the universal Object itself, or from some other object.

You can though create a new object using an object literal to contain its properties and methods, by using the gizmo example from the code.


var gizmo = new_constructor(Object, function (id) {
    this.id = id;
}, {
    property1: 'foo',
    property2: 'bar',
    method1: function () {
        return "gizmo " + this.id;
    },
    method2: function () {
        ...
    }
});

It’s not possible to extend directly from object literals, as they don’t have prototypes or constructs just by themself, which I have just realized, is also why the Object.keys() compatibility code doesn’t want to work with the object literals in the methods variable.

I’m going to come back to the Douglas Crockford code, but for starters I’m interested in the array forEach compatibility code posted.

I’ve been looking through the ECMA docs and can see it ties in with that, but still a few questions.

First void 0? Not really clear on void.

Second why Object(this)?
1.
Let O be the result of calling ToObject passing the this value as the argument.

Third var len = t.length >>> 0;
I’m guessing that shift right with zero fill is doing this 3. Let len be ToUint32(lenValue).

Why do you need to convert length to a 32 bit?

RLM

It just means that it’s not provided. You’ll often see ancient javsacript code setting the onclick event to “javascript:void(0)”

ECMAScript 262 is not just for JavaScript, but for other things as well. The convert length part for example is in direct response to an issue brought up by Andrew Hunt in relation to VoiceXML
http://www.w3.org/TR/2006/WD-voicexml21-20060915/voicexml21-disp.html#R114-4

By following the specifications from ECMAScript 262 Version 5, you’re guaranteed that the code will work all the time as expected across different environments, even if they may not have much effect on the domain of JavaScript itself.

Ok thanks for that.

By following the specifications from ECMAScript 262 Version 5, you’re guaranteed that the code will work all the time as expected across different environments, even if they may not have much effect on the domain of JavaScript itself.

Sure that makes sense.

I’ll check out the link you provided as well.

Cheers

RLM