Prototype OO with Object.create and named constructors

I come to Javascript from the background in Python and Smalltalk, and I appreciate the Self and Lisp line in the language. With ECMAScript5, I wanted to try my hand at prototype OO without a new operator.

Limitations:

  • optional new operator for creating classes Prototype chain
  • must be correct for instance
  • named constructors to support WebInspector debugging
  • alloc (). Init () creation sequence such as Objective-C and Python

Here is my implementation attempt to fulfill the criteria:

function subclass(Class, Base) { "use strict"; function create(self, args) { if (!(self instanceof this)) self = Object.create(this.prototype); var init = self.__init__; return init ? init.apply(self, args) : self; } if (Base instanceof Function) Base = Base.prototype; else if (Base===undefined) Base = Object.prototype; Class.prototype = Object.create(Base); Class.prototype.constructor = Class; Class.create = create; Class.define = function define(name, fn) { return Class.prototype[name] = fn; }; Class.define('__name__', Class.name); return Class; } 

And this looks like a simple layout:

 function Family(){return Family.create(this, arguments)} subclass(Family, Object); Family.define('__init__', function __init__(n){this.name=n; return this;}); function Tribe(){return Tribe.create(this, arguments)} subclass(Tribe, Family); function Genus(){return Genus.create(this, arguments)} subclass(Genus, Tribe); function Species(){return Species.create(this, arguments)} subclass(Species, Genus); 

Using the class as a factory function:

 var dog = Species('dog'); console.assert(dog instanceof Object); console.assert(dog instanceof Family); console.assert(dog instanceof Tribe); console.assert(dog instanceof Genus); console.assert(dog instanceof Species); 

Or using the new operator:

 var cat = new Species('cat'); console.assert(cat instanceof Object); console.assert(cat instanceof Family); console.assert(cat instanceof Tribe); console.assert(cat instanceof Genus); console.assert(cat instanceof Species); console.assert(Object.getPrototypeOf(dog) === Object.getPrototypeOf(cat)) 

Have I really forgotten about the necessary capabilities of prototype OO in my implementation? Are there any Javascript conventions or interactions for which I have to make changes? So what are the “gains” here, and are there any obvious improvements that need to be made?

I wanted to be DRYer with constructor definitions, but I found that the function name property is not writable, and that is what supports the names of the WebKit Inspector objects. I was able to build eval to accomplish what I wanted, but ... yuck.

+7
source share
4 answers

Edit: Oh, I see a question now. Answer: no, you understood it correctly. The only way to name the function is to use function declarations, which means evaluation time. So you should have it in your source code (which eval is the back door). I answered a simpler question earlier, but with the same principle: A minor flaw with the prototype inheritance of Crockford . Another resource on this topic is http://kangax.github.com/nfe/

There is a movement to use the displayName property to parse the immutable function name from the appearance of the debugger. This is implemented in Firefox and some other things, and is a straw for inclusion in es6, but is not yet part of the trial specification: http://wiki.ecmascript.org/doku.php?id=strawman:name_property_of_functions

Here's an article from some people working in Chrome about the topic of naming functions http://querypoint-debugging.googlecode.com/files/NamingJSFunctions.pdf

And here is the chrome problem discussing why it hasn't been implemented yet: http://code.google.com/p/chromium/issues/detail?id=17356

To the original answer:

What you intended to accomplish, you did it well. A few examples of similar materials that I made:

At first it is a simple “inherited” function that allows you to do things like:

 var MyObjCtor = heritable({ constructor: function MyObj(){ /* ctor stuff */}, super: SomeCtor, prop1: val, prop2: val, /**etc..*/ }); function heritable(definition){ var ctor = definition.constructor; Object.defineProperty(ctor, 'super', { value: definition.super, configurable: true, writable: true }); ctor.prototype = Object.create(ctor.super.prototype); delete definition.super; Object.keys(definition).forEach(function(prop){ var desc = Object.getOwnPropertyDescriptor(definition, prop); desc.enumerable = false; Object.defineProperty(ctor.prototype, prop, desc); }); function construct(){ var obj = new (ctor.bind.apply(ctor, [].concat.apply([null], arguments))); ctor.super.call(obj); return obj; } construct.prototype = ctor.prototype; return construct; } 

Another way is to create structures for use with libffi (node ​​-ffi). In essence, here you have both constructor inheritance and prototype inheritance. You create constructors that inherit from constructors that instantiate structures. https://github.com/Benvie/node-ffi-tools/blob/master/lib/Struct.js

Using eval is required to create a named function, so if you need a named constructor. I do not hesitate to use it where necessary.

+8
source

Shane. I can say that you come from your background. Javascript behaves differently from what you are trying to do. Before you immediately hate this answer, read on ...

I love OOP and get a very friendly image of OOP. It took me a while to wrap my head around Prototype objects (think of prototype methods as static functions ... that only work with initialized objects). Many things that are done in javascript are very “wrong”, based on a language that uses good protection and encapsulation.

The only real reason to use the “new” operator is to create multiple instances or try to prevent a certain type of logic from starting until the event fires.

I know this is not an answer ... there were too many to enter comments. To get a better perspective, you have to create an object and vars and look at your DOM ... you will realize that because of this you can literally exit any context. I found these articles very helpful in filling out the details.

Good luck.

UPDATE

So, if you need a factory method to create different instances of the same object, here are some tips I would like to hear:

  • Please, please forget about all the rules that you learned from the encapsulation of OO that you know. I say this because when using your proto-objects, literals and instance objects, there will be so many ways to access things in a way that is not natural to you.
  • If you plan on using some type of abstraction or inheritance, I would strongly suggest playing with the DOM. Create some objects and find them in the DOM and see what happens. You can simulate your inheritance using existing prototypes (this killed me without extending interfaces and abstract classes!).
  • Know that everything in javascript is an object ... it allows you to do very cool things (pass functions around, return functions, create simple callback methods). Take a look at the javascript calls to .call () ','. Apply () 'and'.bind ()'.

Knowing these two things will help you decide how you decide. To keep the DOM clean and follow good javascript development practice, you should clear the global namespace (link to the window in the DOM). It also makes you feel “better” in everything that you feel “wrong” with what you are doing. And be consistent.

These are the things that I learned from trial and error. Javascript is a great and unique language if you can wrap your head in contrast to the classic OO languages. Good luck

+4
source

This may not answer your question, and it probably does not do whatever you want, but it is another way to get around JavaScript OO. I like this mainly because of the syntax; it's easier for me to peek, but I don’t have your background.

 // ------- Base Animal class ----------------------- var Animal = function(name) { this.name = name; }; Animal.prototype.GetName = function() { return this.name; }; // ------- End Base Animal class ----------------------- // ------- Inherited Dog class ----------------------- var Dog = function(name, breed) { Animal.call(this, name); this.breed = breed; }; Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; Dog.prototype.GetBreed = function() { return this.breed; }; Dog.prototype.Bark = function() { alert(this.GetName() + ' the ' + this.GetBreed() + ' says "Arf!"'); }; // ------- End Inherited Dog class ----------------------- // Usage var harper = new Dog('Harper', 'miniature pinscher'); harper.Bark(); 
+3
source

You cannot create named constructors without eval. All you will find is a constructor with a hidden property with a name if the library implements namespaces.

Answering the remaining question, there are many javascript class libraries. You can write your own, but a little randomly understand and implement the right inheritance system if you want to do it right.

I recently wrote my own class library, trying to improve all libraries: Classful JS . I think it’s worth a look, because its simple design, use, and some other improvements, such as calling any super method from a subclass (I have not seen a single library that can do this, all of them can only call the super method, overlap).

+1
source

All Articles