Enumerating the bound arguments using bind - javascript

In JavaScript, can I enumerate the bound arguments for a function?
function foo(a, b) {}
foo = foo.bind(null, '1');
How can I tell that a has been bound to '1'?

Not sure if I understood your question correctly. However, here is a hacky way to get all currently bound arguments of a function.
It works by overwriting the native bind method and storing all bound arguments in a property of the newly bound function.
Function.prototype.bind = (function() {
var bind = Function.prototype.bind;
var slice = Array.prototype.slice;
return function() {
// bind the function using the native bind
var bound = bind.apply(this, arguments);
// store the bound arguments in a property of the bound function
bound.bArgs = (this.bArgs || []).concat(slice.call(arguments, 1));
return bound;
};
})();
function foo(a, b) {}
foo = foo.bind(null, '1');
console.log(foo.bArgs); // ["1"]
foo = foo.bind(null, '2');
console.log(foo.bArgs); // ["1", "2"]
http://jsfiddle.net/49d1xfns/

You cannot. The function is simply being called passed a certain this value and optionally certain argument values: it's not a problem of the called function to distinguish who has been passing those values...
To simplify a bit the issue consider:
function add(x, y) {
return x + y;
}
function bind_first(f, x) {
return function(y) { return f(x, y); };
}
var add3 = bind_first(add, 3);
console.log(add3(2)); // ==> output is 5
the add function is called with two arguments as usual... both of them are coming from the body of an anonymous closure created by bind_first and it's it's nothing that should interest add how this anonymous closure computed the value of those parameters... got them from the outside? were captured variables? computed them itself? are constants?... WHO CARES!
In other words if you want to be able to differentiate between a bound argument and a regular argument there's for sure something wrong in your design (it's none of your business :-) ).

To know how many explicit arguments were bound to a particular function, you'd need to know:
The number of explicit arguments in the original function
The number of explicit arguments in the bound function
This can be achieved if you have access to both the original function and the newly bound function. This also assumes that the binding was done with an ES5 compliant Function.prototype.bind call.
The way you'd figure out which arguments were bound is by subtracting the newly bound function's length from the original function's length:
var foo = function (a, b, c) {...};
var bar = foo.bind(fizz, buzz);
var argumentsBound = foo.length - bar.length; // 1
var argumentNames = ['a', 'b', 'c'].slice(0, argumentsBound); // 'a'
Details for why this works can be found in the ES5 spec (15.3.4.5, #15)
If the [[Class]] internal property of Target is "Function", then
Let L be the length property of Target minus the length of A.
Set the length own property of F to either 0 or L, whichever is larger.
All of that said, this doesn't give much access at all to which properties were bound, and is pretty well useless within the original function (foo in my example above) because the original function probably shouldn't reference bound derivative functions.
This also doesn't help at all if no arguments are defined on the function. If you're making use of arguments to pass additional parameters, the length property is no longer relevant.

Related

using Function.prototype.bind causes the original properties to be lost

function x() { }
x.a = '1'
let y = x.bind({}) // [Function: bound x]
console.log(y.a)
function y is missing property a defined in x.
Is there any way I can bind a function's this yet still retain its properties like a?
No, there is no way to achieve it in the way you presented. And to understand why you need to know how the .bind() method works.
I think the best explanation can be found on MDN:
The bind() function creates a new bound function, which is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling the bound function generally results in the execution of its wrapped function.
and reading further:
When a bound function is called, it calls internal method [[Call]] on [[BoundTargetFunction]], with following arguments Call(boundThis, ...args). Where boundThis is [[BoundThis]], args is [[BoundArguments]], followed by the arguments passed by the function call.
That means that in your example, the y function is a new function that wraps the x function internally and calls it with changed this context. The polyfill created on the MDN site I linked explains it perfectly.
.bind() returns another function, therefore it does not have any properties you gave it.
I don't know your case but one solution for you might be to treat x as a constructor function like this:
function x() {
this.a = '1';
}
const y = x.bind({});
const newy = new y();
console.log(newy.a);
My solution is like below by copying old function's descriptors to the new one.
const newfunc = func.bind({});
const descriptors = Object.getOwnPropertyDescriptors(func);
Object.defineProperties(newfunc, {
...descriptors
})

confused again by javascript this

some updates:
thank all for all your help. i guess this might be the key confusion point: "this" in the param area is not considered "inside" the function, and thus will not follow the rule (this points to the obj the method is called on) mdn specified. example below:
someObj {
someF(//but if "this" shows up here, it doesn't point to someObj) {
//when called, "this" here will point to someObj
}
}
original question:
After reading many docs, I thought I had a good understanding of this, but I was wrong.
The example below is from MDN:
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
// Here "this" points to obj
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note, why it points to obj, not array [2,5,9]???
// Here "this" points to obj
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3
obj.sum;
// 16
I understand:
The needs to pass in this to a forEach, otherwise in the callback function, this would point to global/window (non-strict mode).
Within most areas of function(array), this points to obj (created from new Counter()), as shown by the comments.
How the callback function uses "this" passed from forEach as the 2nd param. I don't have any question about it
But based on this article on MDN
specifically:
"As an object method, when a function is called as a method of an
object, its this is set to the object the method is called on."
Shouldn't the this (highlighted by ^---Note) passed into the callback point to the array object, i.e., [2,5,9] in this case. Why would it point to obj instead of the array?
Thanks a lot for the help, it's just so confusing.
the needs to pass in "this" in forEach, otherwise "this" in internal function would point to global/window (non-strict mode).
The value of this depends on how the function is called. You can't see the code which calls the callback function you pass to forEach (it is internal to the browser). As it happens, it does call it that way, so it is window (if you don't pass a second argument to forEach).
As an object method, when a function is called as a method of an object, its this is set to the object the method is called on
This is irrelevant. You aren't calling the callback function as a method of an object. You're passing it to forEach … and then forEach is calling it.
You are calling forEach as a method of the array, so inside the forEach function — which you can't see because it is internal to the browser — this will be the array.
Why it would point to obj instead of the array?
Because forEach is explicitly designed to call the callback function in such a way that the second argument to forEach is the this value inside the callback function.
You quoted the documentation that said so.
You will probably see it easier in the following passing in a reference to the object's this as the thisArg of forEach()
Counter.prototype.add = function(array) {
//here "this" points to obj
const _self = this
array.forEach(function(entry) {
// _self passed in as second argument is now `this`
console.log(this === _self) // true
this.sum += entry;
++this.count;
}, _self);
// ^---- passing in reference to the `add` object instance `this`
};
I think your confusion is stemming from the words "called on" in the quote from MDN, and I wouldn't say that is your fault. It doesn't really sound wrong that to say, in the case of obj.add([2, 5, 9]), obj.add is being "called on" [2, 5, 9]. However, what they're trying to say is that the add method is being "called on" obj.
Regardless of how you want to word it, the way it works (in general) is if you're calling a function from a property of an object, that object is what this will point to inside that function. In the case of obj.add([2, 5, 9]), the add function is being accessed via the object obj, so obj is set to this inside that call of add.
This problem is exactly why arrow functions were introduced. They are more tightly bound to your enclosing closure.
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(entry => {
/*
Because we are using an arrow function here, it is using the
enclosing closure (which originates from `obj`).
*/
this.sum += entry;
++this.count;
});
};
const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.sum);
console.log(obj.count);
From the linked documentation:
An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.

what happen when we pass arguments inside the function itself?

Kindly Accept my apologies for being a beginner in JS.
I stopped studying JS on a point of what is the correct answer of what happen inside a function when passing arguments, let us describe by code snippet.
function myFun(x){
console.log(arguments);
console.log(x);
}
myFun(1);
according to the above example,
is the correct action happened is :
Example 1
when passing calling myFun(1) this will do :
function myFunction(x){
var x = 1
}
OR
Example 2
function myFunction(x,y,z){
x=arguments[0];
y=arguments[1];
z=arguments[2];
}
according to the main documentation, i didn't find the desired approach, what i found in arguments object creation process is that:
Repeat while indx >= 0,
Let val be the element of args at 0-origined list position indx.
which I couldn't specify this related to the process of assigning the values or not.
So according to above at all ,
which solution is the correct?
how arguments assigned to parameters? if we passed value 1 as an argument , in the function scope, 1 will be assigned to x or 1 will be assigned to arguments Object?
Let's say you have a function like this:
function oneParam(x)
{
console.log(arguments[0]);
console.log(x);
}
Calling it like so: oneParam(1); will put the first parameter passed in, into the arguments array. If you run the above code, you will get the same value printed twice, because they are the same.
1
1
Now let's take a function with multiple params:
function myFunction(x,y,z)
{
console.log(arguments[0], arguments[1], arguments[2]);
x=arguments[0];
y=arguments[1];
z=arguments[2];
console.log(x, y, z);
}
You will notice, that calling this function like this myFunction("a","b","c"); will produce two similar outputs.
a b c
a b c
So essentially, both scenarios are correct. Just depends on what you passed in when calling the function.
Edit: To clarify, the arguments object will have all the params you passed in. These params will, in order, be assigned to named variables that are there in the function signature.
So you can even have oneParam(1, 2, 3); and the arguments object will have all three params, but only the first param 1 will be assigned to the function variable x.
fiddle here
If you name arguments in your function like this:
function myFunction(x,y,z)
{
console.log(x, y, z);
}
then it's better to access them by their names directly.
You can use argumens if you don't know the number of the arguments while defining the function, like Math.max(), for example.
ES6 makes use of arguments slightly easier than it was before, but it's quite similar. Just remember that arguments is not an array, therefore it has to be converted to an array if you want to have access to all array's methods.
function myFunc_ES6() {
var args = [...arguments];
// or
//var args = Object.values(arguments)
args.forEach(arg => console.log(arg));
}
function myFunc_ES5() {
var args = Array.prototype.slice.apply(arguments);
args.forEach(function(arg) {
console.log(arg);
});
}
myFunc_ES6(1,2,3,4);
myFunc_ES5(6,7,8,9);
The Arguments Object
JavaScript functions have a built-in object called the arguments object.
The argument object contains an array of the arguments used when the function
was called (invoked).
as stated in https://www.w3schools.com/js/js_function_parameters.asp
arguments can be used when you don't want to explicitly specify the parameters of a function. This can be useful in many situations.
Eg.
different behavior on different arguments count
function foo() {
if(arguments.length == 2){
// do something
}else{
//do something else
}
}
extending some method
var oldMethod = Object.prototype.method;
Object.prototype.method = function() {
// some extended functionality
// then call the original
oldMethod.apply(this, arguments);
}
working with unknown number of arguments
function average(){
var i, s=0;
if(!arguments.length){
throw('Division by zero.');
}
for(i=0;i<arguments.length;i++){
s+=arguments[i];
}
return s/arguments.length;
}
Regarding the way the values get assigned,
if we have a function:
function foo(x){}
no matter what you pass in as parameters thiswill be true:
x === arguemnts[0]
It is almost like x is staticaLly referenced in the arguments, because operating on arguments alters the refs too:
arguments[0]++;
x === arguments[0] // true
however deleting the arguments[0] will not modify the explicit parameter value
delete arguments[0]
x !== undefined

Verifying my understanding of the scope chain

(Question 1)
In Flanagan's JS Definitive Guide, he defines the Function method bind() in case it's not available (wasn't available n ECMAScript 3).
It looks like this:
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
He illustrates the use of it with an example (which I have modified to change the 3rd line from f.bind(o)):
function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 }; // An object we'll bind to
var g = bind(f, o); // Calling g(x) invokes o.f(x)
g(2) // => 3
When I first saw this, I thought "Wouldn't arguments refer to the arguments variable within the bind function we're defining? But we want the arguments property of the function we eventually apply it to, like g in the example above..."
I verified that his example did indeed work and surmised that the line return f.apply(o, arguments) isn't evaluated until var g = bind(f, o) up above. That is, I thought, when you return a function, are you just returning the source code for that function, no? Until its evaluated? So I tested this theory by trying out a slightly different version of bind:
function mybind2(f, o) {
var arguments = 6;
return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
If it's simply returning tbe unevaluated function source, there's no way that it stores arguments = 6 when later evaluated, right? And after checking, I still got g(2) => 3. But then I realized -- if it's just returning unevaluated code, how is the o in return f.apply(o, arguments) getting passed?
So I decided that what must be happening is this:
The o and the arguments variables (even when arguments equals 6) are getting passed on to the function. It's just that when the function g is eventually called, the arguments variable is redefined by the interpreter to be the arguments of g (in g(2)) and hence the original value of arguments that I tried to pass on was replaced. But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
(Question 2) Earlier on the same page, he defines the following function which makes use the apply method to trace a function for debugging:
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message.
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
Question 1
For your first question, let's simplify the example so it's clear what being done:
function bind(func, thisArg) {
return function () {
return func.apply(thisArg, arguments);
};
}
What happens here is that a closure is created that allows the access of the original function and the value of this that is passed. The anonymous function returned will keep the original function in its scope, which ends up being like the following:
var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);
About your issue with arguments, that variable is implicitly defined in the scope of all functions created, so therefore the inner arguments will shadow the outer arguments, making it work as expected.
Question 2
Your problem is to your misunderstanding of the late binding of this -- this is one of the more confusing things about JavaScript to people used to more object-oriented languages that also have their own this keyword. The value of this is only set when it is called, and where it is called defines the value of this when it is called. It is simply the value of the parent object from where it is at the time the function is called, with the exception of cases where the this value is overridden.
For example, see this:
function a() {return this;};
a(); // global object;
var b = {};
b.a = a;
b.a(); // object b
If this was set when the function was defined, calling b.a would result in the global object, not the b object. Let's also simplify what happens with the second function given:
function trace(obj, property) {
var method = obj[property]; // Remember original method in the closure.
obj[property] = function () { // Now define the new method.
console.log(1); // Log message.
// Invoke original method and return its result
return original.apply(this, arguments);
};
}
What happens in this case is that the original method is stored in a closure. Assigning to the object that the method was originally in does not overwrite the method object. Just like a normal method assignment, the principles of the this value still work the same -- it will return the parent object, not the function that you've defined. If you do a simple assignment:
var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj
It does what was expected, returns the parent object at the time of calling. Placing your code in a nested function makes no difference in this regard.
Some good resources
If you'd like to learn more about writing code in JavaScript, I'd highly recommend taking a look at Fully Understanding the this Keyword by Cody Lindley -- it goes into much more detail about how the this keyword behaves in different contexts and the things you can do with it.
But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
No. this and arguments are just special variables which are implicitly set when a function is executed. They don't adhere to normal "closure rules". The function definition itself is still evaluated immediately and bind returns a function object.
You can easily verify this with:
var g = bind(f, o);
console.log(typeof g);
Here is a simpler example which doesn't involve higher order functions:
var arguments = 42;
function foo() {
console.log(arguments);
}
foo(1, 2);
I think you see that the definition of foo is evaluated like you'd expect. Yet, console.log(arguments) logs [1, 2] and not 42.
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
this never refers to the function itself (unless you explicitly set it so). The value of this is completely determined by how the function is called. That's why this is often called "the context". The MDN documentation provides extensive information about this.
Reading material:
MDN - this
MDN - arguments

Does the Arguments object leak?

Assuming I had this sloppy-mode function, which (for some odd reason) returns its arguments object to the caller:
function example(a, b/* ...*/) {
var c = // some processing
return arguments;
}
Does storing the result of an invocation (var d=example();) prevent the variable environment of example (containing a, b, c etc) from being garbage-collected? The internal setters and getters of the Arguments object might still refer to it, just like a function returned from a closure does. Demo:
function example(a, b) {
var c = Array(1000).fill(0); // some large object
return {
args: arguments,
set: function(x) { a = x; },
get: function() { return a; }
};
}
var d = example('init');
console.log(d.get());
d.args[0] = 'arguments update'; // assigns the `a` variable
console.log(d.get());
d.set('variable update');
console.log(d.args); // reads the `a` variable
I know there is hardly a use case (and passing around Arguments objects is considered bad practice, most likely because of their similarity to arrays), but this is more a theoretical question. How do different EcmaScript implementations handle this? Is it implemented close to the spec?
I would expect c to be garbage-collected like with a normal closure and not to be leaked, but what about b? What would happen if I delete the properties of the arguments object?
Consider this:
var x = function() {
return arguments;
}
console.log( x() === x() );
It's false, because it's not the same arguments object: it's (for each invokation of x) a newly constructed object that has the values of all the params stored within. Yet it has properties of arguments:
var y = x([]);
console.log(y instanceof Object); // true
console.log(y instanceof Array); // false
console.log(y.length); // 1
console.log(y.callee + ''); // function() { return arguments; }
Yet there's more to this. Obviously, objects sent into function as its params will not be collected by GC if arguments are returned:
var z = x({some: 'value'});
console.log(z[0]); // {some:'value'}
This is expected: after all, you can get the similar result by declaring some local object inside the function, assigning the value of the function's first parameter as its object '0' property, then returning this object. In both case the referred object will still be "in use", so no big deal, I suppose.
But what about this?
var globalArgs;
var returnArguments = function() {
var localArgs = arguments;
console.log('Local arguments: ');
console.log(localArgs.callee.arguments);
if (globalArgs) { // not the first run
console.log('Global arguments inside function: ');
console.log(globalArgs.callee.arguments);
}
return arguments;
}
globalArgs = returnArguments('foo');
console.log('Global arguments outside function #1: ');
console.log(globalArgs.callee.arguments);
globalArgs = returnArguments('bar');
console.log('Global arguments outside function #2: ');
console.log(globalArgs.callee.arguments);
Output:
Local arguments: ["foo"]
Global arguments outside function #1: null
Local arguments: ["bar"]
Global arguments inside function: ["bar"]
Global arguments outside function #2: null
As you see, if you return arguments object and assign it to some variable, inside the function its callee.argument property points to the same set of data as arguments itself; that's, again, expected. But outside the function variable.callee.arguments is equal to null (not undefined).
Without having done any research into a particular JavaScript engine this is hard to answer conclusively. I would however argue that the relation between the arguments Object and the context created by example is the same as of any other local variable and its host context.
That is to say, storing the value does not require its context to be stored as well.
One caveat is the arguments.callee property which is a reference to the context (i.e. Function) which a given arguments Object is bound to. This property however does not exist in strict mode and also has been deprecated.
Other than that I think it's safe to assume that returning and storing the arguments Object won't lead to a memory leak.

Categories

Resources