javascript: access function in closure
Variables and functions declared in the current lexical scope cannot be accessed by name using []
syntax - only property keys (per your second example) can be looked up dynamically based on the contents of another variable.
The only way around this is to resort to eval
, which is almost never a good idea.
An exception applies for variables and functions declared in the global scope - those are actually properties of window
.
Calling a parent function inside a closure
The reason why is that this
in the callback function will typically refer to the global window scope.
this
is a common source of pain in JS developers and more often than not it doesn't refer to what you think it may. You can search the full details online.
Why not closure it like this?
function Thing(request, response) {
this.foo = function() {
console.log("this is foo");
}
this.doThing = function() {
var self = this; //Closure to the Thing function
// Get SOAP args
args = { foo: this.request.cookies.foo };
// From the SOAP npm package library
soap.createClient(function(err, client) {
client.doSomething(args, function(err, result) {
self.foo();
});
});
}
}
How To Call a func within a Closure
To handle this situation you have multiple option.
Create
delegate/protocol
with your Location class- Create one protocol and implement that protocol method with your
ViewController
and declare its instance in yourLocation
class. After then in thecompletionHandler
ofreverseGeocodeLocation
call this delegate method. Check Apple documentation onProtocol
for more details.
- Create one protocol and implement that protocol method with your
You can create
completionHandler
with yourgetLocationName
method ofLocation
class.Add
completionHandler
withgetLocationName
and called thatcompletionHandler
inside thecompletionHandler
ofreverseGeocodeLocation
like this way.func getLocationName(completionHandler: @escaping (_ success: Bool) -> Void) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
completionHandler(false)
return
}
if let city = addressDict["City"] as? String {
self.currentCity = city
print(city)
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
if let country = addressDict["Country"] as? String {
print(country)
}
completionHandler(true)
//self.nowUpdateUI()
})
}Now in
ViewController
where you are calling this function call yourupdateUI
method inside the completion block.Location.sharedInstance.getLocationName { (success) in
if success {//If successfully got response
self.updateUI()
}
}
You can add observer for
(NS)NotificationCenter
.- Register the observer with
(NS)NotificationCenter
and then post the notification inside thecompletionHandler
ofreverseGeocodeLocation
. You can get more detail on this with this StackOverflow Post.
- Register the observer with
Calling a function within a closure in python
In the absence of lambda
I might look into functools.partial
. If that isn't allowed, you could probably do:
import threading
def func1(a,b):
def func2():
def func4():
return func3(a,b)
return threading.Thread(target=func4)
return func2
func2=func1(a,b)
func2()
call a function in a closure using call or apply?
If Process is in the global scope, you can walk the tree to get the reference to the method so you can execute it.
var Process = (function(Process, $) {
_thing1 = function(a, b, c) {
return a + b + c;
}
_thing2 = function(a, b, c) {
return $(a).html(b + c);
}
return {
Fred: {
Add: _thing1
},
Bob: {
Add: _thing2
}
}
}(Process || {}, jQuery));
var data = {
"function": "Process.Fred.Add",
"params": [1, 2, 3]
}
var result = data.function.split(/\./g) //break up string into pieces
.reduce((o, k) => o[k], window) //walk the tree to get method reference
.apply(this, data.params); //execute the method with the parameters
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
javascript, about closure function call
The reason you are getting 123 all the time is because every time you click on the button, you take a new inner function with a closure on counter variable with value 0; So value of counter
always remains 0 and when you add 123 to 0 you get 123.
If you move closure part out of the event handler, you would get exact same result as in the first case.
Notice the line var inner = add(); //<---Notice this line
. This would take the closure one time and subsequently you will keep increasing the value of counter
.
Also, notice this line inside myFunction
:
document.getElementById("demo").innerHTML = inner.call(this, 123);
Here we are calling the inner function that we had reference on earlier.
/*var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();*/
function add(test) {
var counter = 0;
return function(test) {
return counter += test;
}
}
var inner = add(); //<---Notice this line
function myFunction() {
//document.getElementById("demo").innerHTML = add(123);
document.getElementById("demo").innerHTML = inner.call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
Trigger an event to call a function that's inside of a closure
You can define a variable var press;
outside of WM
, inside of WM
, remove var
before press
and set press = function() {}
. You should then be able to call press(down)
at click of button
var press;
press = function(e) {
console.log(e);
// Mouse down location
var sizeHotspotStartX, toolIndex,
mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX),
mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY);
var elementPos = getElementPos(
document.getElementById(options.canvasElementId
|| 'canvasDiv')
);
mouseX -= elementPos.x;
mouseY -= elementPos.y;
announce(mouseX, mouseY);
};
jQuery(document).ready(function() {
jQuery('#buttonDiv').on('click', 'button', function() {
var down = jQuery.Event("mousedown", {
pageX: 50,
pageY: 50
});
press(down); // call `press` at `button` click
//jQuery('#canvasDiv canvas').trigger(down);
});
});
// based on http://www.williammalone.com/projects/html5-canvas-javascript-drawing-app-with-bucket-tool/
var press;
var WM = {};
WM.drawingApp = function(options) {
"use strict";
var canvas, context,
// Add mouse and touch event listeners to the canvas
createUserEvents = function() {
var getElementPos = function(element) {
var parentOffset, pos;
if (!element) {
pos = {
x: 0,
y: 0
};
} else {
pos = {
x: element.offsetLeft,
y: element.offsetTop
};
if (element.offsetParent) {
parentOffset = getElementPos(element.offsetParent);
pos.x += parentOffset.x;
pos.y += parentOffset.y;
}
}
return pos;
};
press = function(e) {
console.log(e)
// Mouse down location
var sizeHotspotStartX, toolIndex,
mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX),
mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY);
var elementPos = getElementPos(document.getElementById(options.canvasElementId || 'canvasDiv'));
mouseX -= elementPos.x;
mouseY -= elementPos.y;
announce(mouseX, mouseY);
};
var announce = function(x,y) { alert('press at: ' + x + ', ' + y); }
// Add mouse event listeners to canvas element
canvas.addEventListener("mousedown", press, false);
},
// Creates a canvas element, loads images, adds events, and draws the canvas for the first time.
init = function() {
// Create the canvas (Neccessary for IE because it doesn't know what a canvas element is)
canvas = document.createElement('canvas');
canvas.setAttribute('width', 100);
canvas.setAttribute('height', 100);
canvas.setAttribute('id', 'canvas');
document.getElementById(options.canvasElementId || 'canvasDiv').appendChild(canvas);
context = canvas.getContext("2d"); // Grab the 2d canvas context
createUserEvents();
};
init();
return {};
};
jQuery(document).ready(function() {
jQuery('#buttonDiv').on('click', 'button', function() {
var down = jQuery.Event("mousedown", {
pageX: 50,
pageY: 50
});
press(down)
//jQuery('#canvasDiv canvas').trigger(down);
});
});
var drawingApp = WM.drawingApp({
canvasElementId: "canvasDiv"
});
#canvasDiv canvas {
border: solid black 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div id="canvasDiv"></div>
<div id="buttonDiv">
<button>why can't I send a click to the canvas?</button>
</div>
Related Topics
Update Firebase Multi-Location with Error: Path Is an Ancestor of Path. Swift
Disable Email Detection in Swiftui's Text
Code Only Retrieving One Value from Data in Firebase
Firestore Security Rules Breaking with Update Rule
Xcode Swift 3: Timer and Segue View Controller Error
.Sink Is Not Returning the Promise Values from a Future Publisher
How to Know When to Call a Closure with () or Without Parentheses in Handler:
How to Procedurally Draw Rectangle/Lines in Swift Using Cgcontext
Swift: How to Invalidate a Timer If the Timer Starts from a Function
Cannot Stop Background Music from Within Game Scenes, Swift 3/Spritekit
Swift Why Isn't My Date Object That's (Equatable) Equal After Converting It to a String and Back
Does _Arraytype or _Arrayprotocol Not Available in Swift 3.1
How to Sort an Array of Posts by Their Elements
How to Deallocate All References Elements from an Array
How to Check for Value in Firebase That Is Held Under a Autoid Child