I have been creating several classes but I never had any that needed parameters on the class itself.
The following code works perfectly.
$(function()
{
search.anotherFunction('a', 'b');
});
search = function()
{
this.anotherFunction = function(param1, param2)
{
// do whatever
};
var public = { anotherFunction: anotherFunction }
return public;
}();
But now I would like to pass parameters inside search in order to avoid passing the same parameters to all the functions.
$(function()
{
search('front/file.php').anotherFunction('a', 'b');
});
search = function(url)
{
this.anotherFunction = function(param1, param2)
{
// use here the 'url' parameter
};
this.anotherFunctionB = function(param1, param2)
{
// use here the 'url' parameter
};
var public = { anotherFunction: anotherFunction,
anotherFunctionB: anotherFunctionB }
return public;
}();
This doesn't work and the console outputs the error.
Uncaught TypeError: object is not a function
It means that search isn't a function but yes a class name and therefore can't receive params?
To start with, the way you're creating your "classes" is incorrect, and ends up creating global variables: Inside the call to your anonymous function, because of the way you call it, this will refer to the global object*, so this.anotherFunction = ... will create a global variable called anotherFunction, because properties on the global object are global variables.
If you want to keep using your current pattern with minimal changes, then don't use this.xyz = ... with your functions, use var instead:
var search = function()
{
var anotherFunction = function(param1, param2)
{
// do whatever
};
var public = { anotherFunction: anotherFunction }
return public;
}();
Also note that you were falling prey to The Horror of Implicit Globals by not declaring search; I've added a var to declare it.
Your second example, with the changes above, would work if you didn't call the outermost function and just assigned the function to the search variable, then called it later:
var search = function(url)
{
var anotherFunction = function(param1, param2)
{
// use here the 'url' parameter
};
var anotherFunctionB = function(param1, param2)
{
// use here the 'url' parameter
};
var public = { anotherFunction: anotherFunction,
anotherFunctionB: anotherFunctionB }
return public;
}; // <== Note, no () here
Now search refers to a function, which we can call like this:
var x = search("http://example.com");
x.anotherFunction(...); // Will have access to the URL
* Why does this refer to the global object when you call your anonymous function? Because you call it without doing anything to set this to something else, and you're using loose mode. (I know you're using loose mode because if you were using strict mode, this would be undefined and so this.anotherFunction = ... would fail.)
Side note: I would recommend you stop using public as a variable name, as it's a future reserved word and has been since at least ES3.
You can use JavaScript closures here. Check out the below approach:
search = function()
{
return function (url) {
this.anotherFunction = function(param1, param2)
{
// use here the 'url' parameter
};
this.anotherFunctionB = function(param1, param2)
{
// use here the 'url' parameter
};
var public = { anotherFunction: anotherFunction,
anotherFunctionB: anotherFunctionB }
return public;
}
}();
search('front/file.php').anotherFunction('a', 'b');
Related
I am trying to create function findStrings which should share it's variables with the function init, stringSplit and stringSearch.
String.prototype.findStrings = (function () {
var action = 'split';
var params = ',';
var init = function (action = 'split', params = ',')
{ this.stringSplit(); }
var stringSplit = function (action)
{ return this.init(); }
var stringSearch = function (action)
{return this.init(); }
return init;
})();
What I need.
When I call test.findStrings('split'); it should 1) initiate variables by calling init 2) then call stringSplit so this should be string test.
When I call var result = test.findStrings( 'search', { params ... } ); 1) initiate variables, process arguments from object params 2) then call stringsSearch and this should be test.
var test = '#a > :matches(.b, #c)';
test.findStrings('split', "," );
var result = test.findStrings( 'search', { params ... } );
Error which I got is saying that this does not have stringSplit (because this is Window) and I need to this to be test.
How to access the test string within the functions and make it possible to access the variables and arguments within them?
Actually, I could remove the function searchStrings to simplify it.
Solution:
String.prototype.findStrings = (function () {
var text = this;
...
String.prototype.init = function (action = 'split', params = ',') {
text.stringSplit();
}
}
There were two errors.
I need to this to be test.
When init() is first called, this does equal test. The problem is that stringSplit and stringSearch are just variables, and have nothing to do with the current context.
Either way, the code is pretty difficult to understand. If you really want to override String, then why don't you do something like:
String.prototype.findStrings = function(action = 'split', params = ',') {
// do logic here (not entirely sure what you want to accomplish)
return (action === 'split') ? this.stringSplit() : this.stringSearch();
}
// Add other functions also to string prototype
String.prototype.stringSplit = function() { ... }
String.prototype.stringSearch = function() { ... }
Personally though, I don't like adding methods to types like String for various reasons. Why not adopt a more functional approach, and create functions that you simply pass strings to, and get an appropriate result?
For example:
function findStrings(str, params){
// do logic and return result
}
First of all, you have a circular reference in your code: the init function calls stringSplit which calls init.
But to reach to that problem you should remove the this keyword from your prototype function. this will get the value of the object that is calling your function, in your case the window object, but your functions are created in a closure so you can't access them using the window object.
I have read this answer and IIFE but I can't seem to find the correct solution to my problem.
I have a simple class here:
define(['jquery'], function($) {
// Need 'self' because someCallback() is being called with .call() and 'this' changes
var self;
function Foo(number) {
self = this;
this.someNumber = number;
}
Foo.prototype = {
someCallback: function () {
//Use self because 'this' changes to a DOM element
var num = self.someNumber;
//Do something with the num
return num * 2;
}
};
return Foo;
});
and someCallBack() is being called by a jQuery plugin using .call(). Because of this, the context changed, hence the use of the self variable.
However, this is wrong because:
define(['foo'], function(Foo) {
describe('context question', function () {
var foo1 = new Foo(1);
var foo2 = new Foo(2);
it('"this" should work', function () {
var call1 = foo1.someCallback.call(this); // 4
var call2 = foo2.someCallback.call(this); // 4
expect(call2).toBe(4); // Only works because it is 'new' last
expect(call1).toBe(2); // Fails because 'self' is taken from foo2
});
});
});
How exactly should I wrap the self variable to make this code work?
You could probably just use the revealing module pattern and declare it as a "global" variable (local to the module):
define(['jquery'], function($) {
var someNumber;
function Foo(number) {
someNumber = number;
}
Foo.prototype = {
someCallback: function () {
return someNumber * 2;
}
};
return Foo;
});
Two ways of calling an object method which stores its own this value include
Define the method as a nested function which references its this value in a closure which stores this value in a variable. The function defined could be anonymous or declared with a name but must be evaluated each time a class instance is created, so as to create a new Function object capturing different values of self in function scope.
Take a statically defined function object and bind its this value using bind. Bind creates a new wrapper function object each time it is called.
The first method looks like (without Jquery or Jasmine):
function Foo(number)
{ var self = this;
this.num = number;
this.someCallback = function() // method with new Foo object stored as self in function scope
{ // something with num:
return self.num * 2;
}
}
and the second method could look like
function Foo(number)
{ this.num = number
this.someCallback = this.someCallback.bind(this); // bind prototypical method as local method.
}
Foo.prototype = {
someCallback: function () {
// this value is bound by constructor;
//Do something with the num
return this.num * 2;
}
};
Example:
var someObject = function () {
return this;
},
someMethod = function (param) {
'use strict';
return /*method name*/;
};
Can I get name of the method (in strict mode)?
P.S. I wish use it for convenient logging for javascript code.
excuse the pseudo code, my actual file is much larger:/
I want to call a function (with parameters) from inside a class. However, that function should be passed to the class as a variable.
someObject = {
itWorked:function(answer){
alert(answer);
},
plugins:{
somePlugin:function(){
var callback;
this.doSomething = doSomething;
function setCallback(c){
callback = c;
}
function doSomething(){
var answer = "hello";
[callback](answer); // how do I call this?
}
}
},
widgets:{
something:function(){
var doIt = new someObject();
doIt.setCallback(someObject.itWorked()); // how do I send this?
doIt.doSomething();
}
}
}
So how would I pass itWorked() to the class?
And how would I call that itWorked(answer) function within the class as well as passing a variable to if?
You will need to change
setCallback = function (c) {callback = c;}
to
this.setCallback = function (c) {callback = c;}
so the setCallback function will be public.
If you also want to scope the callback, you can call it like this
callback.call(scope, param1, param2);
If you don't know how many parameters, you can call it like this
callback.apply(scope, parameters);
Scope could be any object, even an empty one {} if you want.
By the way, I really like your use of private variables in this example, great work with the javascript. Here is a good way to write your javascript object to help with the initialization and readability
var mynamespace = {};
(function () {
function MyObject(param1, param2) {
this.initialize(param1, param2);
}
MyObject.prototype = {
initialize: function (param1, param2) {
var privateScope = {
param1: param1,
param2: param2,
callback: null
};
this.setCallback = function (c) {
privateScope.callback = c;
}
this.doSomething = function () {
if (privateScope.callback) {
privateScope.callback.call();
}
}
}
}
mynamespace.MyObject = MyObject;
}());
Then to use it
var obj = new mynamespace.MyObject("value1", "value2");
Remove the parentheses to pass the function as a variable.
doIt.setCallback( someObject.itWorked );
You can then use the callback as you would any other function.
callback( answer );
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(this._instance);
}
} (); //note that it is self invoking function
var l = new Dog(); //#> undefined
In the above case I was expecting an output of:
'hello world'
Why is this._instance not accessing the the variable which should be accessible by virtue of closure? I tested this in FF and am getting undefined.
You don't assign _instance to the object, it's just a closure variable, and should be accessed without using this:
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(_instance);
}
} (); //note that it is self invoking function
var l = new Dog();
I'd probably write it like so instead:
var Dog = (function() {
var defaults = {
name: 'Rags'
};
var Dog = function (options) {
// Take options as a constructor argument, this
// allows you to merge it with defaults to override
// options on specific instances
this.setOptions(options);
};
Dog.prototype = {
// Common methods for Dogs here
setOptions: function (options) {
// Declare all variables in the beginning of the method because
// JavaScript hoists variable declarations
var key = null;
// First assign all the defaults to this object
for ( key in defaults) {
this[key] = defaults[key];
}
// Now override with the values in options:
if (options && options.hasOwnProperty) {
for ( key in options ) {
this[key] = options[key];
}
}
}
};
return Dog; // Return the constructor method
} ()); // wrap the self-invoked function in paranthesis to visualize that
// it creates a closure
var buster = new Dog({name: 'Buster'}),
unnamed = new Dog();
alert(buster.name); // Alerts 'Buster'
alert(unnamed.name); // Alerts 'Rags'
Note that I have not tried to compile the above code, so it might contain a few mistakes. Nothing JsLint can't handle though!
You might want to consider adding filtering to the setOptions method so that it doesn't assign properties you don't want, or filter out methods etc declared in the options-parameter.
Additionally, if you use JQuery, or similar library, there are (often) utility functions for merging objects, making it trivial to write the setOptions-method:
function setOptions (options) {
// I assume JQuery here
// true as the first argument gives us a recursive merge
var mergedOptions = $.extend(true, defaults, options);
for (var key in mergedOptions ) {
if(this.checkAllowedProperty(key, typeof(mergedOptions[key])) {
this[key] = mergedOptions[key];
}
}
}
/**
* This method checks if propertyName is an allowed property on this object.
* If dataType is supplied it also checks if propertyName is allowed for
* dataType
* #return true if propertyName, with type dataType, is allowed on this object,
* else false
*/
function checkAllowedProperty (propertyName, dataType);
Your problem is this.
Change this._instance to _instance. You may also want to wrap your self-invoking function in parentheses like (function() { ... })(); for maximum browser compatibility.
As the others have said, you need to remove "this." from your function.
The reason for the problem is down to the binding of the "this" keyword in the two function contexts. Inside the closure, "this" refers to the function that is being returned, and not to the outer function. You could resolve this by doing the following:
var Dog = function() {
var _instance = 'hello world';
var that = this; //Assign "this" to "that"
return function() {
console.log(that._instance); //Use reference to "that"
}
} ();
var l = new Dog();
You could also probably do something closer with the function.apply() method, but I'll leave that to you.
I hope that helps.
Perhaps you are satisfied by removing "this.", but you may be interested to learn that "this" doesn't refer to what you wanted it to anyway. What it refers to really depends on how the function is called. It does not necessarily refer to an instance of an object constructed by the function you returned, or its container function, or to any other object. By default, if you merely call the function as a normal function, "this" will refer to the global window context.
What you must do to have "this" be bound to any specific object is to call the function as a method of that object, or of its prototype. e.g. foo.myMethod(). Another way is that you can use the apply or call method, passing in the object you want it to apply to. e.g. anyFunction.apply(foo).