Calling class object with variable count of arguments - javascript

I'm making my own jQuery-like library for a very specific task. (dont want to use jquery, to be closer to native js).
I need a solution to call class with and without agruments, in my case - certain selector.
What I've ment - class has to work in both ways:
myclass.function();
and:
myclass(selector).function();
Initialization of class looks like this
(function(window){
var myclass = function( selector ){
return new myclass.model.build( selector );
};
myclass.model = myclass.prototype = {
constructor: myclass,
myfunction: function(){
alert("some function's alert");
}
};
myclass.model.build = function( selector ){
if( !selector )
return this;
/* Picking objects by selector */
};
window.myclass = myclass;
return myclass;
})(window);

jsFiddle Demo
There are a few ways to accomplish this. Here is a common way, which is to setup an instance of a local function ("class") that has its prototype configured as an object for extensibility and has properties attached to it as an object as well.
//use window and document shortcut
(function(win,doc){
//setup local function in order to extend
var jakeWeary = function(selector){
//force an instance to be returned when referenced
return new jakeWeary.fun.start(selector);
};
//allow prototype to be extended by referencing fun
jakeWeary.fun = jakeWeary.prototype = {
constructor : jakeWeary,
//use passed values to query
start : function(selector){
//query against present document
var nodes = doc.querySelectorAll(selector);
//mimic an array of matched elements
for(var i = 0; i < nodes.length;i++){
this[i] = nodes[i];
}
//return self for chaining
return this;
}
};
//extend function object in order to be used without calling instance
jakeWeary.div = function(content){
var div = document.createElement("div");
div.innerHTML = content;
return div;
};
//expose
win.jakeWeary = win.jk = jakeWeary;
})(window,document)
//call instance to match the selector `.d`
var $ = jakeWeary('.d');//lets use a fun variable like $ for this
//reference the third matched element, and then append a div created from a function on the
//jakeWeary function object
$[2].appendChild(jk.div("<p>Reinventing the wheel</p>"));
<div id="i">i</div>
<div class="d">d</div>
<div class="d">d</div>
<div class="d">d</div>

You mean you want the non-instance call to generate a default instance and then be applied to it?
var Foo = (function () {
// set up constructor
var Foo = function () {
this.fizz = 'buzz';
};
Foo.prototype = Object.create(null);
// instance version
Foo.prototype.bar = function () {return 'this.fizz is ' + this.fizz;};
// non-instance version
Foo.bar = function () {return Foo.prototype.bar.apply(new Foo(), arguments);};
return Foo;
}());
Foo.bar(); // "this.fizz is buzz"

Related

How do I augment a method from the superclass in javascript

I have a method in a base class that I want to keep in a subclass, but just add to it. I've found lots of stuff on augmenting classes and objects with properties and methods, but I can't find, or don't understand, how to just augment the method. The worst case scenario is that I would have to paste the entire method of the parent class into the subclass, but that seems like duplicate code... please help
function someObject (){
this.someProperty = 1;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
//do everything the super class has for this property already
return this.someProperty;
}
}
var incrementer = new newObject;
alert (incrementer.incrementProperty()); //I want output to be 2
// parent object
function someObject () {
this.someProperty = 1;
}
// add incrementProperty to the prototype so you're not creating a new function
// every time you instantiate the object
someObject.prototype.incrementProperty = function() {
this.someProperty += 1;
return this.someProperty;
}
// child object
function newObject () {
// we could do useful work here
}
// setup new object as a child class of someObject
newObject.prototype = new someObject();
// this allows us to use "parent" to call someObject's functions
newObject.prototype.parent = someObject.prototype;
// make sure the constructor points to the right place (not someObject)
newObject.constructor = newObject;
newObject.prototype.incrementProperty = function() {
// do everything the super class has for this property already
this.parent.incrementProperty.call(this);
return this.someProperty;
}
var incrementer = new newObject();
alert (incrementer.incrementProperty()); // I want output to be 2
See: http://jsfiddle.net/J7RhA/
this should do, you have to use prototype to have a real concept of oo with javascript
function someObject (){
this.someProperty = 1;
this.propertyOfSomeObject = 0;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
return this.propertyOfSomeObject;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
this.__super__.incrementProperty.apply(this);
return this.propertyOfSomeObject + 1;
}
}
newObject.prototype = new someObject()
newObject.prototype.__super__ = newObject.prototype
var incrementer = new newObject();
alert(incrementer.incrementProperty()); //I want output to be 2
experiment removing incrementProperty from newObject and it will return 1
I usually use the augment library to write classes in JavaScript. This is how I would rewrite your code using augment:
var Foo = Object.augment(function () {
this.constructor = function () {
this.someProperty = 1;
};
this.incrementProperty = function () {
this.someProperty++;
};
});
var Bar = Foo.augment(function (base) {
this.constructor = function () {
base.constructor.call(this);
};
this.incrementProperty = function () {
base.incrementProperty.call(this);
return this.someProperty;
};
});
As you can see since Bar extends Foo it gets Foo.prototype as a parameter (which we call base). This allows you to easily call the base class constructor and incrementProperty functions. It also shows that the constructor itself is just another method defined on the prototype.
var bar = new Bar;
alert(bar.incrementProperty());
The output will be 2 as expected. See the demo for yourself: http://jsfiddle.net/47gmQ/
From this answer:
Overriding functions
Sometimes children need to extend parent functions.
You want the 'child' (=RussionMini) to do something extra. When RussionMini can call the Hamster code to do something and then do something extra you don't need to copy and paste Hamster code to RussionMini.
In the following example we assume that a Hamster can run 3km an hour but a Russion mini can only run half as fast. We can hard code 3/2 in RussionMini but if this value were to change we have multiple places in code where it needs changing. Here is how we use Hamster.prototype to get the parent (Hamster) speed.
// from goog.inherits in closure library
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var Hamster = function(name){
if(name===undefined){
throw new Error("Name cannot be undefined");
}
this.name=name;
}
Hamster.prototype.getSpeed=function(){
return 3;
}
Hamster.prototype.run=function(){
//Russionmini does not need to implement this function as
//it will do exactly the same as it does for Hamster
//But Russionmini does need to implement getSpeed as it
//won't return the same as Hamster (see later in the code)
return "I am running at " +
this.getSpeed() + "km an hour.";
}
var RussionMini=function(name){
Hamster.apply(this,arguments);
}
//call this before setting RussionMini prototypes
inherits(RussionMini,Hamster);
RussionMini.prototype.getSpeed=function(){
return Hamster.prototype
.getSpeed.call(this)/2;
}
var betty=new RussionMini("Betty");
console.log(betty.run());//=I am running at 1.5km an hour.

How to structure javascript objects, while not getting 'undefined' is not a function errors?

If I have a javascript class that runs some initialization code, it seems logical to put this code at the top, and any methods at the bottom of the class. The problem is, if the initialization code contains a method call, I get a 'undefined' is not a function error. I imagine because the method is defined after the method call. How do people normally structure javascript classes to avoid this? Do they put all the initialization code at the end of the class? For example:
var class = function() {
this.start();
this.start = function() {
alert('foo');
};
};
var object = new class();
causes an error, while:
var class = function() {
this.start = function() {
alert('foo');
};
this.start();
};
var object = new class();
does not. what would be considered a good structure for a javascript object like this?
Here's what I would do
// create a "namespace"
var com = com || {};
com.domain = com.domain || {};
// add "class" defintion
com.domain.MyClass = function(){
var privateFields = {};
var publicFields = {};
privateFields.myFunction = function(){
// do something
}
publicFields.initialize = function(){
privateFields.myFunction();
}
return publicFields;
}
var myClass = new com.domain.MyClass();
myClass.initialize();
of course, you could just make initialize(); "private" and run it before return publicFields;
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
now you can mess around with start as much as you want in the constructor. but dont use the keyword class as it is a reserved word.
I like http://ejohn.org/blog/simple-javascript-inheritance/ where I feel much more programing OOP.
var myClass = Class.extend({
init: function () {
this.foo();
},
foo: function () {
alert("foo");
}
});
new myClass();
Consider making start a method on class's prototype. This has the bonus of saving memory since all instances of class can share the same start, instead of creating a new start function for each instance.
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
I actually work a lot with structures like this:
var foo = function() {
// Actual construction code...
start(); // <-- doesn't make a difference if functions are
privateMethod(); // public or private!
// ---------------------------------
function start() {
// ... whatever ...
};
function privateMethod() {
// ... whatever ...
};
// ---------------------------------
// Add all public methods to the object (if start() was only used internally
// just don't assign it to the object and it's private)
this.start = start;
};

javascript inheritance with protected variables

Is it possible in javascript to have a variable that is not able to access out side the class's functions, but is able to be accessed by classes that inherit it? I.E:
class1 has protected var x = 4;
class2 inherits class1;
class2.prototype.getVar = function(){return /* parent, uber, super, whatever */ this.x;};
var cl2 = new class2();
console.log(cl2.x) // undefined
console.log(cl2.getVar()) // 4
No. Prototypal inheritance is limited to properties of objects.
Variables within the constructor are only available to other code in that variable scope.
You could probably come up with something like...
function cls1() {
var a = 'foo';
this.some_func = function() {
alert(a);
};
}
function cls2() {
cls1.apply(this, arguments);
var cls1_func = this.some_func;
var b = 'bar'
this.some_func = function() {
cls1_func.apply(this, arguments);
alert(b);
};
}
var x = new cls2;
x.some_func(); // alert "foo" alert "bar"
Or to make it more specific to your pseudo code...
function class1() {
var x = 4;
this.getVar = function() {
return x;
};
}
function class2() {
class1.apply(this, arguments);
var cls1_get_var = this.getVar;
this.getVar = function() {
return cls1_get_var.apply(this, arguments);
};
}
class2.prototype = Object.create( class1.prototype );
var cl2 = new class2;
console.log(cl2.x) // undefined
console.log(cl2.getVar()) // 4
I think you need to use a closure to achieve what your trying to do. Something like this:
Class1 = function() {
var x = 4;
return {
getVar: function() {
return x;
}
}
} ();// executes the function immediately and returns an
//an object with one method - getVar. Through closure this method
//still has access to the variable x
Class2 = function() { };// define a constructor function
Class2.prototype = Class1;//have it inherit from Class1
Cl2 = new Class2();//instantiate a new instance of Class2
console.log(Cl2.x);//this is undefined
console.log(Cl2.getVar());//this outputs 4
This is one of the neat things about javascript in that you can achieve the same things in javascript as you would in a class based language without all the extra key words. Douglas Crockford (always good to consult about javascript) explains prototypal inheritance here
Edit:
Just had a second look at your question.If you want newly created methods in your class to access the variable in the base class then you would have to call the getVar method within your own method.Like such:
Class2 = function() {
this.getVar2 = function() {
return this.getVar();
}
};
console.log(Cl2.getVar2()) //outputs 4

What does 'var that = this;' mean in JavaScript?

In a JavaScript file I saw:
function Somefunction(){
var that = this;
...
}
What is the purpose of declaring that and assigning this this to it?
I'm going to begin this answer with an illustration:
var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
// this is a reference to the element clicked on
var that = this;
colours.forEach(function() {
// this is undefined
// that is a reference to the element clicked on
});
});
My answer originally demonstrated this with jQuery, which is only very slightly different:
$('#element').click(function(){
// this is a reference to the element clicked on
var that = this;
$('.elements').each(function(){
// this is a reference to the current element in the loop
// that is still a reference to the element clicked on
});
});
Because this frequently changes when you change the scope by calling a new function, you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this.
Personally, I dislike the use of that as the alias. It is rarely obvious what it is referring to, especially if the functions are longer than a couple of lines. I always use a more descriptive alias. In my examples above, I'd probably use clickedEl.
From Crockford
By convention, we make a private that
variable. This is used to make the
object available to the private
methods. This is a workaround for an
error in the ECMAScript Language
Specification which causes this to be
set incorrectly for inner functions.
JS Fiddle
function usesThis(name) {
this.myName = name;
function returnMe() {
return this; //scope is lost because of the inner function
}
return {
returnMe : returnMe
}
}
function usesThat(name) {
var that = this;
this.myName = name;
function returnMe() {
return that; //scope is baked in with 'that' to the "class"
}
return {
returnMe : returnMe
}
}
var usesthat = new usesThat('Dave');
var usesthis = new usesThis('John');
alert("UsesThat thinks it's called " + usesthat.returnMe().myName + '\r\n' +
"UsesThis thinks it's called " + usesthis.returnMe().myName);
This alerts...
UsesThat thinks it's called Dave
UsesThis thinks it's called undefined
This is a hack to make inner functions (functions defined inside other functions) work more like they should. In javascript when you define one function inside another this automatically gets set to the global scope. This can be confusing because you expect this to have the same value as in the outer function.
var car = {};
car.starter = {};
car.start = function(){
var that = this;
// you can access car.starter inside this method with 'this'
this.starter.active = false;
var activateStarter = function(){
// 'this' now points to the global scope
// 'this.starter' is undefined, so we use 'that' instead.
that.starter.active = true;
// you could also use car.starter, but using 'that' gives
// us more consistency and flexibility
};
activateStarter();
};
This is specifically a problem when you create a function as a method of an object (like car.start in the example) then create a function inside that method (like activateStarter). In the top level method this points to the object it is a method of (in this case, car) but in the inner function this now points to the global scope. This is a pain.
Creating a variable to use by convention in both scopes is a solution for this very general problem with javascript (though it's useful in jquery functions, too). This is why the very general sounding name that is used. It's an easily recognizable convention for overcoming a shortcoming in the language.
Like El Ronnoco hints at Douglas Crockford thinks this is a good idea.
The use of that is not really necessary if you make a workaround with the use of call() or apply():
var car = {};
car.starter = {};
car.start = function(){
this.starter.active = false;
var activateStarter = function(){
// 'this' now points to our main object
this.starter.active = true;
};
activateStarter.apply(this);
};
Sometimes this can refer to another scope and refer to something else, for example suppose you want to call a constructor method inside a DOM event, in this case this will refer to the DOM element not the created object.
HTML
<button id="button">Alert Name</button>
JS
var Person = function(name) {
this.name = name;
var that = this;
this.sayHi = function() {
alert(that.name);
};
};
var ahmad = new Person('Ahmad');
var element = document.getElementById('button');
element.addEventListener('click', ahmad.sayHi); // => Ahmad
Demo
The solution above will assing this to that then we can and access the name property inside the sayHi method from that, so this can be called without issues inside the DOM call.
Another solution is to assign an empty that object and add properties and methods to it and then return it. But with this solution you lost the prototype of the constructor.
var Person = function(name) {
var that = {};
that.name = name;
that.sayHi = function() {
alert(that.name);
};
return that;
};
Here is an example
`
$(document).ready(function() {
var lastItem = null;
$(".our-work-group > p > a").click(function(e) {
e.preventDefault();
var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
if (item == lastItem) {
lastItem = null;
$('.our-work-single-page').show();
} else {
lastItem = item;
$('.our-work-single-page').each(function() {
var imgAlt = $(this).find('img').attr('alt'); //Here value of "this" is '.our-work-single-page'.
if (imgAlt != item) {
$(this).hide();
} else {
$(this).show();
}
});
}
});
});`
So you can see that value of this is two different values depending on the DOM element you target but when you add "that" to the code above you change the value of "this" you are targeting.
`$(document).ready(function() {
var lastItem = null;
$(".our-work-group > p > a").click(function(e) {
e.preventDefault();
var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
if (item == lastItem) {
lastItem = null;
var that = this;
$('.our-work-single-page').show();
} else {
lastItem = item;
$('.our-work-single-page').each(function() {
***$(that).css("background-color", "#ffe700");*** //Here value of "that" is ".our-work-group > p > a"....
var imgAlt = $(this).find('img').attr('alt');
if (imgAlt != item) {
$(this).hide();
} else {
$(this).show();
}
});
}
});
});`
.....$(that).css("background-color", "#ffe700"); //Here value of "that" is ".our-work-group > p > a" because the value of var that = this; so even though we are at "this"= '.our-work-single-page', still we can use "that" to manipulate previous DOM element.

Method delegation in Javascript/jQuery?

I have this code:
var myWidget = $('#myWidget');
and calls like this elsewhere:
myWidget.hide();
myWidget.slideToggle();
These work of course because jQuery adds these methods.
Now, let's say I'm doing some refactoring to make myWidget a proper object with its own custom methods and state:
var myWidget = (function() {
// private stuff
var actualJQueryObject = $('#myWidget');
return {
publicMethod: function() {...},
// MAGIC!
}
})()
but I want to have all the calls that expect a jQuery object, which are all around my code, to still work even though myWidget is no longer a jQuery object, because myWidget knows how to delegate these calls to actualJQueryObject.
Is this possible?
You could also extend your jQuery object, with another object that has your custom methods:
var myWidget = function() {
// private stuff
var actualJQueryObject = $('#myWidget');
var extensionMethods = {
publicMethod: function() { alert('public method!'); }
}
return $.extend(actualJQueryObject, extensionMethods);
}();
Just be careful with the name of your extension methods, to not clash with any other jQuery defined function.
You can try the above snippet here.
One option is using the original jquery object as a prototype.
function wrap(jqObject) {
function MyNewType() {
this.changeFontSize = function(a) {
this.css({fontSize : this.size});
};
}
MyNewType.prototype = jqObject;
return new MyNewType;
}
var obj = wrap($('#someitem'));
obj.size = 50; // obj.size
obj.changeFontSize(); // obj.changeFontSize
obj.hide(); // $.hide
obj.fadeIn("slow"); // $.fadeIn
I've written a plugin that might help you. It's basically a plugin for writing plugins. This dev group post explains it and has some code samples:
http://groups.google.com/group/jquery-dev/browse_thread/thread/664cb89b43ccb92c/72cf730045d4333a?hl=en&q=structure+plugin+authoring#72cf730045d4333a
And the source is here:
http://code.google.com/p/jquery-plugin-dev/source/browse/trunk/jquery.plugin.js
EDIT:
I created a function that has similar functionality to that plugin:
jQuerify = function(fn) {
function plugin() {
var instantiate = false;
// check to see if it has any prototyped methods (we only need one iteration to do this)
for (var i in construct.prototype) {
instantiate = true;
break;
}
// if there are prototyped methods, return an instance (since an instance's return value won't vary)
// otherwise just call it using apply so the return value can vary
return instantiate
? new construct(this, arguments)
: construct(this, arguments);
}
function construct(parent, args) {
// 'this' will not mimic jQuery unless given the length property
this.length = 0;
this.selector = parent.selector;
this.context = parent.context;
// mimic 'this' in jQuery, but for the plugin namespace
Array.prototype.push.apply(this, $.makeArray(parent));
// return the constructors return value
// should be 'this' if you want to chain the new namespace
return fn.apply(this, arguments);
}
// copy all static properties and methods
for (var i in fn) {
plugin[i] = fn[i];
}
// allow .fn and copy all instance properties and methods; the last part is for IE
plugin.fn = construct.prototype = plugin.prototype = fn.prototype;
return plugin;
}
This allows you to add custom objects to jQuery as a plugin while using 'this' to refer to the selected objects and also allows you to have an unlimited depth to your namespace:
function test1() {
return this;
}
test1.prototype.getHtml1 = function() {
return $(this).html();
}
function test2() {
return this;
}
test2.prototype.getHtml2 = function() {
return $(this).html();
}
function test3() {
return this;
}
test3.prototype.getHtml3 = function() {
return $(this).html();
}
jQuery.fn.test1 = jQuerify(test1);
jQuery.fn.test1.fn.test2 = jQuerify(test2);
jQuery.fn.test1.fn.test2.fn.test3 = jQuerify(test3);
jQuery(function($) {
alert($('body').test1().getHtml1());
alert($('body').test1().test2().getHtml2());
alert($('body').test1().test2().test3().getHtml3());
});

Categories