Let's say there is a function:
const foo = (arg) => { do_something };
Before I run do_something, I actually want to normalize arg to an array because the function accept string or types other than array.
I know it is a bad practice to directly modify the passed argument in a function because it may be mutated and resulted in bugs, which are hard to be debugged. So, I will do something like this:
const foo = (arg) => {
const _arg = Array.isArray(arg) ? [...arg] : [arg];
do_something(_arg)
};
As above, I actually do a shallow copy of that array(arg), and use _arg to do something afterward, and I will ensure not to mutate it (since it just a shallow copy). My question is what if I just don't copy it? i.e.:
const foo = (arg) => {
const _arg = Array.isArray(arg) ? arg : [arg];
do_something(_arg)
};
Will it be different? Or which one is a better/healthier way of coding? Will it affect the garbage collecting or any performance issue?
Thanks for the answer.
IMO, the first approach is recommended, because the second approach often leads to the unexpected issues if do_something causes some side effects.
Will it affect garbage collecting? Yes. With the first approach, _arg will be cleaned because nothing references to it. I'm not sure about performance. It depends on the size of data you handle.
My two cents on your concern is that in a language where there's no immutability by default, I would stick with coding conventions rather than trying to emulate immutability.
That is, if you state that arrays mustn't be modified and if a new element should be pushed or replaced you need to use Array.prototype.concat or array spread operator, I believe that your code will be simpler and easier to follow and maintain.
Also, you might want to use immutable collections like ones provided by ImmutableJS.
Related
Suppose I've a Set as a lookup table.
const myset = new Set(["key1", "key2", "key3", "keepMe"]);
I wanted to filter another array of some other keys (say mykeys) which are in myset.
const mykeys = ["ignoreMe", "keepMe", "ignoreMeToo", "key2"];
Question:
Why do I have to use
const filtered = mykeys.filter(k => myset.has(k))
instead of
const filtered = mykeys.filter(myset.has)
// TypeError: Method Set.prototype.has called on incompatible receiver undefined
i.e., why do I've to create an anonymous lambda function in filter? keys.has has same signature (argument - element, return boolean). A friend told me it's related to this.
Whereas mykeys.map(console.log) works without error (although not being of much use).
I came across this article at MDN and I still don't get why "'myset' is not captured as this". I understand the workaround but not the "why". Can anyone explain it with some details and references in a human friendly way?
Update: Thank you all for the responses. Maybe I wasn't clear about what I'm asking. I do understand the workarounds.
#charlietfl understood. Here's his comment, the thing I was looking for:
Because filter() has no implicit this where as set.has needs to have proper this context. Calling it inside anonymous function and manually adding argument makes the call self contained.
You could use thisArg of Array#filter with the set and the prototype of has as callback.
This pattern does not require a binding of an instance of Set to the prototype, because
If a thisArg parameter is provided to filter, it will be used as the callback's this value. Otherwise, the value undefined will be used as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
const
myset = new Set(["key1", "key2", "key3", "keepMe"]),
mykeys = ["ignoreMe", "keepMe", "ignoreMeToo", "key2"],
filtered = mykeys.filter(Set.prototype.has, myset);
console.log(filtered);
This is a fundamental design decision dating back to the first definition of the JavaScript language.
Consider an object
var myObjet = {
someValue: 0,
someFunction: function() {
return this.someValue;
}
};
Now, when you write
var myValue = myObject.someValue;
You get exactly what you have put in it, as if you had written
var myValue = 0;
Similarly, when you write
var myFunction = myObject.someValue;
You get exactly what you have put in it, as if you had written
var myFunction = (function() {
return this.someValue;
});
...except now, you are not in an object anymore. So this doesn't mean anything. Indeed, if you try
console.log(myFunction());
you will see
undefined
exactly as if you had written
console.log(this.someValue);
outside of any object.
So, what is this? Well, JavaScript decides it as follows:
If you write myObject.myFunction(), then when executing the myFunction() part, this is myObject.
If you just write myFunction(), then this is the current global object, which is generally window (not always, there are many special cases).
A number of functions can inject a this in another function (e.g. call, apply, map, ...)
Now, why does it do this? The answer is that this is necessary for prototypes. Indeed, if you now define
var myDerivedObject = Object.create(myObject);
myDerivedObjet.someValue = 42;
you now have an object based on myObject, but with a different property
someValue
console.log(myObject.someFunction()); // Shows 0
console.log(myDerivedObject.someFunction()); // Shows 42
That's because myObject.someFunction() uses myObject for this, while myDerivedObject.someFunction() uses myDerivedObject for this.
If this had been captured during the definition of someFunction, we would have obtained 0 in both lines, but this would also have made prototypes much less useful.
I have created a node module with a couple of custom methods for arrays and strings.
First I just used it like a regular module and got the functions from a require like this:
Alt 1.
const invSlice = require('inverted-slice');
let arr1 = [1,2,3,4];
invSlice.iSlice(arr, start, stop);
This works but it would be nicer to call iSlice as a method on the Array object. I solved this by adding the following code in my library:
Array.prototype.iSlice = iSliceBuiltin; // iSliceBuiltin is my function
And the method can now be used like:
Alt 2.
require('inverted-slice');
let arr1 = [1,2,3,4];
arr1.iSlice(start, stop);
Which I think is nicer then Alt 1.
Question
My question is if there is any best practice or guidelines to follow when adding custom methods like in Alt 2 to built-in Objects like Array or String ?
Extending built-in prototypes has always triggered debates, and I think we can conclude it is not considered best practice.
On the other hand it is indeed nice if you can call these custom methods as object methods instead of plain functions.
You might consider a wrapper function that will return an Array instance that has the extra methods defined for it: i.e., not on the prototype, but on the Array instance itself.
Your module could look like this:
function iArray(arr) {
return Object.assign([], arr || [], {
iSlice: iSliceBuiltin,
iSplice: iSpliceBuiltin
});
}
// ... your module functions come here, but excluding the changes to the Array prototype
module.exports = {
iArray
}
Then you would use it like this:
const iArray = require('inverted-slice');
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1);
To allow chaining, you could change the return statement in iSliceSpliceHelper to:
return iArray(newArr);
So, now you can write:
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1).iSlice(1, 2);
Existing libraries might implement your alternative 1 (e.g. underscore), but many also go for something like I propose here. See for instance Sugar (new Sugar.Array([1,2,3])), or Lazy (Lazy([1,2,3])).
In small doses I think it's not that big of a deal to use Alt 2, but I believe that over usage can create problems. If I remember correctly, they had to completely redo Cut The Rope due to performance problems that I believe stemmed largely in part from prototype extensions. You may also want to consider posting this on https://codereview.stackexchange.com/
A couple references:
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
https://softwareengineering.stackexchange.com/questions/104320/why-is-extending-the-dom-built-in-object-prototypes-a-bad-idea
Recently I've been getting into the javascript ecosystem. After sometime with javascript's callbacks I started asking myself if the javascript interpreters are capable of doing conditional evaluation of callback arguments. Let's take the following two example:
var a = 1;
var b = 2;
// example 1
abc.func(a, b, function (res) {
// do something with res
});
// example 2
abc.func(a, b, function () {
// do something
});
From what I understand, Javascript uses the arguments object to keep track of what is passed into a function. This is regardless of what the function definition is. So assuming that:
abc.func = function (a, b, cb) {
// do stuff
var res = {};
// Expensive computation to populate res
cb(res);
}
In both examples (1, 2) the res object will be passed to arguments[0]. In example 1 res === arguments[0] since the res parameter is defined.
Let's assume that computing res is expensive. In example 1 it's ok to go through this computation since the res object is used. In example 2, since the res object is not used, there really is no point in doing that computation. Although, since the arguments object needs to be populated, in both cases the computation to populate res is done. Is this correct ?
Assuming that's true, this seems like a (potentially) huge waste. Why compute something that's going to go out of scope and be garbage collected ? Think of all the libraries out there that use callbacks. A lot of them send multiple arguments back to the callback function, but sometimes none of them are used.
Is there a way to prevent this behaviour. Essentially make the Javascript interpreter smart enough to not compute those specific variables that will turn into unused arguments ? So in example 2 the res object would not actually be computed since it will never actually be used.
I understand that until this point things like this were used:
function xyz(a, b /*, rest */)
// use arguments to iterate over rest
}
So by default it makes sense to still compute those arguments. Now let's look forward to ECMAScript 2015. This will include the ...rest parameter to be defined. So for engines that support the new version, is there a way to enable conditional evaluation? This would make much more sense, since now there is a way to explicitly ask to evaluate and pass in all extra arguments to a function.
No, JavaScript is not a lazy call-by-name language. This is mostly because expressions can have side effects, and the ES standard requires them to be executed in the order the programmer expects them.
Yes, JS engines are smart. If they do detect that code does not execute side effects, and its results are not used anywhere, it just dumps them (dead code elimination). I'm not sure whether this works across function boundaries, I guess it doesn't, but if you are in a hot code path and the call does get inlined, it might be optimised.
So if you know that you are doing a heavy computation, you may want to make it lazy explicitly by passing a thunk. In an eagerly evaluated language, this is typically simply represented by a function that takes no parameters. In your case:
abc.func = function (a, b, cb) {
// do stuff
var res = {};
cb(() => {
// Expensive computation to populate res
return res;
});
}
// example 1
abc.func(a, b, function(getRes) {
var res = getRes();
// do something with res
});
// example 2
abc.func(a, b, function() {
// no heavy computation
// do something
});
You couldn't do that on an interpreter level, it's not feasible to determine whether or not computing a argument was dependent on computing another argument, and even if you could this would create inconsistent behaviour for the user. And because passing in variables into a function is extremely cheap, this becomes a pointless exercise.
It could be done on a functional level - if you wanted to you could pass the expected arguments of the callback as a parameter to the function, thereby augmenting the behaviour of the function based on the parameters, which is commonplace.
I was reading the source code for pallet.js and came across this.
var ret = (function(proto) {
return {
slice: function(arr, opt_begin, opt_end) {
return proto.slice.apply(arr, proto.slice.call(arguments, 1));
},
extend: function(arr, arr2) {
proto.push.apply(arr, arr2);
}
};
})(Array.prototype);
var slice = ret.slice;
var extend = ret.extend;
Why is this necessary? Why could they not simply write this:
var slice = function(arr,opt_begin,opt_end) {
return Array.prototype.slice.apply(arr,[opt_begin,opt_end]));
}
var extend = function(arr,arr2) {
return Array.prototype.push.apply(arr,arr2);
}
EDIT 1:
In response to the duplicate question. I don't think it is a duplicate, but that question definitely does address my question. So it is an optimization. But won't each one only be evaluated once? So is there really a significant improvement here for two function calls?
Also if we are worried about performance why are we calling proto.slice.call(arguments,1) instead of constructing the array of two elements by hand [opt_begin,opt_end], is slice faster?
Because the syntax is just so much cooler. Plus you can rationalize it's use by telling yourself that it's more DRY. You didn't have to type Array.prototype twice.
I can't be sure what was the original rationale behind that code (only the author knows) but I can see a few differences:
proto is a closed-over local variable, while instead Array is a global. It's possible for a smart enough Javascript engine to optimize access because proto is never changed and thus it could even be captured by value, not reference. proto.slice can be faster than Array.prototype.slice because one lookup less is needed.
passing opt_begin and opt_end as undefined is not the same as not passing them in general. The called function can know if a parameter was passed and happens to be undefined or if instead it wasn't passed. Using proto.slice.call(arguments, 1) ensures that the parameters are passed to slice only if they were actually passed to the closure.
I'm currently reading through this jquery masking plugin to try and understand how it works, and in numerous places the author calls the slice() function passing no arguments to it. For instance here the _buffer variable is slice()d, and _buffer.slice() and _buffer seem to hold the same values.
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
//functionality fn
function unmaskedvalue($input, skipDatepickerCheck) {
var input = $input[0];
if (tests && (skipDatepickerCheck === true || !$input.hasClass('hasDatepicker'))) {
var buffer = _buffer.slice();
checkVal(input, buffer);
return $.map(buffer, function(element, index) {
return isMask(index) && element != getBufferElement(_buffer.slice(), index) ? element : null; }).join('');
}
else {
return input._valueGet();
}
}
The .slice() method makes a (shallow) copy of an array, and takes parameters to indicate which subset of the source array to copy. Calling it with no arguments just copies the entire array. That is:
_buffer.slice();
// is equivalent to
_buffer.slice(0);
// also equivalent to
_buffer.slice(0, _buffer.length);
EDIT: Isn't the start index mandatory? Yes. And no. Sort of. JavaScript references (like MDN) usually say that .slice() requires at least one argument, the start index. Calling .slice() with no arguments is like saying .slice(undefined). In the ECMAScript Language Spec, step 5 in the .slice() algorithm says "Let relativeStart be ToInteger(start)". If you look at the algorithm for the abstract operation ToInteger(), which in turn uses ToNumber(), you'll see that it ends up converting undefined to 0.
Still, in my own code I would always say .slice(0), not .slice() - to me it seems neater.
array.slice() = array shallow copy and is a shorter form of array.slice()
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
Yes there may be a reason in the following cases (for which we do not have a clue, on whether they apply, in the provided code):
checkVal() or getBufferElement() modify the content of the arrays passed to them (as second and first argument respectively). In this case the code author wants to prevent the global variable _buffer's content from being modified when calling unmaskedvalue().
The function passed to $.map runs asynchronously. In this case the code author wants to make sure that the passed callback will access the array content as it was during unmaskedvalue() execution (e.g. Another event handler could modify _buffer content after unmaskedvalue() execution and before $.map's callback execution).
If none of the above is the case then, yes, the code would equally work without using .slice(). In this case maybe the code author wants to play safe and avoid bugs from future code changes that would result in unforeseen _buffer content modifications.
Note:
When saying: "prevent the global variable _buffer's content from being modified" it means to achieve the following:
_buffer[0].someProp = "new value" would reflect in the copied array.
_buffer[0] = "new value" would not reflect in the copied array.
(For preventing changes also in the first bullet above, array deep clone can be used, but this is out of the discussed context)
Note 2:
In ES6
var buffer = _buffer.slice();
can also be written as
var buffer = [..._buffer];