I am creating some basic plugin and i am getting Reference error. Below is my code
jQuery.fn.validate = function(options) {
var _self = this;
// with below call I gets reference error.
abc();
//but if call in below it works fine
_self.on("submit", function(event) {
abc(); // works fine
}),
abc = function () {
console.log('here);
}
};
Can someone explain why I am getting this error and how to overcome it. As i need to call some reset and init functions at the begining of the plugin.
It seems like you're expecting abc to be hoisted, but you're specifically using a syntax that leaves abc undefined until the assignment is executed.
You need to move abc = function ... up above the invocations of abc(), or define the function using function abc() { } which will allow it to be hoisted above your invocations.
Note that, if you simply move the assignment, you should use var abc = function ... and create a local variable, rather than the global abc variable you're currently creating.
Related
I'm using PhantomJS v2.0 and CasperJS 1.1.0-beta3. I want to query a specific part inside the page DOM.
Here the code that did not work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc());
this.echo("value: " + del);
And here the code that did work:
var del=this.evaluate(function()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
});
this.echo("value: " + del);
It seems to be the same, but it works different, I don't understand.
And here a code that did also work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc);
this.echo("value: " + del);
The difference here, I call the myfunc without the '()'.
Can anyone explain the reason?
The problem is this:
var text = this.evaluate(myfunc());
Functions in JavaScript are first class citizen. You can pass them into other functions. But that's not what you are doing here. You call the function and pass the result into evaluate, but the result is not a function.
Also casper.evaluate() is the page context, and only the page context has access to the document. When you call the function (with ()) essentially before executing casper.evaluate(), you erroneously try to access the document, when it is not possible.
The difference to casper.evaluate(function(){...}); is that the anonymous function is defined and passed into the evaluate() function.
There are cases where a function should be called instead of passed. For example when currying is done, but this is not applicable to casper.evaluate(), because it is sandboxed and the function that is finally run in casper.evaluate() cannot use variables from outside. It must be self contained. So the following code will also not work:
function myFunc2(a){
return function(){
// a is from outer scope so it will be inaccessible in `evaluate`
return a;
};
}
casper.echo(casper.evaluate(myFunc2("asd"))); // null
You should use
var text = this.evaluate(myfunc);
to pass a previously defined function to run in the page context.
It's also not a good idea to use reserved keywords like del as variable names.
I have inherited a code base that I need to update for my job. I'm slowly learning what they are trying to accomplish with the closure that is in place, but I am getting stuck when trying to update a part of the site that uses this functionality. I'll give a basic outline of what the code is trying to accomplish and see if anyone can help.
var TheObject = (function (){
var veryLargeDependantData = {
var1: {},
var2: {},
var3: [],
//Set these variables via functions
function1: function f1(data){...},
function2: function f2(data){...},
initialize: function initialize() { //set values for var1... var3}
};
return {initialize: veryLargeDependentData.initialize};
})().initialize();
Since I obviously cannot show production code on the site this will have to do. But basically the veryLargeDependentData variable is the entrance to the function. When the page loads it calls the initialize function and everything is happy. But now I need to add this to an onclick event for and older page and the firebug console says that the variable is undefined. In the other pages I am able to use it with no problem.
My question is what magic is going on that causes the closure not to be part of a callable namespace such as this. I'm a bit of a javascript nOOb so I apologize if the question sounds misguided.
onclick='TheObject.initialize();'
I assume what you mean is that you want to run the initialize function in a click event handler, and you're currently attempting to do so like this:
TheObject.initialize();
If that's the case, the problem is that TheObject actually refers to the return value of initialize, since you called initialize on the return value of the immediately-invoked function expression. And the chances are that initialize is returning undefined (most likely, it has no explicit return statement).
To solve this, you probably want to remove the immediate call to initalize, which will allow you to use the line shown above both on page load and anywhere else.
In this code, the value of TheObject will be whatever the veryLargeDependentData.initialize() method returns. If the initialize method returns nothing, TheObject will be undefined.
A simplified example:
var TheObject = (function () {
return {
initialize: function () {
// stuff happens here, but importantly, there's nothing returned
}
}
})().initialize();
You can break this down into the following order of execution:
// the value of step_one is a function that will return an object when it is run
var step_one = (function () {
return {
initialize: function () {
// ...
}
}
});
// after running the function step_one, step_two will be an object
// containing one key - initialize - which is a function
var step_two = step_one();
// since initialize doesn't return anything, TheObject is set to undefined.
var TheObject = step_two.initialize();
You can get around this by setting TheObject to be the object containing the initialize method then run that method again whenever you need it.
var TheObject = (function () {
return {
initialize: function () {
}
}
})();
// initialize
TheObject.initialize();
// and again
TheObject.initialize();
PLEASE NOTE!
The original author may have intended for the initialize method to only be run once. Running it more than once might introduce bugs into your system!
Seems unnecessarily complicated, I'm not sure what is gained by using the anonymous function and closure in this case. Is there any reason you can't simple do the following?
var TheObject = {
var1: {},
var2: {},
var3: [],
//Set these variables via functions
function1: function(data){...},
function2: function(data){...},
initialize: function(){alert("initialize");}
};
var initButton = document.getElementById("initButtonName");
initButton.addEventListener("click", TheObject.initialize);
Note that you'd want to remove the inline event.
I have a massive javascript file with many function expressions. All of a sudden console gives me the following errors:
In IE
The value of the property 'myFunc' is null or undefined, not a Function object
In Firefox
TypeError: myFunc is not a function
This is how I call the function:
myFunc();
This is the function:
myFunc = function() {
//do stuff
}
This is happening on ALL function expressions. If I change one to a function declaration it works, but then will fail on some other function expression call inside of it. What the heck?
Possibility 1
If you are calling the function expression before it is defined, you will get this error. If you however turn it into a function declaration, the function declaration would get hoisted to the top of the scope, and could be called before or after the actual declaration occurs. So:
functionFoo();
var functionFoo = function() {
};
Will give this error, because you are trying to call the function before it is defined. But:
functionFoo();
function functionFoo() {
}
Will work, because actual function declarations are hoisted to the top of the scope, and can be used anywhere.
Possibility 2
If you are calling the function expression from a different scope that is outside where the function expression is defined, you will get this error. function expressions, like other variables, can only be used within the scope they are defined. So:
$( document ).ready( function() {
var functionFoo = function() {
};
} );
functionFoo();
Will give you an error, because the defining of the function happens in a different scope than the call. But:
$( document ).ready( function() {
var functionFoo = function() {
};
functionFoo();
} );
Will work just fine, because both the defining and the call happen in the same scope.
I have a script that I didn't write already running on a page. I'd like to, in a script I did write, be able to execute a function from the original script. Here's a simplified example of what I'm trying to do:
(function ($) {
$.fn.myExistingFunction = function (options) {
function doMyThing(text) {
alert(text);
}
}
}(jQuery));
jQuery(document).ready(function($) {
$.fn.myExistingFunction.doMyThing("alert text");
});
However, when I run this, I get the console output:
Uncaught TypeError: Object function (options) {
function doMyThing(text) {
alert(text);
}
} has no method 'doMyThing'
But it clearly has the method! I can see it right there. What am I getting wrong here?
You can only access that method from the scope of the plugin function. It is not defined in the global scope.
So you can't call that function. All you can do in your code is call functions that are available through what's called the scope chain where the function is called. In your case that is everything that is either defined in your function($){} or in global scope. But the function in the plugin is neither.
When you call $.fn.myExistingFunction.doMyThing you are treating doMyThing like an field in the myExistingFunction object. But actually it is defined inside the function. Maybe this code makes it clearer:
$.fn.myExistingFunction = function (options) {
var doMyThing = function(text) {
alert(text);
}
var hi = "hello";
}
In hi and doMyThing are local variables in the scope of myExistingFunction. If the plugin wasn't designed to expose the functionality to external code you can't access the variables.
An example of a plugin that was designed to expose some of its internal functions would be the jQuery UI datepicker (documentation). It needs functions like parseDate and formatDate interally, but these utility functions are likely to be useful for general development as well, which is why they have been added to the datepicker object explicitly.
I think your code needs a major rewriting, 'cause as it is it will never work, in fact your function doMyThing is not a property of $.fn.myExistingFunction, it is simply declared inside of it, and due to how the scope works in javascript, you'll never be able to call it.
Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error