JavaScript extend objects and prototypes - javascript

I am creating a framework to simplify the oriented object coding with prototypes. But I am pondering with inheritance in JavaScript.
By default, to extend an object, we write :
var B = function() { /*...*/ } ;
B.prototype = new A() ;
But what about A constructor function requires a parameter ?
var A = function(args) {
if (!args) throw "Arguments required." ;
} ;
Or maybe A constructor function could also perform unwanted things before B was instancied.
What would you suggest to replace the default inheritance behaviour ?
(I thought about storing all the members of all "classes" to copy while inheriting or mixins.)

If you want to inherit from a prototype without calling the constructor, you can use Object.create() to do something like this:
var B = function() { /*...*/ };
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
In the above, Object.create(A.prototype) will return a new object whose prototype is given by A.prototype, and it does this without calling A(). The second line is there so you can look up the constructor property on any instances of B, and it'll point back to B().
One thing to note is that Object.create() is relatively new, so you might need a polyfill for older browsers. You can find one here, along with more info:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

I usually use a defclass utility function to define "classes" in JavaScript:
function defclass(base, body) {
var uber = base.prototype;
var prototype = Object.create(uber);
var constructor = (body.call(prototype, uber), prototype.constructor);
constructor.prototype = prototype;
return constructor;
}
Then I use it as follows:
var A = defclass(Object, function () {
this.constructor: function (arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
this.log = function (which) {
console.log(which ? this.arg1 : this.arg2);
};
});
Inheritance is dead simple:
var B = defclass(A, function (uber) {
this.constructor = function (arg1, arg2, arg3) {
uber.constructor.call(this, arg1, arg2);
this.arg3 = arg3;
};
this.log = function (which) {
uber.log.call(this, which);
console.log(this.arg3);
};
});
As you can see when we are extending a "class" we use Object.create. This is the new way of inheritance. Using new is antiquated. In the constructor of B we pass arguments to the constructor of A using uber.constructor.call.
If you like this pattern then you should take a look at the augment library.

Related

javascript create new object by avoiding new keywords

JavaScript can create a object in many ways.
I try the following code to avoid new keyword to create a new Object of Class A.
My question is that A.prototype.init() here is whether equals new A()? is this good for practice, and why?
function A(){
}
A.prototype.init=function(){
return this;
}
var a = A.prototype.init();
console.log(a);
var a1=new A();
console.log(a1);
jsfiddle
All you're doing is returning the A.prototype object. You're not really initializing anything, and you're not using the result.
console.log(A.prototype === A.prototype.init()); // true
So unless you have a particular use in mind, I'd say, no it's not a good practice.
Not sure exactly why you want to avoid new, but in any case, you can change your constructor so that it can be called with or without new and still behave like a constructor.
function A() {
var ths = Object.create(A.prototype);
ths.foo = "bar";
return ths;
}
Now it won't matter if you use new. You're going to get a new object that inherits from A.prototype no matter what.
You can still use an .init() method, but you might as well just put the logic in the constructor.
Furthermore, you can easily create a factory that takes care of that little bit of boilerplate code.
function Ctor(fn) {
return function() {
var ths = Object.create(fn.prototype);
fn.apply(ths, arguments);
return ths;
};
}
So now you'd create your constructor like this:
var A = Ctor(function() {
this.foo = "bar";
});
You can avoid new by encapsulating your code with the module pattern and returning a function that calls the constructor, in other words:
var A = (function ClassA() {
// Constructor
function A(prop) {
this.prop = prop; // instance property
this._init();
}
// Public methods
A.prototype = {
_init: function() {
}
};
// Mini factory to create new instances
return function(prop) {
return new A(prop); // well, only one `new`
};
}());
Now you can create new instances without new:
var a = A('foo'); //=> A { prop: "foo", init: function }
Usually you catch direct function calls with instanceof:
function MyConstructor (a, b, c) {
if (!(this instanceof MyConstructor)) {
return new MyConstructor(a, b, c);
}
// ...
}
but there is no good reason to avoid using new. Object.create and other alternatives can have a significant performance impact.

prototype chain in javascript

Suppose I have two constructor function:
var Person = function(xx,xx,xxx,xxxxxxx) {
//Person initialization
}
var Man = function(xx,xx,xxx,xxx) {
//Man initialization
}
And I want Man extends from Person.
The following is what I thought:
Given a created Man object:
var m=new Man("xx",....);
1) when a property of m is accessed,it will search it in Man.prototype.
2) If not find, It should find it in Man.prototype.__prop__.
So what I have do is make the Man.prototype.__prop__ linked to Person.prototype.
I know this is the common way:
function inherit(superClass) {
function F() {}
F.prototype = superClass.prototype;
return new F;
}
Man.prototype=inherit(Person);
But when I try this:
Man.prototype.prototype=Person.prototype.
Why it does not work?
It sounds like you actually want to link instances of Man to an instance of Person that retains any properties added in its constructor, in which case you might really want something like this:
function Person(a, b) {
this.a = a;
this.b = b;
}
function Man() {}
Man.prototype = new Person("val1", "val2");
var m = new Man();
console.log(m.a);
...which prints val1.
Additional Ramblings
The purpose of inherit is to create an object from a function whose prototype is that of the given super class (without explicitly using new), which is exactly what it does. Therefore, the following prints string:
function Person() {}
Person.prototype.test = "string";
function Man() {}
function inherit(superClass) {
function F() {}
F.prototype = superClass.prototype;
return new F;
}
var t = inherit(Person);
console.log(t.test);
But you generally want to assign the returned object to another function's prototype:
Man.prototype = inherit(Person);
var m = new Man();
console.log(m.test);
...so that m.test also prints string, which means that objects created using Man are linked to Person's prototype).
Note that Man.prototype.prototype is undefined and -- this is the important part -- also meaningless. Functions have prototypes. Other objects (such as Man.prototype) do not. The prototype property is not magical in any other context. Assigning a value to a random object's prototype property doesn't do anything special. It's just another property.
Note also that the thing we returned from inherit is linked to Person by way of its prototype and has no access to any properties added to instances of Person.
How I do it (in regards to your example):
var Person = function () {
}
// Define "Person" methods
var Man = function () {
}
Man.prototype = new Person();
// Define "Man" methods
UPDATE
Regarding constructors with parameters: just found this SO question that can really help you figure it out (second answer, first part): JavaScript inheritance: when constructor has arguments.
There are quite a few generic methods to do that. I will provide three of them:
1.) Using Function object as a constructor and as a prototype object for inherited new objects. The implementation should look like as the following:
var person = {
toString : function() {
return this.firstName + ' ' + this.lastName;
}
}
function extend(constructor, obj) {
var newObj = Object.create(constructor);
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
newObj[prop] = obj[prop];
}
}
return newObj;
}
var man = extend(person,
{
sex: "male",
age: "22"
});
var name = extend(man,
{
firstName: "Simo",
lastName: "Endre"
});
name.man;
name.toString();
2.) In this case we'll use the Function object as the constructor for simulating the classical inheritance in a language like C# or Java. The prototype property of an object will be used in the role of a constructor and the the newly created object will inherit all the properties from the object prototype root. In this case the object's prototype has only one kind of augmentation role, the effective implementation is done in the function method.
var Person = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.toString = function() {
return this.firstName + ' ' + this.lastName;
}
function inherit(func) {
// passing function arguments into an array except the first one which is the function object itself
var args = Array.prototype.slice.call(arguments, 1);
// invoke the constructor passing the new Object and the rest of the arguments
var obj = Object.create(func.prototype);
func.apply(obj, args);
//return the new object
return obj;
}
var man = inherit(Person, "Simo", "Endre");
man.toString();
3.) The well known inheritance model:
function extend(o) {
function F() {}
// We'll set the newly created function prototype property to the object.
//This act as a constructor.
F.prototype = o;
// Using the `new` operator we'll get a new object whose prototype is o.
return new F();
};

Is there a way to change the prototype from within a function in Javascript?

I have this type of code for prototypal inheritance in Javascript.
function A() {
this.a = 1;
}
function B() {
someEl.innerHTML(this.a);
}
B.prototype = new A();
However, I am wondering if there is a better way to do it, preferably one that brings the prototype declaration into the declaration of B.
I tried setting this.prototype in B, but it just showed this.a as undefined.
So, is there a better way to do this?
The prototype property should be used only in constructor functions, this.prototype doesn't makes sense because this just the new object instance, not the constructor.
Assign the prototype inside the constructor itself isn't a good idea, since that assignment will be executed each time you create a new object (by new B();).
Your code seems perfectly valid to me, the only thing that you should be aware of, is that if you replace the prototype property of a constructor, like you do it with B.prototype = new A(); the constructor property of the object instances of B, will point to A.
Usually is recommended to restore that property, e.g.:
function A() {
this.a = 1;
}
function B() {
someEl.innerHTML(this.a);
}
B.prototype = new A();
B.prototype.constructor = B; // restore the `constructor` property
var foo = new B;
foo.constructor === B; // true
Give a look to:
Constructors considered mildly confusing (article)
Setting javascript prototype function within object class declaration (SO question)
Try:
function A() {
this.a = 1;
}
function B() {
this.prototype = new A();
someEl.innerHTML(this.A.a); //notice A.a
}
function MyClass() {
this.initialize();
}
MyClass.prototype = {
initialize: function() { // adding a function name
this.some_var = "Blue"; // makes debugging easier
},
tell_color: function() {
alert(this.color);
},
color: "red"
}
var instance = new MyClass();
instance.tell_color();
This wasn't my idea, but I don't know anymore where I've found it.
Inheritance in JS is a little weird, but everyone else is correct that you should never put a prototype within a constructor. The point of prototyped functions/vars is so you only have a singular instance of that function for ALL instances of said class to use. Likewise it is good for class vars, a singular value which every instance will reference.
Really, you should make class B inherit from class A:
function B(){
this.object = new A();
}
B.prototype.a = function(){
return object.a;
}
which can be a little annoying since you must then create new getters/setters, buuuuuuuttttt, if you are feeling a little adventurous, you can build something like this......
function breakApart(what){
var functions = [];
var properties = [];
for(property in what){
if(typeof what[property] == 'function'){
functions.push({'name':property,'value':what[property]});
}else{
properties.push({'name':property,'value':what[property]});
}
}
var data = [
{'title':'functions','data':functions},
{'title':'properties', 'data':properties}
];
return data;
}
Add a few eval() calls and some logic and you can make this function build you new getters/setters/function references.

prototype and constructor object properties

I've:
function Obj1(param)
{
this.test1 = param || 1;
}
function Obj2(param, par)
{
this.test2 = param;
}
now when I do:
Obj2.prototype = new Obj1(44);
var obj = new Obj2(55);
alert(obj.constructor)
I have:
function Obj1(param) {
this.test1 = param || 1;
}
but the constructor function has been Obj2... why that?
Obj1 has become the Obj2 prototype...
Can someone explain me, in detail, the prototype chain and the constructor property
Thanks
constructor is a regular property of the prototype object (with the DontEnum flag set so it doesn't show up in for..in loops). If you replace the prototype object, the constructor property will be replaced as well - see this explanation for further details.
You can work around the issue by manually setting Obj2.prototype.constructor = Obj2, but this way, the DontEnum flag won't be set.
Because of these issues, it isn't a good idea to rely on constructor for type checking: use instanceof or isPrototypeOf() instead.
Andrey Fedorov raised the question why new doesn't assign the constructor property to the instance object instead. I guess the reason for this is along the following lines:
All objects created from the same constructor function share the constructor property, and shared properties reside in the prototype.
The real problem is that JavaScript has no built-in support for inheritance hierarchies. There are several ways around the issue (yours is one of these), another one more 'in the spirit' of JavaScript would be the following:
function addOwnProperties(obj /*, ...*/) {
for(var i = 1; i < arguments.length; ++i) {
var current = arguments[i];
for(var prop in current) {
if(current.hasOwnProperty(prop))
obj[prop] = current[prop];
}
}
}
function Obj1(arg1) {
this.prop1 = arg1 || 1;
}
Obj1.prototype.method1 = function() {};
function Obj2(arg1, arg2) {
Obj1.call(this, arg1);
this.test2 = arg2 || 2;
}
addOwnProperties(Obj2.prototype, Obj1.prototype);
Obj2.prototype.method2 = function() {};
This makes multiple-inheritance trivial as well.
Check out Tom Trenka's OOP woth ECMAscript, the "Inheritance" page. Everything from the prototype is inherited, including the constructor property. Thus, we have to unbreak it ourselves:
Obj2.prototype = new Obj1(42);
Obj2.prototype.constructor = Obj2;
Short version: ‘constructor’ doesn't do what you think, and isn't cross-browser compatible. Never use it.
Long version: Convention for prototype inheritance in JavaScript
Generally: you're getting confused due to (a) the impedence mismatch between class-based and prototype-based OO, and (b) the strangeness of JavaScript's particular rather poor interpretation of prototype-based OO.
You'll probably be happier if you find one classes-in-prototypes implementation you like and stick with that. Many libraries have one. Here's an arbitrary one I use:
Function.prototype.subclass= function() {
var c= new Function(
'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
'if (arguments[0]!==Function.prototype.subclass.FLAG && this._init) this._init.apply(this, arguments); '
);
if (this!==Object)
c.prototype= new this(Function.prototype.subclass.FLAG);
return c;
}
Function.prototype.subclass.FLAG= new Object();
And here's an example of how one might use it:
// make a new class
var Employee= Object.subclass();
// add members to it
Employee.prototype._LEGS= 2;
Employee.prototype.getLegs= function() {
return this._LEGS;
};
// optional initialiser, takes arguments from constructor
Employee.prototype._init= function(name) {
this.name= name;
};
// make a subclass
Manager= Employee.subclass();
// extend subclass method
Manager.prototype._init= function(name, importance) {
// call base class's method
Employee.prototype._init.call(this, name);
this.importance= importance;
}
// all managers are well-known to have three legs
Manager.prototype._LEGS= 3;
// create one
var jake= new Manager('Jake the Peg', 100);
Well, the constructor property is a property like any other, on the prototype (property) of Obj1. If you understand how prototypes work, this might help:
>>> obj.hasOwnProperty("constructor")
false
// obj's [[Prototype]] is Obj2.prototype
>>> Obj2.prototype.hasOwnProperty("constructor")
false
// Obj2.prototype's [[Prototype]] is Obj1.prototype
>>> Obj1.prototype.hasOwnProperty("constructor")
true
// Oh?
>>> Obj1.prototype.constructor
Obj1()
Aha! So obj has no constructor, JS goes to get it up the [[Prototype]] chain, all the way from Obj1.prototype.constructor
I'm not sure why the constructor property isn't just set on an object when you use `new'. There might be a reason, or it might just be an oversight. Either way, I tend to avoid it.

Can a JavaScript object have a prototype chain, but also be a function?

function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () { };
c.prototype = a;
var d = new c();
d.b(); // returns "bar"
d(); // throws exception, d is not a function
Is there some way for d to be a function, and yet still inherit properties from a?
Actually, it turns out that this is possible, albeit in a non-standard way.
Mozilla, Webkit, Blink/V8, Rhino and ActionScript provide a non-standard __proto__ property, which allow changing the prototype of an object after it has been created. On these platforms, the following code is possible:
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
return "hatstand";
}
c.__proto__ = a;
c(); // returns "hatstand"
c.b(); // returns "bar"; inherited from a
This might be of use to anyone who doesn't need to worry about cross-platform compatibility.
However, note that only the properties of an object can be inherited. For example:
var d = {};
d.__proto__ = a;
d.b(); // returns "bar"
d(); // throws exception -- the fact that d is inheriting from a function
// doesn't make d itself a function.
Short answer: not possible.
This line of your code:
var d = new c();
automatically assumes that d is an object. Unless c is a constructor of a builtin object, e.g., Function. But if c is already defined by the language, you cannot manipulate its prototype, and cannot "inherit" it from whatever you like. Well, in some interpreters you can, but you cannot do it safely across all interpreters — the standard says: "you doth not mess with standard objects or the interpreter will smite you!".
The builtin objects are "unique" and JavaScript does not provide ways to duplicate them. It is not possible to recreate String, Number, Function, and so on without resorting to incompatible trickery.
Based on a discussion on meta about a similar question I'm posting this answer here based on #alexander-mills original
This can now be done in a standards compliant way
First create an object which inherits Function
const obj = Object.create(Function.prototype); // Ensures availability of call, apply ext
Then add you custom methods and properties to obj
Next declare the function
const f = function(){
// Hello, World!
};
And set obj as the prototype of f
Object.setPrototypeOf(f,obj);
Demonstraction
const obj = Object.create(Function.prototype);
// Define an 'answer' method on 'obj'
obj.answer = function() {
// Call this object
this.call(); // Logs 'Hello, World'
console.log('The ultimate answer is 42');
}
const f = function() {
// Standard example
console.log('Hello, World');
};
Object.setPrototypeOf(f, obj);
// 'f' is now an object with an 'answer' method
f.answer();
// But is still a callable function
f();
Yes, it is possible if you use the __proto__ property Daniel Cassidy mentioned. The trick is to have c actually return a function that has had a attached to its prototype chain.
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
var func = function() {
return "I am a function";
};
func.__proto__ = a;
return func;
}
c.prototype = a;
var d = new c();
d.b(); // returns "bar"
d(); // returns "I am a function"
However, you'll need to do some more tweaking of the prototype chain if you want instanceof to return better results.
d instanceof c // true
d instanceof a // false
c instanceof a // false
Does it have to actually be a prototype chain? You can use a mixin pattern to make a function have all of the properties of a instead. You can even wrap it in a nice "new" syntax to fake it if you really want.
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
var f = function(){
return a();
};
//mixin all properties on a
for(var prop in a){
f[prop] = a[prop];
}
return f; //just returns the function instead of "this"
};
var d = new c(); //doesn't need the new keyword, but just for fun it still works
alert(d()); //show "foo"
alert(d.b()); //shows "bar"
You can add properties to d without affecting a. The only difference between this and what you want is that changes to a will not affect existing "instances" of c.
This is something I've been trying to do for a while now. Special thanks to the authors above for their input.
Here is a chained implementation of using a "callable-object":
var $omnifarious = (function(Schema){
var fn = function f(){
console.log('ran f!!!');
return f;
};
Schema.prototype = {
w: function(w){ console.log('w'); return this; },
x: function(x){ console.log('x'); return this; },
y: function(y){ console.log('y'); return this; }
};
fn.__proto__ = (new Schema()).__proto__;
return fn;
})(function schema(){ console.log('created new schema', this); });
console.log(
$omnifarious()().w().x().y()()()
);
Also, with this "Schema" approach, it may be possible to create a reusable interface using Object.freeze() on Schema's prototype or __proto__ object. That might look something like this: fn.__proto__ = Object.freeze(new Schema().__proto__) -- this is not a practical example and more may be needed. That said, its a different discussion. Try it out and let me know!
Hope this is helpful.

Categories

Resources