3 ms·
Thanks for the mention! Creating FixDS was definitely one of those "aha!" moments for me. Like every other Windows programmer of that era, I just assumed that a
by Stratoscope 10y ago
Thanks for the mention! Creating FixDS was definitely one of those "aha!" moments for me. Like every other Windows programmer of that era, I just assumed that all the rigmarole with EXPORTS and MakeProcInstance() was necessary. It was simply How Things Are Done.
So seeing that EXPORTS and MakeProcInstance() existed solely for the purpose of loading the DS register, and finding that in application code the correct value was already in the SS register and could simply be copied over into DS? That was quite a surprise. Why were we all going to so much work when it wasn't necessary at all?
Ever since then, I've had my eye out for places where the conventional wisdom tells us to write code we don't need.
One that I've seen a lot lately - especially in StackOverflow answers - is an unnecessarily complicated way of creating closures in JavaScript.
Many JavaScript programmers seem to believe that the way you create a closure is by writing a function that returns a function. That works, of course, but unless you really need a function that returns a function, it's a complication you can do without. After all, a simple function call can create a closure all by itself.
For example, someone is writing a Google Maps API app that takes an array of data about different places, loops through the array with a for loop, and creates a marker for each of those places along with a click event handler for each marker.
Experienced JavaScript programmers can probably guess what the problem is going to be. I'll simplify the code a bit here to illustrate:
var places = [
{ name: "One", lat: 34.1, lng: -80.7 },
{ name: "Two", lat: 35.2, lng: -81.8 }
];
var infowindow = new google.maps.InfoWindow();
for( var i = 0; i < places.length; i++ ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
places[i].lat,
places[i].lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( places[i].name );
infowindow.open( map, marker );
});
}
The problem here, of course, is that when the click callback function is called asynchronously, the for loop has already run to completion, and the i and marker variables no longer have the correct values for the marker that was clicked.
Inevitably, someone will now answer this question by suggesting a closure, and illustrating how you get a closure by using a function that returns a function:
for( var i = 0; i < places.length; i++ ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
places[i].lat,
places[i].lng
),
map: map
});
// Changes are below, using a function that returns a function
google.maps.event.addListener( marker, 'click', (function( marker, i ) {
return function() {
infowindow.setContent( places[i].name );
infowindow.open( map, marker );
}
})( marker, i );
}
While that works, it's harder to understand than it needs to be. It's like using EXPORTS and MakeProcInstance() to load the DS register.
There is a simpler way:
for( var i = 0; i < places.length; i++ ) {
addMarker( places[i] );
}
function addMarker( place ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
place.lat,
place.lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( place.name );
infowindow.open( map, marker );
});
}
By simply calling a function in the loop, we create the closure we need, and we get simpler and more readable code too.
Of course you can achieve the same effect by using array.forEach(), because that calls a function and gives you a closure automatically:
places.forEach( function( place ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
place.lat,
place.lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( place.name );
infowindow.open( map, marker );
});
});
Here's a search that will show numerous StackOverflow answers that promote the complicated function-returning-a-function pattern:
https://www.google.com/search?q=stackoverflow+maps+api+marker+closure+function+returns+function https://www.google.com/search?q=stackoverflow+maps+api+marke...
- userbinator 10y agoAwesome to see the Michael Geary show up on HN! I believe MakeProcInstance was originally created in order to allow apps with multiple data or stack segments ('huge' memory model), but as you noted, is certainly not necessary for the majority of them. In fact, if you were careful or using Asm, even the 64K tiny model would've been sufficient for a lot of applications. ...and I never thought I'd see x86 segment registers and JavaScript closures mentioned in the same comment.
- EdSharkey 10y agoYou get a closure whenever you evaluate a function statement. When a statement containing the 'function' keyword is evaluated, you get a closure implicitly instantiated for that new function instance. Invoking an already existing function instance doesn't make a new closure - it keeps its existing one. Evaluating a statement containing the 'function' keyword does make a new closure. A closure links to the enclosing function's scope, all the way back to the root/global scope (the window object on browsers). Memory leaks can occur when closure scopes link infinitely, so it's handy to use the function-returns-function pattern (but not the way you described it) to 'reset' the closure links to something near the root scope. JS module systems like CommonJS have done well to encourage very tidy closure scope chains even in extremely complex javascript programs.
- rasz_pl 10y agohttps://en.wikipedia.org/wiki/Immediately-invoked_function_expression https://en.wikipedia.org/wiki/Immediately-invoked_function_e... first "bad" example will work just fine if you replace "var i/marker" with "let i/marker"