Can you use a previously defined parameter for defaulting other parameters?
I have an Object with an update function which will update one item in a list.
Therefore I pass a data object into the function and the specified object will be updated.
class Device {
constructor() {
this.systems = [new System()];
// ...
}
update(data, index) {
this.systems[index].update(data);
}
}
But I want to call it with the index optional. Meaning assigning the data.index property as a default value.
Not: new Device().update(data, data.index);
You want the index in the data object, something like that:
function test({ index = 42, ...data } = {}) {
console.log(index, data);
}
test();
test({ hello: 'world' });
test({ index: 100, a: 'b' });
It does work but I'm not sure if it is supposed to be used.
function test(a, b = a) {
return a + b;
}
When calling the function with only one parameter b will be set to a.
test(2); will return 4 and test(2, 3); will return 5.
Of course this won't work the other way around.
function test2(a = b, a) {
return a + b;
}
Calling test2(undefined, 2); will throw Uncaught ReferenceError: Cannot access 'b' before initialization as kind of expected.
The parameter used as a default value must be specified earlier on.
You could to this directly as parameter:
function multiply(a, b = a) {
return a * b;
}
Or checking for an undefined
function multiply(a, b) {
b = (typeof b !== 'undefined') ? b : a;
return a * b;
}
Depends if you want to have it directly as parameter or in the method body.
Have a look: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Default_parameters
I'm working through and trying to understand the source code of https://trianglify.io/ which can be found at https://github.com/qrohlf/trianglify. In the lib/trianglify.js file, I come across the following lines of code:
var x_color = chroma.scale(opts.x_colors).mode(opts.color_space);
var y_color = chroma.scale(opts.y_colors).mode(opts.color_space);
gradient = function(x, y) {
return chroma.interpolate(x_color(x), y_color(y), 0.5, opts.color_space);
};
My question is when the x_color(x) gets called, where does the "x" argument go? How does this argument get passed into the function if it doesn't appear in the definition? My main purpose for doing this is to add some extra custom parameters to x_color() but I can't do that if I have no idea how the parameters even get processed in the function.
EDIT
The .mode(opts.color_space) function can be found at https://github.com/gka/chroma.js/blob/master/src/scale.coffee line 158. It reads as follows:
f.mode = (_m) ->
if not arguments.length
return _mode
_mode = _m
resetCache()
f
not sure what to make of this since my coffeescript knowledge is limited.
chroma is part of chroma.js.
Looking at the code, chroma.scale(...) constructs a function with prototypes with fluent methods.
f = function(v) {
var c;
c = chroma(getColor(v));
if (_out && c[_out]) {
return c[_out]();
} else {
return c;
}
};
f.mode = function(_m) {
if (!arguments.length) {
return _mode;
}
_mode = _m;
resetCache();
return f;
};
So when you call chroma.scale(...) it returns f, and then when you call .mode(...) on the returned object, it again returns the same instance f.
The instance of f is created by the following method:
chroma = function() {
if (arguments[0] instanceof Color) {
return arguments[0];
}
return (function(func, args, ctor) {
ctor.prototype = func.prototype;
var child = new ctor, result = func.apply(child, args);
return Object(result) === result ? result : child;
})(Color, arguments, function(){});
};
As you can see, this makes use of the arguments object. Mozilla defines the arguments object as:
The arguments object is an Array-like object corresponding to the arguments passed to a function.
In short, even if you do not specify parameter names in the function signature, the arguments object will still exist, and any parameters you pass will exist in the arguments array.
I've created an example of using the arguments array here
function a() {
alert(arguments[0] + ' is ' + arguments[1] + ' years old.');
}
function b(name) {
return function test() {
alert(name + ' will vw ' + arguments[0] + ' years old on his next birthday.');
}
}
a('John', 29);
var example2 = b('John');
example2(30);
In the first example, there is a straight function call, but in the second example I am returning an actual function from the b() method, and then I'm calling that returned function.
Given the following piece of code:
function Foo() {};
Foo.prototype.one = fluent(function(a , b) {
return a + b;
});
Foo.prototype.two = fluent(function(c) {
var d = c + 0.15; //0.15 cause I just couldnt thougth anything else at this moment...
return d;
});
ok that's all good for the moment, now let's say fluent is a decorator function that allows me to implement it like this:
var test = new Foo();
test.one(10, 5).two(); //here is the problem...
Thinking as it was a promise, how can I modify this code in order to make the returned valued of one available on two??? meaning, c should be the returned valued of one(), while keeping the sample implementation.
Here is the fiddle;
I would propose the following definition of fluent. Note that if needed, the final return value is in this.$lastReturn:
function fluent(impl) {
return function() {
// Convert arguments to a real array
var args = Array.prototype.slice.call(arguments);
// Prepend the last return value for this object
if(typeof this.$lastReturn != 'undefined')
args.unshift(this.$lastReturn);
// Invoke the function and save the return value
this.$lastReturn = impl.apply(this, args);
// Return this to allow chaining of the next fluent call
return this;
}
}
This solution utilised the answer of Dark Falcon and makes a little extent to the feature of returning a value or the chain.
Javascript offers the possibillity to request a primitive value of the object, Object.prototype.valueOf()
. In this case it may be used to get a value in case we need a value and on other cases, there is the object returned.
For more information have a look to this article Object-to-Primitive Conversions in JavaScript.
Another addition is argument control at fluent and the call of the methods. If arguments are given, then the arguments are taken, if not given, then the this.$lastreturn is used.
function fluent(impl) {
return function () {
var args = Array.prototype.slice.call(arguments);
// Prepend the last return value for this object only if arg length is 0
if (!args.length && typeof this.$lastReturn !== 'undefined') {
args.unshift(this.$lastReturn);
}
this.$lastReturn = impl.apply(this, args);
return this;
}
}
function Foo() { };
Foo.prototype.one = fluent(function (a, b) {
return a + b;
});
Foo.prototype.two = fluent( function (c) {
return c + 0.77;
});
// this returns the primitive value
Foo.prototype.valueOf = function (c) {
return this.$lastReturn;
};
var test = new Foo();
var x = test.one(10, 5);
document.write(x + '<br>'); // 15
document.write(typeof x + '<br>'); // object
var y = x.two();
document.write(y + '<br>'); // 15.77
document.write(typeof y + '<br>'); // object
var z = y.two(35);
document.write(z + '<br>'); // 35.77
document.write(typeof z + '<br>'); // object
In Javascript, how can I bind arguments to a function without binding the this parameter?
For example:
//Example function.
var c = function(a, b, c, callback) {};
//Bind values 1, 2, and 3 to a, b, and c, leave callback unbound.
var b = c.bind(null, 1, 2, 3); //How can I do this without binding scope?
How can I avoid the side-effect of having to bind the function's scope (e.g. setting this = null) as well?
Edit:
Sorry for the confusion. I want to bind arguments, then be able to call the bound function later and have it behave exactly as if I called the original function and passed it the bound arguments:
var x = 'outside object';
var obj = {
x: 'inside object',
c: function(a, b, c, callback) {
console.log(this.x);
}
};
var b = obj.c.bind(null, 1, 2, 3);
//These should both have exact same output.
obj.c(1, 2, 3, function(){});
b(function(){});
//The following works, but I was hoping there was a better way:
var b = obj.c.bind(obj, 1, 2, 3); //Anyway to make it work without typing obj twice?
I'm still new at this, sorry for the confusion.
Thanks!
You can do this, but best to avoid thinking of it as "binding" since that is the term used for setting the "this" value. Perhaps think of it as "wrapping" the arguments into a function?
What you do is create a function that has the desired arguments built into it via closures:
var withWrappedArguments = function(arg1, arg2)
{
return function() { ... do your stuff with arg1 and arg2 ... };
}(actualArg1Value, actualArg2Value);
Hope I got the syntax right there. What it does is create a function called withWrappedArguments() (to be pedantic it is an anonymous function assigned to the variable) that you can call any time any where and will always act with actualArg1Value and actualArg2Value, and anything else you want to put in there. You can also have it accept further arguments at the time of the call if you want. The secret is the parentheses after the final closing brace. These cause the outer function to be immediately executed, with the passed values, and to generate the inner function that can be called later. The passed values are then frozen at the time the function is generated.
This is effectively what bind does, but this way it is explicit that the wrapped arguments are simply closures on local variables, and there is no need to change the behaviour of this.
In ES6, this is easily done using rest parameters in conjunction with the spread operator.
So we can define a function bindArgs that works like bind, except that only arguments are bound, but not the context (this).
Function.prototype.bindArgs =
function (...boundArgs)
{
const targetFunction = this;
return function (...args) { return targetFunction.call(this, ...boundArgs, ...args); };
};
Then, for a specified function foo and an object obj, the statement
return foo.call(obj, 1, 2, 3, 4);
is equivalent to
let bar = foo.bindArgs(1, 2);
return bar.call(obj, 3, 4);
where only the first and second arguments are bound to bar, while the context obj specified in the invocation is used and extra arguments are appended after the bound arguments. The return value is simply forwarded.
In the native bind method the this value in the result function is lost. However, you can easily recode the common shim not to use an argument for the context:
Function.prototype.arg = function() {
if (typeof this !== "function")
throw new TypeError("Function.prototype.arg needs to be called on a function");
var slice = Array.prototype.slice,
args = slice.call(arguments),
fn = this,
partial = function() {
return fn.apply(this, args.concat(slice.call(arguments)));
// ^^^^
};
partial.prototype = Object.create(this.prototype);
return partial;
};
var b = function() {
return c(1,2,3);
};
One more tiny implementation just for fun:
function bindWithoutThis(cb) {
var bindArgs = Array.prototype.slice.call(arguments, 1);
return function () {
var internalArgs = Array.prototype.slice.call(arguments, 0);
var args = Array.prototype.concat(bindArgs, internalArgs);
return cb.apply(this, args);
};
}
How to use:
function onWriteEnd(evt) {}
var myPersonalWriteEnd = bindWithoutThis(onWriteEnd, "some", "data");
It's a bit hard to tell exactly what you ultimately want to do because the example is sort of arbitrary, but you may want to look into partials (or currying): http://jsbin.com/ifoqoj/1/edit
Function.prototype.partial = function(){
var fn = this, args = Array.prototype.slice.call(arguments);
return function(){
var arg = 0;
for ( var i = 0; i < args.length && arg < arguments.length; i++ )
if ( args[i] === undefined )
args[i] = arguments[arg++];
return fn.apply(this, args);
};
};
var c = function(a, b, c, callback) {
console.log( a, b, c, callback )
};
var b = c.partial(1, 2, 3, undefined);
b(function(){})
Link to John Resig's article: http://ejohn.org/blog/partial-functions-in-javascript/
Using LoDash you can use the _.partial function.
const f = function (a, b, c, callback) {}
const pf = _.partial(f, 1, 2, 3) // f has first 3 arguments bound.
pf(function () {}) // callback.
May be you want to bind reference of this in last but your code:-
var c = function(a, b, c, callback) {};
var b = c.bind(null, 1, 2, 3);
Already applied binding for instance this and later you can not change it.
What I will suggest that use reference also as a parameter like this:-
var c = function(a, b, c, callback, ref) {
var self = this ? this : ref;
// Now you can use self just like this in your code
};
var b = c.bind(null, 1, 2, 3),
newRef = this, // or ref whatever you want to apply inside function c()
d = c.bind(callback, newRef);
Use a protagonist!
var geoOpts = {...};
function geoSuccess(user){ // protagonizes for 'user'
return function Success(pos){
if(!pos || !pos.coords || !pos.coords.latitude || !pos.coords.longitude){ throw new Error('Geolocation Error: insufficient data.'); }
var data = {pos.coords: pos.coords, ...};
// now we have a callback we can turn into an object. implementation can use 'this' inside callback
if(user){
user.prototype = data;
user.prototype.watch = watchUser;
thus.User = (new user(data));
console.log('thus.User', thus, thus.User);
}
}
}
function geoError(errorCallback){ // protagonizes for 'errorCallback'
return function(err){
console.log('#DECLINED', err);
errorCallback && errorCallback(err);
}
}
function getUserPos(user, error, opts){
nav.geo.getPos(geoSuccess(user), geoError(error), opts || geoOpts);
}
Basically, the function you want to pass params to becomes a proxy which you can call to pass a variable, and it returns the function you actually want to do stuff.
Hope this helps!
An anonymous user posted this additional info:
Building on what has already been provided in this post -- the most elegant solution I've seen is to Curry your arguments and context:
function Class(a, b, c, d){
console.log('#Class #this', this, a, b, c, d);
}
function Context(name){
console.log('#Context', this, name);
this.name = name;
}
var context1 = new Context('One');
var context2 = new Context('Two');
function curryArguments(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function bindContext() {
var additional = Array.prototype.slice.call(arguments, 0);
return fn.apply(this, args.concat(additional));
};
}
var bindContext = curryArguments(Class, 'A', 'B');
bindContext.apply(context1, ['C', 'D']);
bindContext.apply(context2, ['Y', 'Z']);
Well for the exemple you gave, this will do
var b= function(callback){
return obj.c(1,2,3, callback);
};
If you want to guarenty enclosure of the parameters :
var b= (function(p1,p2,p3, obj){
var c=obj.c;
return function(callback){
return c.call(obj,p1,p2,p3, callback);
}
})(1,2,3,obj)
But if so you should just stick to your solution:
var b = obj.c.bind(obj, 1, 2, 3);
It's the better way.
Simple like that?
var b = (cb) => obj.c(1,2,3, cb)
b(function(){}) // insidde object
More general solution:
function original(a, b, c) { console.log(a, b, c) }
let tied = (...args) => original(1, 2, ...args)
original(1,2,3) // 1 2 3
tied(5,6,7) // 1 2 5
I'm using this function:
function bindArgs(func, ...boundArgs) {
return function (...args) {
return func(...boundArgs, ...args);
};
}
// use
const deleteGroup = bindArgs(this.props.deleteGroup, "gorupName1");
Why not use a wrapper around the function to save this as mythis ?
function mythis() {
this.name = "mythis";
mythis = this;
function c(a, b) {
this.name = "original";
alert('a=' + a + ' b =' + b + 'this = ' + this.name + ' mythis = ' + mythis.name);
return "ok";
}
return {
c: c
}
};
var retval = mythis().c(0, 1);
jQuery 1.9 brought exactly that feature with the proxy function.
As of jQuery 1.9, when the context is null or undefined the proxied function will be called with the same this object as the proxy was called with. This allows $.proxy() to be used to partially apply the arguments of a function without changing the context.
Example:
$.proxy(this.myFunction,
undefined /* leaving the context empty */,
[precededArg1, precededArg2]);
Jquery use case:
instead:
for(var i = 0;i<3;i++){
$('<input>').appendTo('body').click(function(i){
$(this).val(i); // wont work, because 'this' becomes 'i'
}.bind(i));
}
use this:
for(var i = 0;i<3;i++){
$('<input>').appendTo('body').click(function(e){
var i = this;
$(e.originalEvent.target).val(i);
}.bind(i));
}
I am creating an attribute in a javascript object by replacing some strings in an existing object, as a side effect I want to make some additional changes to a third property, which I try to access with this.property however in the replace function this is referring to the window instead of my 'master' object. How can I pass in the encapsulating object, so that I can use this to access third property.
b = {
a: 'onetwothree',
count: 0,
rp: function () {
this.c = this.a.replace(/e/g, function (str, evalstr) {
this.count++; // <-- this is refering to window.
return 'e' + this.count
})
}
};
b.rp();
b.c = 'oneNaNtwothreNaNeNaN whereas I want it to be one1twothre2e3
rp: function () {
this.c = this.a.replace(/e/g, function (str, evalstr) {
this.count++;
return 'e' + this.count
}.bind( this )) // <-- bind the function
}
Cache the this context in another variable.
rp: function () {
var self = this; // Cache here
this.c = this.a.replace(/e/g, function(str, evalstr) {
return 'e' + (++self.count); // Use here
});
}
Protip: ++self.count gives the new value after incrementing.
You can usually solve this by making use of the closure you're creating, like this:
b = {
a: 'onetwothree',
count: 0,
rp: function () {
var self = this; // <-- Create a variable to point to this
this.c = this.a.replace(/e/g, function (str, evalstr) {
self.count++; // <-- And use it here
return 'e' + self.count; // <-- And here (also added the ;)
})
}
};
b.rp();
More to explore (disclosure: both are posts on my blog):
You must remember this
Closures are not complicated