I know that a new method for an object can be declare like so:
var MyObject = function() {return new MyObject.prototype};
MyObject.prototype.exists = function() {alert("The object exists.")};
How can I create many methods as a bunch for MyObject instead of one by one?
I have tried:
MyObject.prototype = {
exists: function() {alert("The object exists.")},
isBorn: function() {alert("The object is born.")},
isDead: function() {alert("The object has left our world.")}
}
Calling MyObject.exists() returns: Uncaught TypeError: MyObject.exists is not a function
What I am trying to do:
I am trying to do something like jQuery does.
jQuery is defined like so:
jQuery = function(selector, context) {included)
return new jQuery.fn.init(selector, context);
}
We don't say var j = new jQuery; when we call it like jQuery("#foo").
Then the files says:
jQuery.fn = jQuery.prototype = {
jquery: version,
constructor: jQuery,
length: 0,
toArray: function() {
return slice.call( this );
}
...
}
Isn't toArray() a method of the jQuery object? Why doesn't it show the same error when I call it.
When the jQuery function is called, you're not creating an instance of jQuery which is why you don't use the new keyword. Instead, you're returning an instance of jQuery.fn.init.
Follow it down and you'll see that the prototype of init being declared:
init.prototype = jQuery.fn;
And the definition for jQuery.fn is:
jQuery.fn = jQuery.prototype = { ... };
Which means that (new jQuery.fn.init(selector, context)) has all the methods from jQuery.prototype.
So, toArray is not a method of the jQuery object, but rather a method on the prototype of the return value from calling jQuery().
You can achieve the same thing in fewer steps by manually assigning the prototype of the return value.
function MyObject() {
var obj = {};
return Object.setPrototypeOf(obj, MyObject.prototype);
}
MyObject.prototype.toArray = function() {};
MyObject().toArray();
You were very close, you just have to create a new instance of the object for it to inherit from its own prototype chain.
var myObj = function () {};
myObj.prototype = {
a: function () { console.log(1) },
b: function () { console.log(2) },
c: function () { console.log(3) }
};
var myObject = new myObj();
myObject.a(); // 1
myObject.b(); // 2
myObject.c(); // 3
The first one is still better because prototype objects have predefined properties (currently only constructor, but later the standard can be extended), and totally overwriting the prototype object will effectively remove those properties. But still it can be shortened like:
function Foo() {}
const p = Foo.prototype;
console.log(Object.getOwnPropertyNames(p));
p.exists = function() { console.log("exists"); };
p.isBorn = function() { console.log("isBorn"); };
(new Foo).exists();
But hey, it's 2016! We have javascript classes in most major browsers (check compatibility at MDN)!
class Foo {
exists() { console.log("exists"); }
isBorn() { console.log("isBorn"); }
}
(new Foo).exists();
Related
I'd like to do some inheritance using Javascript. The problem is I don't know how to keep the A class prototype on the inherited object.
Here is my code :
function Base() {
this.attribute1 = null;
}
Base.prototype = {
baseFunction: function() {}
};
function SubBase()
{
Base.call(this); // Constructor call
this.baseFunction();
this.subBaseFunction();
}
SubBase.prototype = Base.prototype;
SubBase.prototype = {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
}
With that way I erase Base.prototype but I don't want to.
I tried SubBase.prototype.__proto__ = Base.prototype; but it is apparently too slow.
I know I can do (but too long to write and not that clean for me) :
SubBase.prototype.subBaseFunction = function() {};
SubBase.prototype.subBaseOtherFunction = function() {};
How can I do it/write it better ?
First, you aren't actually inheriting from Base... you're just adding functions to it.
SubBase.prototype = Base.prototype;
SubBase.prototype.foo = true;
alert(Base.prototype.foo);
To set up the inheritance chain, you need to assign an instance of base as the prototype:
SubBase.prototype = new Base();
Then you augment it, just like you have in your post:
SubBase.prototype.newFunction = function () { };
One weakness of doing inheritance this way is that your base class constructor can't receive arguments. If you need that, then you need to use Object.create to set up the inheritance, and manually call your base class constructor:
function Base(x) {
alert(x);
};
Base.prototype.baseFunction = function () {
alert('called method from base class');
};
function SubBase(x) {
Base.call(this, x);
};
SubBase.prototype = Object.create(Base.prototype);
var instance = new SubBase(1);
instance.baseFunction();
Instead of this:
SubBase.prototype = Base.prototype;
SubBase.prototype = {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
}
you need to add each additional method onto the existing prototype object to avoid overwriting the Base.prototype you just put there:
// establish the prototype we're inheriting from
// make a new object into the prototype so when we change it, it
// doesn't change the original
SubBase.prototype = Object.create(Base.prototype);
// now add more methods onto the inherited prototype
SubBase.prototype.subBaseFunction = function() {};
SubBase.prototype.subBaseOtherFunction = function() {};
Note: you also don't want to just assign Base.prototype because then when you change SubBase.prototype, you'll actually be changing both objects (an object assignment is just a reference). So here, I'm using Object.create(Base.prototype) to create a copy of that prototype.
Many libraries support some sort of extend() function that copies properties from one object to another. That lets you define a separate object of methods and then "add" it to the existing prototype, but this functionality isn't built-in to plain javascript.
For example in jQuery, you can do this:
// establish the prototype we're inheriting from
SubBase.prototype = Object.create(Base.prototype);
jQuery.extend(SubBase.prototype, {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
});
Or, updated in 2016, ES6 contains an Object.assign() function that will copy properties from one object to another:
Object.assign(SubBase.prototype, {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
});
Or, you can make your own function that will copy properties from one object to another in only a few lines of code.
Or, the same code in plain javascript:
// copy properties from src to target
function copyProperties(target, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
target[prop] = src[prop];
}
}
return(target);
}
// establish the prototype we're inheriting from
SubBase.prototype = Object.create(Base.prototype);
copyProperties(SubBase.prototype, {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
});
Here's the style I typically use:
function Base() {
this.attribute1 = null;
}
Base.prototype = {
baseFunction: function() {}
};
function SubBase()
{
Base.apply(this, arguments); // Constructor call with arguments
}
(function() {
var proto = SubBase.prototype = Object.create(Base.prototype);
proto.constructor = SubBase;
proto.subBaseFunction = function() {
// code here
};
proto.subBaseOtherFunction = function() {
// code here
};
})();
You could use jQuery.extend() for this:
SubBase.prototype = {
subBaseFunction: function() {},
subBaseOtherFunction: function() {}
};
$.extend(SubBase.prototype, Base.prototype);
See: http://jsfiddle.net/dd6UC/
jQuery.extend() docs: http://api.jquery.com/jquery.extend/
Let's say I have the following code:
(function($) {
var Obj = {
init: function() {
var c1 = Object.create(this.MyChild);
var c2 = Object.create(this.MyChild);
c1.init(); //not working!!!
},
MyChild: function() {
this.init = function() {
console.log('calling MyChild init function');
}
}
};
Obj.init();
})(jQuery);
When creating Obj, I used object literal as I don't need to create the instance of it, and when creating MyChild objects, I used the constructor function and used Object.create as I need to create multiple instances of MyChild.
However, when I call Object.create, it doesn't work, when c1.init() is called, it says the init function is undefined, but if I replaced Object.create(this.MyChild) to:
var c1 = new this.MyChild();
c1.init();
why?
Object.create(func) doesn't do the same thing as new func()!
Object.create() creates an (otherwise empty!) object, which prototype will be set to the object, that is passed to that function (MDN)
To use Object.create() in your example, you could modify it like this:
(function($) {
var Obj = {
init: function() {
var c1 = Object.create(this.MyChild);
var c2 = Object.create(this.MyChild);
c1.init(); //not working!!!
},
MyChild: {
init: function() {
console.log('calling MyChild init function');
}
}
};
Obj.init();
})(jQuery);
But in this case everything will just POINT to your MyChild object. Properties of MyChild will be shared among every object, that you create using Object.create().
I think you should use
var c1 = Object.create(this.MyChild.prototype);
instead of
var c1 = Object.create(this.MyChild);
I'd like to have an object with multiple levels of methods and properties. The top level will have properties and methods. Some of these properties will then act as name-spaces for second level methods and properties.
e.g.
//first level methods
base.doStuff();
base.doMore();
//second level methods
base.level2.doStuff();
Doing the first level is straight forward:
function Base(foo) {
this.foo = foo;
}
Base.prototype.doStuff = function () {
console.log(this.foo);
}
Base.prototype.doMore = function () {
console.log(this.foo);
}
base = new Base("bar");
base.doStuff();
Is it possible to get a second level, where in the function expression the "this" keyword points back to the Base constructor?
It's much easier to do this without prototypes:
function Base() {
var base = this;
base.level2 = {
moreStuff: function() {
// use "base" instead of "this" here
}
};
}
This can be combined with either prototypical methods, as in your example, or methods defined directly on base in the constructor. The downside of this is that you are creating the method functions every time you instantiate a new object, so you miss some of the shared-prototype goodness of standard prototypical methods.
You could create a new prototype-based object to be your level2:
function Level2() {}
Level2.prototype.moreStuff = function() {
// do stuff
}
function Base() {
this.level2 = new Level2();
}
But the methods of base.level2 won't be bound to base unless you bind them explicitly. Various libraries have bind support (e.g. Underscore's _.bind), or you can do it in plain JS:
function Base() {
var base = this;
base.level2 = new Level2();
base.level2.moreStuff = function() {
return Level2.prototype.moreStuff.apply(base, arguments);
}
}
You could further simplify here, but you're always going to have to make new methods bound in one way or another, because JS is never going to assign this in base.level2.moreStuff() to base without explicit binding - so in most cases the first option is the easiest and cleanest.
But really, is it worthwhile just for namespacing? If there's no functional value, it's a lot harder than simply calling your methods level2MoreStuff(), etc.
Well,
base.doStuff();
is calling doStuff in context of base. It is the same as
base.doStuff.call(base);
You can call and apply any function, for overriding this:
var base = new Base();
var someFun = function () {
console.log (this === base); // true
};
someFun.call(base);
Further anonymous example:
var anObj = {
method0: function () {
console.log (this === anObj); // true
}
};
anObj.method1 = function () {
console.log (this === anObj); // true
};
anObj.method0();
anObj.method1();
So the "second level" points this to level2, not to the "first level" object.
This is a really bad idea, but here goes:
function Base() {
this.name = 'Base';
this.level2 = new Level2(this);
}
Base.prototype.whatsMyName = function(){
alert(this.name);
};
function Level2(base) {
this.name='Level2';
for(var func in Level2.prototype) {
this[func] = Level2.prototype[func].bind(base);
}
}
Level2.prototype.whatsMyName = function(){
alert(this.name);
};
var b = new Base();
b.whatsMyName(); //Base
b.level2.whatsMyName(); //Also Base
You can see it running here: http://jsfiddle.net/zLFgd/1/
Here is my code:
function Class() {};
Class.prototype.extend = function () {
var instance = new Class();
instance.constructor.prototype = {
say: function () {
console.log("Hello");
}
}
console.log(instance); //Class {extend: function}
}
Class.extend = function () {
this.prototype.extend();
}
Class.extend();
In the extend method, I rewrite an instance's prototype instance.constructor.prototype = {..},
however, when I log the instance, it doesn't show the say method
Why the rewrite doesn't work? How can I let it work?
Here is the demo
You successfully changed Class.prototype, but you changed it to a totally different object.
instance has a reference to its prototype object, called instance.__proto__. When a new Class instance is created, the __proto__ of the instance points to the same object as Class.prototype.
However, your change what Class.prototype refers to. This will affect the __proto__ of future instances, but not of any existing instances. instance.__proto__ still points to the old object that Class.prototype used to refer to.
This is how it looks at first, after instance is constructed:
instance.__proto__ ===> { extend: function } <=== Class.prototype
This is how it looks after assignment of Class.prototype to a new object:
instance.__proto__ ===> { extend: function }
{ say: function } <=== Class.prototype
Instead, you want to modify the object Class.prototype refers to:
instance.constructor.prototype.say = function () {
console.log("Hello");
}
That will get you a final picture like this:
instance.__proto__ ===> { extend: function, say: function } <=== Class.prototype
See that Class.prototype and instance.__proto__ still point to the same object, but the object itself now has an additional property.
When you assign a new prototype object only newly instantiated objects will have the new prototype:
function Class() {};
Class.prototype.extend = function () {
var instance = new Class();
instance.constructor.prototype = {
say: function () {
console.log("Hello");
}
}
console.log(instance); //Class {extend: function}
console.log(new Class()); //Class {say: function}
}
Class.extend = function () {
this.prototype.extend();
}
Class.extend();
This is because the reference to the prototype object is copied from the constructor's prototype at the time the object is instantiated. If you want to add to the prototype of all existing instances and future instance you can just modify the prototype object rather than assigning a whole new object to the constructor:
function Class() {};
Class.prototype.extend = function () {
var instance = new Class();
instance.constructor.prototype.say = function () {
console.log("Hello");
}
delete instance.constructor.prototype.extend;
console.log(instance); //Class {say: function}
}
Class.extend = function () {
this.prototype.extend();
}
Class.extend();
Though a non-standard approach, this will help you incase
the JS environment allows you to
edit proto internal property.
function Class() {};
Class.prototype.extend = function () {
var instance = new Class();
instance.__proto__ = instance.constructor.prototype = {
say: function () {
console.log("Hello");
}
}
console.log(instance); //Class {extend: function}
console.log(instance.say)
}
Class.extend = function () {
this.prototype.extend();
}
Class.extend();
I does add this function. But you are changing the prototype object itself, rather than changing its properties:
Demo
Class.prototype.extend = function () {
var instance = new Class();
instance.constructor.prototype.say =
function () {
console.log("Hello");
}
console.log(instance);
// Class {extend: function, say: function}
}
When you're trying to access some_obj.some_field the JS engine itself will first check objects own properties and then will go to some_obj.prototype and look for some_field.
By assigning instance.constructor.prototype = {...} you are changing where prototype will point for all newly created objects, but not for already existing ones.
I have a JavaScript object defined like so:
var Object = (function () {
function Object() {
this.id = RandomNumber();
}
// Custom Object.prototype / Object impementations here...
return Object;
})();
The problem is that once this has been constructed, it loses original functionality like Object.defineProperty etc.
The idea is that I want to extend the basic functionality of Object, not re-write or overwrite the existing prototype.
How can this be achieved?
EDIT: Just to be clear, I know I can do this without affecting the original functionality:
Object.prototype.foo = function() { }
but I need to specifically add functionality to Object's constructor, i.e.
function Object() { this.id = 0; }
The new functionality must not overwrite the original Functionality.
Use the .prototype to add a property:
Object.prototype.specialMethod = function () {
// Your method's code
};
And you'd use it like:
var a = {};
a.specialMethod();
Although I would discourage adding a property to the Object's prototype, because it is enumerable and will mess up looping, and will be inherited by all objects, and objects that inherit from Object, which is basically everything.
You could actually use the Object.defineProperty method you mention:
Object.defineProperty(Object.prototype, "specialMethod", {
enumerable: false, // The important one, to avoid looping problems
configurable: false,
writable: false,
value: function () {
// Your method's code
}
});
Do as Ian wrote. If you also want to check it the method already exists use
if (Object.prototype.specialMethod == null) Object.prototype.specialMethod = function() { ... };
In order to extend this object you should create another object that has its prototype assigned a new instance of Object.
var Object = (function () {
function Object() {
this.id = 5;
}
Object.prototype.speak = function(prop){
alert(this[prop]);
}
return Object;
})();
function ExtendsObject(prop){
this.someProperty = prop;
}
ExtendsObject.prototype = new Object();
var xObj = new ExtendsObject("derived");
xObj.speak("id");
xObj.speak("someProperty");
Working Example: http://jsfiddle.net/RbCcA/
If you want to stick with the self executing functions here is the example rewrote:
var Object = (function () {
function Object() {
this.id = 5;
}
Object.prototype.speak = function(prop){
alert(this[prop]);
}
return Object;
})();
var ExtendsObject = (function(){
function ExtendsObject(prop){
this.someProperty = prop;
}
ExtendsObject.prototype = new Object();
return ExtendsObject;
})();
var xObj = new ExtendsObject("derived");
xObj.speak("id");
xObj.speak("someProperty");
Working Example: http://jsfiddle.net/RbCcA/1/
I do question the use of self executing functions in this situation. They are usually used to encapsulate and shield internals, however in the code example they are being exposed by returning the object from the SEF. Returning the object and storing it in a global variable just re-exposes the object, allowing its prototype and properties to be manipulated. Maybe there are private variables you have not mentioned, but as stated I find the SEFs unnecessary.