I have a function that looks like this:
function outer() {
function inner_1() {
alert('inner_1');
}
function inner_2() {
alert('inner_2');
}
function inner_3() {
alert('inner_3');
}
inner_1();
inner_2();
inner_3();
}
I need to call outer(), but I want to replace inner_1() with another function.
I have tried this:
new_outer = outer;
new_outer.inner_1 = function() {
alert('my new inner function');
};
If I try to call the newly redefined inner_1 like this:
new_outer.inner_1();
it works as expected ('my new inner function' is alerted).
But if I try to call the outer function:
new_outer();
the old version of inner_1 is called.
I want to redefine inner_1 and the call outer. How can I achieve this?
This seems like a really bad idea so I am not going to post any code. However, if you are pursuing the answer for "educational purposes only", I will just hint that although you cannot easily redefine a function from outside its scope (as per your example), there is nothing stopping you from redefining a function attached to a function object.
I do think, however, there is a better solution to whatever the problem you are trying to solve is.
Related
today my question is asking how I would access a function inside a function. So, for example, I have a button, and if I click it, it would alert. The thing is, if you have a function surrounding the function, the inside function with the alert would not alert.
Here's an example:
html:
<button onclick="doStuff()">Alert</button>
js:
function nothing() {
var doStuff = function() {
alert("This worked!")
}
}
so the doStuff() function would not work. Can someone help me find a way to access it?
#Joseph the Dreamer is ultimately correct, but if you were dead set on calling a function that's nested in another function you could use an OOP approach.
Create a javascript "class" object and scope your function to "this":
function Nothing() {
this.doStuff = function() {
alert("works");
}
}
Next you add an id to your button,
along with a click event listener
Then, inside your click event you can call doStuff within the Nothing "Class" function like this:
var object = new Nothing();
object.doStuff();
https://jsfiddle.net/me7fek5f/
You can't. That's because it's enclosed in a scope that you can't really access globally. The only way you can access it is to expose it somewhere outside nothing.
Is this a homework question?
You're probably asked to do something like this:
function nothing() {
var doStuff = function() {
alert("This worked!")
}
var yourButton = getYourButton();
attachClickListener(yourButton, doStuff);
The implementations of getYourButton and attachClickListener are left to the reader.
In my knockout.js project I wrote some self invoking functions like this:
var addMarkers = function () {
ko.utils.arrayForEach(self.sectionList(), function (sectionItem) {
ko.utils.arrayForEach(sectionItem.placeList(), function (placeItem) {
placeItem.marker.addListener('click', function () {
map.panTo(placeItem.marker.getPosition());
});
});
});
}();
The function works without problems, however in JSLint the "var addMarkers" was highlighted as unused variable. That makes me wonder if I should the function like this, or just make anonymous because it is a better practice?:
function addMarkers (){ code to be executed };
Assigning the result of a self-executing function is often useful. In particular, I like to define my main viewmodel this way, because
I don't need a prototype for my viewmodel
I'm not likely to need more than one instance of a viewmodel
The reason you would use a self-executing function is that you need a local scope for whatever you're doing, to isolate it from surrounding scope. The reason you would assign a variable from it is that you want it to return a value you will use later. In your example, neither of these is true.
Let's start from the code:
function say(name) {
var ghost=function () {
function ghost() {
alert('!');
};
return body;
};
eval("var body=''+"+name+';');
eval(name+('=('+ghost).replace('body', body)+')();');
eval(name+'();');
}
function Baal() {
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
say('Baal'); // or just Baal();
Looks like that saying the devil's name invoke his presence (well, maybe he needs somebody for spiritual possession) ..
As you can see the ghost doesn't exist along with Baal, but we can invoke it since there're evals in say(name).
say(name) reassigns Baal to its code body as a closure and makes it captured a ghost method, that's how things work. But I'm trying to avoid eval ..
So .. let me reword the question:
How do I make a nonexistent(and not a member or global) method invocable without using eval?
Let me rephrase your question, just to make sure I’ve got it. Given a function, you want to put a new variable in its scope, without that scope being the global scope or a scope shared between the caller and the subject, without using eval (or the equivalent new Function and other hacks depending on the environment).
You can’t.
In the case you just mentioned, you could define one function, base(), that uses arguments.callee.caller.
Don’t do that.
The short answer: You don't.
That scope is not available. If you were to attach the scope then it would be available inside of the scope used. You could then access the method handles. I assume this is not what you were looking for, but here is what that would look like. demo
function say(name){
var methods = {};
methods.Baal = function(){
alert("!");
};
return methods[name];//this could invoke as well: methods[name]()
}
var handle = say('Baal');
handle();
What your evals break down to is something along these lines (although with dynamic content from string building - this is the end result)
function say(name) {
var Baal = (function () {
function ghost() {
alert('!');
};
return function(){
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
})();
Baal();
}
say('Baal'); // or just Baal();
Note that the meat of what happens here is from the function Baal, namely that it calls a hardcoded ghost() which in turn calls a hardcoded alert. Why go through all of this trouble to access a hardcoded function?
A better way would be to inject this function as a callback which expects some parameters to be injected.
jsFiddle Demo
function say(callback){
var params = "!";
if( typeof callback == "function" ){
callback(params);
}
}
say(function(params){
alert(params);
});
It's very difficult for me to read through your code and figure out what you are trying to accomplish with it, but it appears that you are trying to introduce a variable into the current scope so that you can call it. You cannot do this in javascript with the method that you demonstrated. Scoping only ever "flows down". By that I mean that a variable or function defined within a function will only be available to that function and any other functions defined therein. Your function named ghost will only ever be available within the function where it is defined, regardless of when that function is evaluated.
What you can do, however, is write a function that returns a function. You can then call that function and assign the result to a variable in the scope where you want to expose functionality. Doing that would look something like this.
function defineSpecialAlert() {
return function(name) {
alert(name + "!");
};
}
var newlyDefinedMethod = defineSpecialAlert();
newlyDefinedMethod("Baal");
So if I understand, it seems like you want to create an alias of eval: Something like
#Note this code is not intended as a solution, but demonstrates
#an attempt that is guaranteed to fail.
#
function myAlias(ctx) {
eval.call(ctx, 'var ghost = 42');
}
myAlias(this);
alert(ghost);
Javascript allows many funky sleight-of-hand tricks especially with closures, but this is maybe the one impossible thing that javascript cannot do. I've tried at length to do this exact same thing, and I can tell you that you'll run into nothing but complaints from the browser, saying that eval cannot be re-contexted or aliased in any way.
I am trying to assign the ajax callback function variable value to the existing variable.
I have
function test(){
}
test.prototype.click=function(){
this.count;
//call ajax codes.....
//ajax callback function
ajax.callback=function(var1){
//I want to assign returned data to this.count property.
this.count=var1.length
}
}
test.prototype.show=function(){
//wont work, it will show undefined...
alert(this.count);
}
var t=new test();
t.click();
t.show();
I think it's the scope issue but I don't know how to solve this. Any idea? Thanks in advance.
Yeah, using this within another scope causes all kinds of issues, so you need to work around this. One way is to avoid using this entirely by defining your function differently. For instance, you can define count like so:
function test() {
function count() {
}
...
And just use count() without the this. prefix.
You can also set a variable to this and use that to refer to count within your other scope. For instance:
var self = this;
Scoping issues with this can be a pain in the neck and can occur when you do more OO with callbacks. It's good you got introduced to this early on, so now you know to be on guard.
I am in a position where I need to "update" a function that exists in another javascript file. The file looks like this:
function jf(){
alert('1');
}
//call jf periodically
jf();
The second js file, which is loaded after looks like this:
console.log(jf);
console.log(window.jf);
var func=function(){
alert('2');
};
jf=func;
window.jf=func;
The first log successfully returns the original jf method, the second doesnt. The first set seems to set the local variable jf, and the second does basically nothing. Is there a way to achieve this functionality?
According to Javascript closures - behavior of overridden functions from the global scope
var done = and function done do basicaly the same thing. They will shadow the outer definition in the inner scope but they will not replace it on the outer scope.
This means you can only override your initial definition of function jf() if you are in the same execution context. Otherwise, replace function jf(){ ... with window.jf = function(){...
Also, running your tests in an inspector console might help.
First, use variables:
var jf = function () {
alert('1');
};
jf();
Then the second bit should work fine:
var func = function () {
alert('2');
};
jf = func;
jf();