I have a question about the annotated underscore.js source code:
It says:
Internal function that returns an efficient (for current engines) version of the passed-in callback, to be repeatedly applied in other Underscore functions.
var createCallback = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
Explicitly, how is reached the efficiency in this block of code ?
Cause I see that with the wrapper, is created one more level of indirection and the scope chain grows one more level two.
I need to understand how is reached the efficiency, in order to apply this tricks in my own js.
Thanks!
There seems to be some shuffling of argument order but some of the cases are actually equivalent if later arguments were left empty. I think that logic also probably makes the callback play nicely with browser optimisations. For example, if the return value is predictable then the result may be inlined rather than calling the actual function each time.
Here is a related discussion about performance and arguments, where testing showed some big speed differences:
Performance penalty for undefined arguments
When using the underscore iterator functions (such as _.map, _.reduce and _.each), the callbacks that are provided offer different arguments. For example:
_.map(list, function(value, key, list) {});
_.reduce(list, function(memo, num, list) {});
The createCallback is a convenience function used internally by underscore to define the order of the arguments in these iterator functions. If we look at the _.reduce function we can see this line:
iteratee = createCallback(iteratee, context, 4);
Which will output:
func.call(context, accumulator, value, index, collection);
Where func is the iterator function provided to _.reduce
Related
I ran across some code I'm trying to refactor where they were writing JavaScript as a string and putting it inside HTML script tags, then writing it to the DOM. Very ugly and not maintainable. But one of the things this allowed them to do was build a function call by appending to a string.
var methods = '';
for (key in obj) {
methods += 'func1("'+key+'", "'+obj[key]+'").';
}
var scriptString = '<script>func2().' + methods + 'func3();</script>'
The result could be:
'<script>func2().func1("key1", "value1").func1("key2", "value2").func3();</script>'
So, since I really disapprove of writing JavaScript inside an HTML string inside of JavaScript ... Does anyone know how to accomplish the same result with pure JavaScript? Is there a way to continuously append methods to a function call by iterating over an object?
Array.reduce() should do most of what you need.
The tricky part is the .func1() method call chain, where you depend on an object's key/value pairs. If you're not used to working with the Array.reduce() method, I would suggest reading through the MDN documentation, but it basically loops through an array, performing a transformation on the previous result until it reaches the end, where it returns the final result. This can be used to our advantage since method chaining is just a method call on the previous method call's return value. But, since that's an array method, we need to get the Object's entries into an array first...and that's where Object.entries() comes in.
Note that much of my syntax here involves new features, which may or may not be supported by your target browsers. Be sure to use a transpiler and polyfills to handle going backwards, if needed.
See below for an example:
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
wrapperFunc({
key1: 'value1',
key2: 'value2'
})
Also, here's a second, more complex example with some implemented methods. Unlike the example above, this one actually runs.
class MyObject {
constructor() {
this.innerString = ''
}
// Chainable method (returns this)
func1(key, val) {
this.innerString += `${key}, ${val} `
return this
}
func3() {
return this.innerString.trim()
}
}
const func2 = function () {
return new MyObject()
}
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
console.log(wrapperFunc({
key1: 'value1',
key2: 'value2'
}))
You can create a function that calls the func1 method repeatedly for all key/value pairs in the object.
var script = function(obj) {
var value = func2();
for (var key in obj) {
value = value.func1(key, obj[key]);
}
value.func3();
};
We can make a regular (POJSO) JS Object iterable, like so:
const tempObj = {a: 1, b: 2, c: 3};
tempObj[Symbol.iterator] = function () {
const self = this;
const keys = Object.keys(self);
return {
next() {
const k = keys.shift();
return {
done: !k,
value: [k, self[k]]
}
}
}
};
now we can use for..of loop:
for (let [k,v] of tempObj) {
console.log(k,v);
}
and we get:
a 1
b 2
c 3
my question is - is there another method we need to implement besides next()? if not, why did the iterator spec choose to return an object instead of just returning a function? why isn't the spec simply:
tempObj[Symbol.iterator] = function () {
return function next {
return {
done: Object.keys(this).length === 0,
value: Object.keys(this).shift()
}
}
};
my only guess is that by returning an object, it leaves room for updates/changes.
The Iterator interface also supports two more optional methods: return and throw. From the ES6 specification, section 25.1.1.2 (Table 54):
return
A function that returns an IteratorResult object. The returned
object must conform to the IteratorResult interface. Invoking this
method notifies the Iterator object that the caller does not intend to
make any more next method calls to the Iterator. The returned
IteratorResult object will typically have a done property whose value
is true, and a value property with the value passed as the argument of
the return method. However, this requirement is not enforced.
throw
A function that returns an IteratorResult object. The returned object
must conform to the IteratorResult interface. Invoking this method
notifies the Iterator object that the caller has detected an error
condition. The argument may be used to identify the error condition
and typically will be an exception object. A typical response is to
throw the value passed as the argument. If the method does not throw,
the returned IteratorResult object will typically have a done property
whose value is true.
The ES6 spec also says:
Typically callers of these methods should check for their existence before invoking them.
So you're definitely not required to implement them; the burden of checking for their existence is on the caller.
Is there another method we need to implement besides next()?
No, none that we need to implement, but we can implement throw and return for the full iterator interface. Generator objects do that, for example.
Why did the iterator spec choose to return an object instead of just returning a function?
Because an iterator is (usually) stateful, and from an OOP viewpoint it should be an object with a method not a (pure) function. This also allows prototypical inheritance for iterator instances.
You could yield a generator with Object.entries as value.
let tempObj = { a: 1, b: 2, c: 3 };
tempObj[Symbol.iterator] = function* () {
yield* Object.entries(this);
};
for (let [k, v] of tempObj) {
console.log(k, v);
}
I am using Underscore.js to expand my knowledge and understanding of more complex javascript concepts and was hoping someone could help me understand how exactly the _.iteratee function gets executed in a specific example.
Here is that example, with comments about my understanding thus far.
I am using the _.map function like so:
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
At the very bottom are the relevant functions being used, with some insignificant ones like _.keys being left out.
My understanding is this:
Within the _.map function, the first instance of iteratee within the body of the function is being set like so: iteratee = _.iteratee(iteratee, context); which based on the _.iteratee function (because an function is being passed into _.map) should evaluate to createCallback(value, context, argCount).
The next time the iteratee variable (which should now be a callback function) is used in the _.map function is here: results[index] = iteratee(obj[currentKey], currentKey, obj);.
This is where I get lost.
Questions:
Assuming my #1 assumption above is indeed correct, when we get to this line of the _.map function inside the loop: results[index] = iteratee(obj[currentKey], currentKey, obj); what we are actually calling is createCallback(obj[currentKey], currentKey, obj). So does obj[currentKey] get passed to the func parameter in createCallback? (Doesn't seem to make sense).
If the above is true, where I get lost is, when createCallback is evaluated, what is the value of obj which in createCallback is the argCount. I do not understand which part of the switch statement in createCallback gets referenced.
Which switch statement gets called in this case?
If I have that, I should be able to complete the trace to the closure inside createCallback. Any additional information you may provide to guide me is greatly appreciated.
Thanks.
Functions
_.map
_.map = _.collect = function(obj, iteratee, context) {
if (obj == null) return [];
iteratee = _.iteratee(iteratee, context);
var keys = obj.length !== +obj.length && _.keys(obj),
length = (keys || obj).length,
results = Array(length),
currentKey;
for (var index = 0; index < length; index++) {
currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
createCallback
var createCallback = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
_.iteratee
_.iteratee = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return createCallback(value, context, argCount);
if (_.isObject(value)) return _.matches(value);
return _.property(value);
};
In this particular line,
results[index] = iteratee(obj[currentKey], currentKey, obj);
iteratee will be actually called like this
iteratee(<actual value of obj[currentKey]>, currentKey, obj);
So, obj will not be passed on to the iteratee function.
In your case, since you are passing an object as the first argument, on each and every iteration the results accumulation line would evaluate something similar to this
results[0] = iteratee(1, "one", obj);
results[1] = iteratee(2, "two", obj);
results[2] = iteratee(3, "three", obj);
iteratee =
And when this line is executed,
_.iteratee(iteratee, context);
iteratee is actually the function you passed as one of the arguments to _.map. And since you are not passing a context object explicitly, by default, the value would be undefined. So,
if (context === void 0) return func;
check in the createCallback function will evaluated to true (since undefined == void 0), the function you actually passed to the _.map will be used as the call back function.
I'm struggling with an example of js memoization found on a book, here's the code:
Function.prototype.memoized = function(key){
this._values = this._values || {};
return this._values[key] !== undefined ? this._values[key] : this._values[key] = this.apply(this, arguments);
}
here's a fiddle with a complete example
what I don't really get is how this piece of code works and what it does, in particular the apply part:
return this._values[key] !== undefined ? this._values[key] : this._values[key] = this.apply(this, arguments);
I know and understand how apply works
The apply() method calls a function with a given this value and arguments provided as an array
suppose that this._values[key] is equal to undefined, then the returned value will be this.apply(this, arguments): does this code re-launch the memoized function? I've tried to add some logs inside the function to see how many times the function is called, but it seems it's been launched only once..
Can anyone please give me a hint? It's probably a dummy question, please be patient, thanks
Let's use a simple example, fibonacci numbers.
function fib(n) {
if (n < 2) return 1;
return fib.memoized(n-1) + fib.memoized(n-2);
}
Here we can see that the memoized method is applied on the fib function, i.e. your this keyword refers to the fib function. It does not relaunch the memoized function, but "launches" the function on which it was called. However, it does call it with this set to the function itself, which does not make any sense. Better:
Function.prototype.memoized = function(key){
if (!this._values)
this._values = {};
if (key in this._values)
return this._values[key];
else
return this._values[key] = this.apply(null, arguments);
// pass null here: ^^^^
}
Even better would be if memoized would return a closure:
Function.prototype.memoized = function(v) {
var fn = this, // the function on which "memoized" was called
values = v || {};
return function(key) {
if (key in values)
return values[key];
else
return values[key] = fn.apply(this, arguments);
}
}
var fib = function(n) {
if (n < 2) return 1;
return fib(n-1) + fib(n-2);
}.memoized();
// or even
var fib = function(n) { return fib(n-1) + fib(n-2) }.memoized({0:1, 1:1});
Notes
Since you are attaching memoized to the Function.prototype, you can invoke this memoized on some other function only. Like in your example
isPrime.memoized(5)
Since you are invoking memoized on a function, the this will be referring to the function on which the memoized is invoked. So, in this case, this refers to isPrime.
Actual explanation
this._values = this._values || {};
This line makes sure that the isPrime has got an attribute with the name _values and it should have an empty object, if it is not there already.
this._values[key] !== undefined
This check is to make sure that we have been already called with key or not. If the value is not undefined, then return this._values[key].
Otherwise,
this._values[key] = this.apply(this, arguments)
store the result of calling this.apply(this, arguments) in this._values[key] and return it. Now the important part.
this.apply(this, arguments)
It is straight forward. arguments is an array like object. So, If you have actually called isPrime like this isPrime(1, 2, 3, 4), arguments will have {'0': 1, '1': 2, '2': 3, '3': 4}. Now that we are inside memoized, we need to invoke isPrime as it was intended to be invoked. So, this.apply(this, arguments) is done. Function.prototype.apply, tries to spread the array like object passed as the second parameter, while invoking the function.
I am new to underscore.js. What is the purpose of [context] in _.each()? How should it be used?
The context parameter just sets the value of this in the iterator function.
var someOtherArray = ["name","patrick","d","w"];
_.each([1, 2, 3], function(num) {
// In here, "this" refers to the same Array as "someOtherArray"
alert( this[num] ); // num is the value from the array being iterated
// so this[num] gets the item at the "num" index of
// someOtherArray.
}, someOtherArray);
Working Example: http://jsfiddle.net/a6Rx4/
It uses the number from each member of the Array being iterated to get the item at that index of someOtherArray, which is represented by this since we passed it as the context parameter.
If you do not set the context, then this will refer to the window object.
Extras:
To answer the What's the advantage of that? Why not just refer to someOtherArray[num] rather than this[num]? upvoted question found in the comments below, let's move the anonymous iteratee callback into a function for easy re-use:
const someOtherArray = ["name","patrick","d","w"];
const yetAnotherArray = ["what","goes","here","?"];
function alertStr(num){
alert( this[num] );
}
_.each([1, 2, 3], alertStr, someOtherArray);
_.each([1, 2, 3], alertStr, yetAnotherArray);
You can see how the this reference allows us to re-use the iteratee function across multiple _.each calls with different context values. This would not work if we had the someOtherArray hardcoded inside the iteratee.
context is where this refers to in your iterator function. For example:
var person = {};
person.friends = {
name1: true,
name2: false,
name3: true,
name4: true
};
_.each(['name4', 'name2'], function(name){
// this refers to the friends property of the person object
alert(this[name]);
}, person.friends);
The context lets you provide arguments at call-time, allowing easy customization of generic pre-built helper functions.
some examples:
// stock footage:
function addTo(x){ "use strict"; return x + this; }
function pluck(x){ "use strict"; return x[this]; }
function lt(x){ "use strict"; return x < this; }
// production:
var r = [1,2,3,4,5,6,7,8,9];
var words = "a man a plan a canal panama".split(" ");
// filtering numbers:
_.filter(r, lt, 5); // elements less than 5
_.filter(r, lt, 3); // elements less than 3
// add 100 to the elements:
_.map(r, addTo, 100);
// encode eggy peggy:
_.map(words, addTo, "egg").join(" ");
// get length of words:
_.map(words, pluck, "length");
// find words starting with "e" or sooner:
_.filter(words, lt, "e");
// find all words with 3 or more chars:
_.filter(words, pluck, 2);
Even from the limited examples, you can see how powerful an "extra argument" can be for creating re-usable code. Instead of making a different callback function for each situation, you can usually adapt a low-level helper. The goal is to have your custom logic bundling a verb and two nouns, with minimal boilerplate.
Admittedly, arrow functions have eliminated a lot of the "code golf" advantages of generic pure functions, but the semantic and consistency advantages remain.
I always add "use strict" to helpers to provide native [].map() compatibility when passing primitives. Otherwise, they are coerced into objects, which usually still works, but it's faster and safer to be type-specific.
Simple use of _.each
_.each(['Hello', 'World!'], function(word){
console.log(word);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Here's simple example that could use _.each:
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
x.addItem('banana');
x.addItem('apple');
x.addItem('kiwi');
x.show();
Output:
items: [ 'banana', 'apple', 'kiwi' ]
Instead of calling addItem multiple times you could use underscore this way:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
which is identical to calling addItem three times sequentially with these items. Basically it iterates your array and for each item calls your anonymous callback function that calls x.addItem(item). The anonymous callback function is similar to addItem member function (e.g. it takes an item) and is kind of pointless. So, instead of going through anonymous function it's better that _.each avoids this indirection and calls addItem directly:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
but this won't work, as inside basket's addItem member function this won't refer to your x basket that you created. That's why you have an option to pass your basket x to be used as [context]:
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
Full example that uses _.each and context:
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
In short, if callback function that you pass to _.each in any way uses this then you need to specify what this should be referring to inside your callback function. It may seem like x is redundant in my example, but x.addItem is just a function and could be totally unrelated to x or basket or any other object, for example:
function basket() {
this.items = [];
this.show = function() {
console.log('items: ', this.items);
}
}
function addItem(item) {
this.items.push(item);
};
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
In other words, you bind some value to this inside your callback, or you may as well use bind directly like this:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
how this feature can be useful with some different underscore methods?
In general, if some underscorejs method takes a callback function and if you want that callback be called on some member function of some object (e.g. a function that uses this) then you may bind that function to some object or pass that object as the [context] parameter and that's the primary intention. And at the top of underscorejs documentation, that's exactly what they state: The iteratee is bound to the context object, if one is passed
As explained in other answers, context is the this context to be used inside callback passed to each.
I'll explain this with the help of source code of relevant methods from underscore source code
The definition of _.each or _.forEach is as follows:
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
Second statement is important to note here
iteratee = optimizeCb(iteratee, context);
Here, context is passed to another method optimizeCb and the returned function from it is then assigned to iteratee which is called later.
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1:
return function(value) {
return func.call(context, value);
};
case 2:
return function(value, other) {
return func.call(context, value, other);
};
case 3:
return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4:
return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
As can be seen from the above method definition of optimizeCb, if context is not passed then func is returned as it is. If context is passed, callback function is called as
func.call(context, other_parameters);
^^^^^^^
func is called with call() which is used to invoke a method by setting this context of it. So, when this is used inside func, it'll refer to context.
// Without `context`
_.each([1], function() {
console.log(this instanceof Window);
});
// With `context` as `arr`
var arr = [1, 2, 3];
_.each([1], function() {
console.log(this);
}, arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
You can consider context as the last optional parameter to forEach in JavaScript.