If I have this ES6 function declaration and invocation:
function myFunction (arg1, arg2 = "bob") {
console.log("arguments", arguments);
}
myFunction(1);
...the console.log() statement shows only one argument with a value of "1". "bob" is nowhere to be seen. Is this expected and/or desired behavior? I would expect that default values would be available in the arguments object. If not, is there a way to dynamically get all arguments + defaults in some other manner?
Thanks in advance!
Yes, this is expected and desired. The arguments object is a list of the values that were passed into the function, nothing else.
It is not implicily linked to the parameter variables (that get assigned the default values), like it was in sloppy mode.
Is there a way to dynamically get all arguments + defaults in some other manner?
No. What parameters you have and whether they have default initialisers is static, you don't need to do anything here dynamically. You can do Object.assign([], arguments, [arg1, arg2]) for your example function.
As you know by now, there is no native method to get both "passed arguments AND defaults where arguments are not passed". But there is a workaround:
This function (that I found here) gets all parameters of a given function:
function getArgs(func) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
return args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
};
So, combining this function with the arguments of your function myFunction, we can get an array that has what you want:
function myFunction (arg1, arg2 = "bob") {
var thisArguments = arguments;
console.log(getArgs(myFunction, thisArguments));
};
function getArgs(func, argums) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
var argsArray = args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
for(var i = 0; i < argsArray.length; i++){
argsArray[i] += " (default)";
}
var defaults = argsArray.slice(argums.length);
argums = Array.prototype.slice.call(argums);
return argums.concat(defaults);
};
Now, we can see the information in the console calling myFunction:
1. Passing more arguments than parameters
This will return only the arguments.
myFunction("foo", "bar", "baz");
//returns: ["foo", "bar", "baz"]
2. Passing less arguments than parameters
Will return the arguments and the remainder parameters as default, as you want (I added "default" to each string).
myFunction("foo");
//returns ["foo", "arg2 = "bob" (default)"]
3. Passing no arguments
This will return all the parameters.
myFunction();
//returns ["arg1 (default)", "arg2 = "bob" (default)"]
This is the fiddle: https://jsfiddle.net/gerardofurtado/25jxrkm8/1/
Related
I am working on a problem and I am not allowed to use ES6 syntax so I can't use the spread/rest operator. How can I write a function that will take any number of arguments without a spread/rest operator?
function memoize(func) {
var cache = {};
return function (...args) {
if (args in cache) {
return cache[args];
} else {
cache[args] = func(...args);
return func(...args);
}
};
};
Firstly you have to use the arguments object. It is an older feature which makes a variable called arguments available in non arrow functions. The value of arguments is all the arguments a function receive. It is an array-like object, not an array. This eliminates the use of rest operator.
Also, it is better to create a key, when you are creating a generic function.
Why? Javascript object keys can only be string. If an object is used as a key it gets auto converted to a string : [object Object]. So basically all your object keys will override each other. Here is a demo:
const obj1 = { a : 1};
const obj2 = { b : 2};
const x = {};
x[obj1] = 2;
console.log(x);
x[obj2] = 2;
console.log(x);
To generate hash key you could use any method. Below is not a bullet proof implementation but I have just joined all the arguments into a . separated array.
Now we have to call your function with these arguments, in the else condition. How? When you want to forward you arguments to another function, and they are in the form of an array, you can use something like apply. It is pre ES6 feature which lets you run a function in a different context, or lets you pass arguments as an array. Here we won't be changing the context, but the second use case is what we will be using.
function memoize(func) {
var cache = {};
return function () {
const key = Object.values(arguments).join('.');
console.log(arguments);
console.log(cache);
if (key in cache) {
return cache[key];
} else {
cache[key] = func.apply(null,arguments);
return cache[key];
}
};
};
const conc = (str,str2,str3) => `${str}_${str2}_${str3}`;
const memoizedSq = memoize(conc);
memoizedSq('hello','hi','hey');
memoizedSq('bye','see you','so long');
memoizedSq('hello','hi','hey');
I have used join to create the string.
Minor optimization I did by changing your code in the else condition is not calling function multiple times. I just saved it in the object and returned the same value.
Note: This could break for cases where if a function takes a string, and the string itself contains ..
So I'm learning Javascript and I see this code:
var apple = {//... an object with some properties};
var fruit = apple.someMethod(function (b) {return b.a_property_of_apple});
Where someMethod and a_property_of_apple are valid methods and properties.
My question pertains to the argument, b, of the anonymous function which is not declared or defined anywhere else:
function (b) {return ...
What is going on here? What is b and why is it being used?
Apologies in advance for the basic nature of the question. If someone just wants to drop some focused terms on me to read up on that would be great short of an explanation.
The anonymous function is a callback function being passed to the apple.method() invocation.
apple.method() will invoke that anonymous function at some point during it's execution, ( or pass it to another function ). Whenever it's invoked it will be invoked with an argument that will be available inside the callback. You could call it b, or response, or whatever you want (logical names are best) and be able to use it within the anonymous function.
You should read about Callback functions over at MDN.
EDIT: I will explain the parts to you
var apple = {} This is the definition of an object
var fruit = apple.someMethod(function (b) {return b.a_property_of_apple}); is defining that fruit is equal to the return value of the invocation of apple.someMethod(...)
apple.someMethod(function (b) {return b.a_property_of_apple}); is the invocation of apple.someMethod with function (b) {return b.a_property_of_apple} as the only argument.
The b argument in the anonymous function function (b) {return b.a_property_of_apple} will be passed to it's invocation within the apple.someMethod.
Here is an example snippet.
// define apple
var apple = {
// define method
someMethod: function( callback ) {
var obj = {
a_property_of_apple: "Eat me!" // this will be returned
}
// return the invocation of callback with obj as argument
return callback(obj);
}
}
var fruit = apple.someMethod(function (b) {return b.a_property_of_apple});
console.log(fruit);
EDIT: Ok, going to use something slightly less abstract as an example.
// notice employees being passed to this function
// that is called an argument and is usable inside the function
var orginization = function( employees ) {
// this will take the empoyees argument and assign it to this.employees
// or set this.employees to an empty array if there is no employees argument
this.employees = employees || [ ];
// this is a method ( a method is a function on an object )
// this function takes 3 arguments
this.addEmployee = function( employee ) {
// we use the 3 arguments to push a new object with title, name, and salary
// properties provided by the function arguments
this.employees.push( employee );
}
// this method returns the value stored in this.employees
this.getEmployees = function() {
return this.employees;
}
}
// this is a variable an array of employees only containing 1 employee
// i will use it in the creation of my new orginization
var employess = [
{
title: "CEO",
name: "Enola",
salary: "$$$$$$$"
}
];
// i use the new to create learningInc from originization( employees )
// originization is a constructor function which creates an object
// with methods and properties found on the constructor
var learningInc = new orginization( employess );
// console.log learningInc.getEmployees() an you will see still only the CEO
// works here
console.log( "before newHire: ", learningInc.getEmployees() );
// lets make a newHire
var newHire = {
title: "Peon",
name: "Sadly McFrownFace",
salary: "$"
};
// add the newHire to the employess of learningInc wth out getEmployees() method
learningInc.addEmployee( newHire );
// log the new value of learningInc.getEmployees and you see we now have 2 employees
console.log( "after newHire: ", learningInc.getEmployees() );
Ok now notice this line var learningInc = new orginization( employess );
The employees variable I'm passing to this function as an argument is used in this function var orginization = function( employees ) { ... }.
Hope this help.
My question pertains to the parameter, b, of the anonymous function which is not declared or defined anywhere else: What is going on here?
What is b and why is it being used?
Why you say it is not declared? It is declared right there. Consider this simple JavaScript function:
function doSomething(a, b){
//do something here;
}
In this code, we are creating a function, naming it "doSomething", and declaring two parameters for it a and b. This is how we declare function parameters in JavaScript. Now your example:
function (b) {return ...
is exactly the same, except we didn't give this function a name, which means it is an anonymous function. That's the only difference, but its parameter b is declared right there like any standard function. So there is nothing special going here, it's a standard function parameter and used as such.
There are a couple concepts at work here
Function declarations vs function expressions; you can use function as an operator to define a function, and assign the function to an identifier and pass it around like any normal object
Callbacks; you can pass a function CB into another function A to be called by A (as defined by A)
Passing something without an identifier
Function Declaration
// declare function
function foo(argFoo) {
console.log('foo', argFoo);
}
// invoke function
foo('was declared'); // "foo" "was declared"
Function Expression
// express function
var bar = function (argBar) {
console.log('bar', argBar);
};
// invoke function
bar('was expressed'); // "bar" "was expressed"
Callbacks
function fizz(callback) {
console.log('first I fizz');
callback();
}
function buzz() {
console.log('then I buzz');
}
fizz(buzz);
// "first I fizz"
// "then I buzz"
Passing without an Identifier,
Basically, defining things in-place
// say we have some fn fizzbuzz
function fizzbuzz(foo) {
console.log(foo);
}
// we could pre-define what we want to pass to it
var i = 1;
fizzbuzz(i); // 1
// or we could pass directly
fizzbuzz(1); // 1
// with anything we like
fizzbuzz({some: 'object'}); // {some: "object"}
// even a function
fizzbuzz(function () {}); // function () {}
Maybe if I break down what is happening into more readable code, you can see what is happening.
someMethod is a method that take a function as an argument. This is more easily seen when broken down like below.
It's up to someMethod to determine what they do with that function. In this example, I am executing the function being passed into someMethod and passing it my this context.
var apple = {
name: 'Apple',
someMethod: function(func) {
return func(this);
}
};
function getName (b) {
return b.name;
};
const name = apple.someMethod(getName); // Apple
To your question: b is defined as the first argument to your anonymous function. This is more clearly expressed when the code is broken out above. But you could also express it like this:
const name = apple.someMethod(function(x) { return x.name; }); // Apple
or like this using ES6:
const name = apple.someMethod(x => x.name); // Apple
Here the code:
Function.prototype.curry = function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments), // no thisArg ? arguments are the sec param [argsArray]
that = this;
return function() {
// thisArg: null
return that.apply(null, args.concat(slice.apply(arguments)));
}
}
Above is what I understand. So why does that.apply have a null param, while the slice.apply doesn't have one?
And when I changed it to args = slice.apply(null, arguments), It threw an error which said:
Uncaught TypeError: Array.prototype.slice called on null or undefined
Where am I wrong about Function.prototype.apply()?
.apply sets context and arguments for a function:
my_fn.apply({}, [1,2,3]);
function my_fn() {
console.log(this); // {}
console.log(arguments); // [1,2,3]
}
slice.apply(arguments); is a hack to convert an array like object to an actual array, actually it could also have been .call(arguments); since call works almost like .apply:
my_fn.call({}, 1,2,3); // <- no array but more arguments
function my_fn() {
console.log(this); // {}
console.log(arguments); // [1,2,3]
}
So that.apply(null, ... just doesn't set a context for the function that. While Array.prototype.slice expects to work on an array like object and will fail if it gets no context.
The slice.apply and that.apply calls in that function have different purposes.
Just a quick recap: Function#apply accepts up to two arguments: The value to use as this during the call to the original function, and any array-like object which has the arguments (if any) to pass to the function.
The slice.apply calls, such as this one:
args = slice.apply(arguments);
...are passing arguments as the first argument, so slice gets called with this referring to the arguments object and with no arguments at all. This is a fairly common idiom for converting the array-like arguments object into a true array. (In modern code with ES2015, we'd probably use args = Array.from(arguments); instead.)
The that.apply call doing something else entirely: It's calling the function object that curry was called on, passing it the arguments supplied to curry followed by the arguments supplied when the curried function was actually called. It passes null as the first argument, the value to use as this during the call, which means the original function will be called with this referring to the global object (if this is in loose mode) or null (in strict mode`).
Not to bikeshed it, but that's not a great implementation of curry if it's been quoted correctly:
(You've fixed this in the question.) It creates two implicit globals: args and that, which is a pretty bad idea. janje suggests it may be a misquoted version of Crockford's curry from The Good Parts; if so, the ; after Array.prototype.slice and after slice.apply(arguments) should be a , instead:
Function.prototype.curry = function() {
var slice = Array.prototype.slice, // <== comma here
args = slice.apply(arguments), // <== comma here
that = this;
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
}; // Crockford probably didn't leave this semicolon out
}; // Or this one
It blocks this when calling the original function; instead, it should use the same this that hte curried function was called with.
It's creating an enumerable property on Function.prototype; all the other methods on Function.prototype are non-enumerable, probably best to keep it that way.
Instead:
(function() {
var slice = Array.prototype.slice;
Object.defineProperty(Function.prototype, "curry", {
value: function() {
var originalFunction = this;
var args = slice.apply(arguments);
return function() {
return originalFunction.apply(this, args.concat(slice.apply(arguments)));
};
},
writable: true,
configurable: true
});
})();
Example:
"use strict";
// Define it
(function() {
var slice = Array.prototype.slice;
Object.defineProperty(Function.prototype, "curry", {
value: function() {
var originalFunction = this;
var args = slice.apply(arguments);
return function() {
return originalFunction.apply(this, args.concat(slice.apply(arguments)));
};
},
writable: true,
configurable: true
});
})();
// Demonstrate it
function foo() {
console.log("this.answer:", this && this.answer);
console.log("args:", arguments);
}
var obj = {answer: 42, foo: foo.curry("curried")};
obj.foo("supplied during call");
There are optimizations one could make (it's not strictly necessary to create a new array on each call to the curried function), but they don't really buy much.
I've seen many questions for that context, but I still can't figure out, what exactly my Problem is. (I'm still experimenting with JavaScript, especially with objects)
Code:
function Field(val)
{ var value = val;
this.__defineGetter__("value", function(){ return value; });
this.__defineSetter__("value", function(val){ value = val; if(this.onchange) this.onchange.call(); });
}
function LW_makePlan()
{
/* [...] */
this.Filter1=new Field("");
this.Filter2=new Field("");
this.Filter3=new Field("");
this.init = function()
{
/* [...] */
this.Filter1.onchange=this.getSomething;
}
this.getSomething = function()
{
arg="modus=doWhat";
arg=arg+"&filter1=" + this.Filter1.value;
arg=arg+"&filter2=" + this.Filter2.value;
arg=arg+"&filter3=" + this.Filter3.value;
request_fkt(null, true, arg , this.setSomething);
}
this.setSomething = function(data)
{
alert(data);
}
this.init();
};
What I'm trying:
test = new LW_makePlan();
test.Filter1.value="anything";
test.Filter1 has an "onchange"-property, that is checked in the setter of "Field". if set, the setter will also call the object given within the onchange-property.
this works so far BUT it seems, that this call creates a whole new object-instance ... no not an instance, it is, as if the function "getSomething" is copied as a stand-alone function, because the Code i called, but for example this.Filter1 within the function "getSomething" is undefined ...
Why is this happening and how can I avoid this?
PS: I don't want to use some type of event-handling-Things from 3rd Party codes, I'd like to do it myself with a little help maybe.
EDIT:
Thanks to Steffen Heil, changed to:
var scope=this;
this.Filter1.onchange=function() { scope.getSomething(); };
and it works!
Your call to this.onchange is in Field, so you are calling a function of Field. The assignment this.Filter1.onchanged=this.getSomething kind of copies the method getSomething from LW_makePlan to Field, where it will be called.
So inside of getSomething that is now called onchanged the reference this referes to the Field not the LW_makePlan.
Replace the assignment with this:
var source = this;
this.Filter1.onchange = function() { return source.getSomething(); };
And it will work. Most frameworks have a bindmethod that makes this more readable (hiding the extra variable in a scope).
In reply to the first comment:
You can explicitly call a function like this:
x.call( object, arg1, arg2, ag3 );
x.apply( object, [ arg1, arg2, arg3 ] );
These the are the same and it does not matter what x is. Inside the called function this has the value of object.
x can be:
alert
window.alert
(function(){})
(alert)
(window.alert)
Normal calls to a function are shortcuts:
object.f = g;
object.f( arg1 ) => g.call( object, arg1 );
f( arg1 ) => f.call( window, arg1 );
While window is the global object in a browser; other environments may use another global object.
While the difference between these two shortcuts seems tivial, what about the following?
(object.f)( arg1 )
This is completely valid javascript, as object.f is a function and a function can be invoked using (args1). But:
object.f = g;
(object.f)( arg1 ) => g.call( window, arg1 )
So a.f = b.f; copies a member reference from a to b, but the this context, the code is executon on depends on the way f is called.
a.f(x) == a.f.call(a,x) == (a.f).call(a,x) == b.f.call(a,x) == (b.f).call(a,x)
b.f(x) == b.f.call(b,x) == (b.f).call(b,x) == a.f.call(b,x) == (a.f).call(b,x)
By the way, you can define your own bind very easily:
function bind( object, method ) {
return function() {
return object[ method ].apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, 'getSomething' );
This would match the fix I gave above using "late binding". Most libraries prefer "early binding":
function bind( object, method ) {
return function() {
return method.apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, this.getSomething );
The advantage is better performance, but the main difference is what happens, when getSomething changes after bind was called. The first implementation calls the new value, the second the old value.
Since bind is not a cross browser (old ones) function , there is a polyfill for it : ( from John Resig's book)
/*1*/ Function.prototype.bind = function ()
/*2*/ {
/*3*/ var fn = this,
/*4*/ args = Array.prototype.slice.call(arguments),
/*5*/ object = args.shift();
/*6*/ return function ()
/*7*/ {
/*8*/ return fn.apply(object,
/*9*/ args.concat(Array.prototype.slice.call(arguments)));
/*10*/ };
/*11*/ };
But I don't understand why do we need arguments at line #9.
I mean :
If I have this object :
var foo = {
x: 3
}
And I have this function :
var bar = function(p,b){
console.log(this.x+' '+p+' '+b);
}
So , if I want bar to run in the foo context , with parameters - All I need to do is :
var boundFunc = bar.bind(foo,1,2)
boundFunc ()...
So When I run var.bind(foo,1,2) the arguments is [object Object],1,2.
Those arguments are saved at line #4.
Great.
Now , the bind function returns its own closured function :
function ()
{
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
}
Question
Why do we need arguments here ? it seems that they are for something like :
var boundFunc = bar.bind(foo,1,2)
boundFunc (more1,more2....) //<----- ??
Am I missing something ?
Oonce I set the first var boundFunc = bar.bind(foo,1,2) , I already declared the parameters. why do we need them twice ?
There are two places you can pass in arguments to the bound function:
1) When you call bind (the first arguments). These are always applied to the bound function when it is called.
2) When you call the bound function (the second arguments). These are the "more1, more2" that you mention. These change depending on what is provided when the bound argument is called.
Line 9 is combining the original bound arguments with the supplied extra arguments.
I guess the concept you might be confused about is that you don't have to bind ALL arguments initially - you can bind just the context object, or you can bind the first one argument as well but have callers of the bound function supply the rest. For example:
function sum() {
var _sum = 0
for (var i = 0; i < arguments.length ; i++) {
_sum += arguments[i];
}
return _sum;
}
var sum_plus_two = sum.bind({},2);
sum_plus_two(5,7) == 14;
.bind also serves as partial application solution. Event handlers might be the best example:
var handler = function(data, event) { };
element.addEventListener('click', handler.bind(null, someData));
If the arguments from the actual function call wouldn't be passed on, you couldn't access the event object.