javascript: passing as object or function - javascript

My question is rather weird, it has to do with something i have seen in jQuery but so far i have been unable to recreate it.
in jQuery you can go like this
jQuery('div').append
or
jQuery.ajax
the application i am making would need a similar syntax, i notice if you use new like
var that=new function(){
}
you can call the function with just that, without the (), but in some cases i would need it.
The reason for this is some functions i have need to select a dom element just like jQuery so.
that('[data-something="this"]').setEvent('click',functin(){})
and some automatically do it so:
that.loadIt('this','[data-something="that"]')
the reason for this is that the dom elements are loaded externally and pushed, then the script waits for it to be ready before continuing. and doing it this way, to me anyway seems like the most cleanest way to get this functionality (i am coding a full javascript framework so i avoid libraries to keep the scripts fast)

Functions are objects.
Just get rid of new, and add properties directly to that.
var that = function() {
// do some work
}
that.loadit = function() {
// do other work
}
Since you're trying to achieve something like jQuery does, then have that call a constructor.
;(function(global) {
// function to be publicly exposed
var that = function(foo, bar) {
return new MyLibrary(foo, bar);
}
// publicly expose the function
global.that = that;
// use the function as a namespace for utilities
that.loadit = function() {
// do other work
}
// The actual constructor function, like the internal jQuery constructor
MyLibrary(foo, bar) {
// constructor function
}
// Prototypal inheritance of objects created from the constructor
MyLibrary.prototype.setEvent = function() {
// do some work
return this; // allows for method chaining
};
MyLibrary.prototype.otherMethod = function() {
// do something else
return this; // allows for method chaining
};
})(this);

Functions are objects and can have properties, just like other objects can. So, you can add a property to a function like this:
function myFunc(){}
myFunc.someFunc = function(){}
If you use new myFunc the resulting object won't have someFunc as it's not part of the prototype.
So, you can make something like this:
function myFunc(){
// This lets you do "myFunc()" instead of "new myFunc()"
if (!(this instanceof myFunc)) {
return new myFunc();
}
else{
this.val = 0;
this.setVal = function(x){
this.val = x;
// for function chaining
return this;
}
this.getVal = function(){
return this.val;
}
}
}
// This function is not part of the prototype
myFunc.test = function(){
alert('hi');
}
// Some tests
var obj = myFunc();
obj.setVal(12).getVal(); // 12
myFunc.test();
obj.test(); // Error: 'test' is not a function
myFunc.getVal(); // Error: 'getVal' is not a function

$.fn.loadIt=function(var1,var2) {
// $(this) is automatically passed
// do stuff
}
call it like this
$('#element').loadIt('a variable','another variable');

Related

Javascript catch protoype calls

Is there a way in javascript to catch every call on an object's prototype?
So if there is a Something object, and someone calls Something.someFunction() where someFunction does not exists I can return false.
This is possible using Proxy, but I need a cross-browser solution.
Using a delegate function which first parameter is a function name, second is the function parameter also not good.
Example:
var Something = (function() {
var Something = function () {};
Something prototype = {
'test': function() {}
};
return new Something();
})();
Something.someFunction(); // false

How can I make an object callable as an instance and a function

I'm working on building a collection of prototype helper methods inside a wrapper. However for ease of use, I'd like to be able to call the object as both a new instance and single global instance under the same call.
For example, with jQuery, you can call both "$" and "$()" which can be used differently http://learn.jquery.com/using-jquery-core/dollar-object-vs-function/:
So given the bellow as simple example, how could I do something similar?
(function () {
var myWrapper = function (foo) {
return new helper(foo);
};
var helper = function (foo) {
this[0] = foo;
return this;
}
helper.prototype = {
putVar: function(foo) {
this[0] = foo;
}
}
if(!window.$) {
window.$ = myWrapper;
}
})();
// create an new instace;
var instance = $("bar");
console.log(instance);
// call a prototype method
instance.putVar("foo");
console.log(instance);
// call a prototype method using the same call without new instance
// this doesnt work :(
$.putVar("foo");
// however this will work
window.myLib = $("foo");
myLib.putVar("bar");
http://jsfiddle.net/2ywsunb4/
If you want to call $.putVar, you should define putVar like this:
myWrapper.putVar = function (foo) {
console.log('Work');
}
In your code, the instance and myLib are the same thing, they are both instances created by you.
If you want to call both $.putVar and $(...).putVar, you should add the code I show you above. That means you have to define two putVar functions, one is used like a 'instance' method, while the other one is used like a 'static' method.
If you search through jQuery source code, you will see two each functions defined. That's why you can all both $.each(...) and $(...).each for different usages.

Having trouble extending a Javascript function with more functions

I'm trying to override a Javascript function that is included in a .js file that I cannot modify(it is served from a server our applications aren't deployed on). The function contains functions and variables within it. There is one sub-function that I need to change the behavior of but it is called by another, I can't call it directly. Here it is:
Simplistic version of included file that I can't change:
com.company.topLevelFunc = function () {
var a = null;
var b = null;
var doSomething = function() {
a = foo;
b = bar;
};
var get = function(cfg) {
//do stuff
};
return {
//other vars and functions here
getValue : function (cfg) {
cfg.url=a + b;
get(cfg);
}
};
}();
The function I need to override is the get(cfg). However, I can't call it directly, I need to go through the getValue(cfg) method and preserve all the other stuff that goes on in the top level function. So I've been trying to override both as follows:
My JS that I full control over, and doesn't work, it errors on the apply saying that 'missing : after property id". Am I doing this correct, not sure how to get the apply to work correctly?
var topLevel = com.company.topLevelFunc;
myTopLevel = function() {
var myGet = function(cfg) {
//do simliar but different stuff from the original get function
};
return {
topLevel.apply(this, arguments);
getMyValue : function (cfg) {
cfg.c= a + b +"some other string";
//do something
myGet(cfg);
}
};
}();
Unfortunately for you, the get method is private. It only exists inside the body of a an anonymous function nobody has access to. Aside from some very clever hacks and unrecommended programming, there is nothing you can do to change it.
What you should do in this case is change the getValue property of com.company.topLevelFunc. The way you are attempting to do that is slightly wrong and i will explain why.
a = function() { // this is not a
var c = function() { return 3; };
return { // this object is a
b : c;
}
}(); // called immediately
In this construct, a is not a function. The function you see there is invoked immediately and a is assigned the return value of that invocation. In fact this is almost equivalent:
a = { b : function() { return 3; } };
Why you would use the first construct instead of the second is a question asked many times here. A short answer is that the first allows you to have private stuff that nobody else can access (like your get method).
So a is an object with a property who's value is a function that returns 3. If you want a to return 4, you need to do something like this:
a.b = function() { return 4; }
This keeps the other properties of a intact (if it had any) and only changes the property you are interested in.
If you want b to access some private properties defined in the anonymous function above, it simply cannot be done.

jQuery-style function that can be accessed like an object

I am creating an AJAX API for a web service and I want to be able to call jQuery-like accessors.
jQuery seems to be able to execute 'jQuery' as a function, but also use it to directly access the object that is the result of the function EG:
jQuery();
jQuery.each({});
This is the trick that I can't seem to pull off:
myAPI('foo'); //output: 'foo'
myAPI('foo').changeBar(); //output: 'foo' 1
myAPI.changeBar(); //Error: not a function
I have seen the answers to similar questions, which are helpful, but don't really answer my question.
#8734115 - Really interesting, but you can't access the methods that were set by f.prototype.
#2953314 - Uses Multiple operations to create object instead of a single function.
here is my code:
(function(window) {
var h = function(foo) {
// The h object is actually just the init constructor 'enhanced'
return new h.fn.init(foo);
};
/**
* Methods defined at protoype.
*/
h.fn = h.prototype = {
constructor: h,
init: function(foo) {
console.log(foo);
return this;
},
splice : function () {},
length : 0,
bar : 0,
changeBar : function() {
this.bar++;
return this.bar;
}
};
h.fn.init.prototype = h.fn;
//Publish
window.myAPI =h;
}( window));
I'm sure I'm missing something simple :(
What jQuery is doing there is using jQuery as both a function and as a pseudo-namespace. That is, you can call jQuery: var divs = jQuery("div"); and you can use properties on it, e.g.: jQuery.each(...);.
This is possible because in JavaScript, functions are first-class objects, and so you can add arbitrary properties to them:
function foo() {
alert("Foo!");
}
foo.bar = function() {
alert("Bar!");
};
foo(); // "Foo!"
foo.bar(); // "Bar!"
That's literally all there is to it.
Within the call to bar, this will be the foo function (because this is determined entirely by how a function is called, not where it's defined). jQuery doesn't use this to refer to itself (usually it uses this to refer to DOM elements, sometimes to other things like array elements; when referring to itself, since it's a single thing, it just uses jQuery).
Now, you might want to ensure that your functions have proper names (whereas the function I assigned to bar above is anonymous — the property has a name, but the function does not). In that case, you might get into the module pattern:
var foo = (function() {
function foo() {
alert("Foo!");
}
function foo_bar() {
alert("Bar!");
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
That pattern also has the advantage that you can have private data and functions held within the scoping function (the big anonymous function that wraps everything else) that only your code can use.
var foo = (function() {
function foo() {
reallyPrivate("Foo!");
}
function foo_bar() {
reallyPrivate("Bar!");
}
function reallyPrivate(msg) {
alert(msg);
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
reallyPrivate("Hi"); // Error, `reallyPrivate` is undefined outside of the scoping function
In your code, you're assigning things to the prototype property of the function. That only comes into play when the function is called as a constructor function (e.g., via new). When you do that, the object created by new receives the function's prototype property as its underlying prototype. But that's a completely different thing, unrelated to what jQuery does where it's both a function and a pseudo-namespace.
You do not need any of that weirdness, to use stuff like $.each
you just attach functions to the function object instead
of the prototype object:
function Constructor() {
if (!(this instanceof Constructor)) {
return new Constructor();
}
}
Constructor.prototype = {
each: function() {
return "instance method";
}
};
Constructor.each = function() {
return "static method";
};
var a = Constructor();
a.each(); //"instance method"
Constructor.each(); //"static method"

How does jQuery have the $ object constructor and the methods associated with the $?

How is it that jQuery can do $("#foo").addClass("bar") and $.ajax()?
I'm creating a micro javascript framework and want to create a new instance of an object, such as $("#hello"). With this object there are associated methods, such as addClass, css, etc, just like with jQuery. So I could do something like
$("#foo").addClass("remove").css("color", "red");
I have been successful in creating this. However, when I want to call a method from this object, such as $.ajax, the constructor function is overwritten, and I can call $.ajax, but not $("#foo").
Basically, how can jQuery do both?
$ = function(arg) { console.log("$ function called with " + arg); }
$.ajax = function(arg) {console.log("$.ajax called with " + arg);}
$('foo');
$.ajax('bar');
http://jsfiddle.net/ac7nx/
I don't think there's any magic here. $ is just a name for the global function. Just keep in mind that in javascript, functions are first class objects that can have their own properties, including sub-functions, which is what $.ajax is.
Since you mentioned the constructor function, I should note that there are no OO objects being used here, just regular functions (no new keyword), so constructor functions don't play into this. If you are using the new keyword, that is probably where you are getting confused. If you want $('#foo') to return a new object, then inside the $ function's code you should create a new object using new and return that, which is what jQuery does, but the $ function itself is not a constructor and should not be called with new. Or in the case of something like $('#someID'), inside that function jQuery is getting an element object from the DOM and then returning that object, but still $ is just a regular function whose return value is an object, not a constructor function.
OK, the $ function is not only a function but an object, like all functions. So it can have methods. That's all that ajax is, a method of the $ function. So we can start off by doing this:
$ = function(obj) {
// some code
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
So far so good. Now, what on earth do we put in the $ function? Well it has to return an object and that object has to have some nice methods defined on it. So we'll need a constructor function (to give us new objects) and a prototype (to provide the nifty methods for those objects).
$ = function(obj) {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
return new myConstructor(obj);
};
So there we have it. We can do this:
var mySnazzObject = $("whatever");
mySnazzObject.niftyMethod().anotherNiftyMethod(true);
And we can do this:
$.ajax("overthere.html", data);
Obviously jQuery does a heck of a lot more than that, and it does it in some really impressive ways, but that's the general idea.
UPDATE: AS #Raynos was kind enough to observe without supplying a constructive answer, my original code would create the prototype ad infinitum. So we make use of an anonymous autoexecuting function to declare the constructor and prototype separately:
(function () {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
var $ = function(obj) {
return new myConstructor(obj);
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
window.$ = $;
}());
Two different things:
$.ajax is a prototyped function of jQuery called ajax.
While $('foo') is a call of the jQuery function and depending on the type of foo it reacts in different ways. At http://api.jquery.com/jQuery you can see that jQuery (almost the same as $) can react on three different types: selectors, html or callbacks.
You can imitate the magic jQuery behaviour as following:
//define
var _$ = {};
var $ = function(selector) {
_$.html = function(args) {
alert("Doing the method 'html' with arguments " + args
+ " for selector " + selector);
return _$; //needed for pipeline
};
return _$;
};
$.ajax = function(args){alert("Doing the method 'ajax' "); }
//and try
$("selector1").html("args1").html("args2");
$.ajax("args2");

Categories

Resources