Function context when calling via arguments - javascript

I've got this interview question and can't really get what the difference between fn() an arguments[0]() since they both reference to the same function.
var length = 10;
function foo() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn(); // TypeError: Cannot read property 'length' of undefined
arguments[0](); // 2
}
};
obj.method(foo, 1);
Ok, so when calling arguments[0](), in foo it's context (this) will be equal to function arguments - [Arguments] { '0': [Function: foo], '1': 1 }. But how is this happend?

Consider the following code:
function foo() {
console.log(this.bar);
}
const obj = {
bar: "foobar",
myFunction: foo
}
obj.myFunction(); // foobar
In the above, the this in the function foo gets bound to the calling context of foo, in the case above, that is the object obj, and so this inside of the function foo will refer to obj.
In your case, arguments is an object. It has a bunch of properties such as its length as well as numeric properties to represent indexes for the arguments passed into the function. For example, you could (roughly) make your own arguments object like so:
function foo() {
console.log(this.length);
}
const myArguments = {
0: foo,
length: 2
}
myArguments[0](); // 2
Notice that the above myArguments[0]() is using the same idea of calling a function from the first code snippet (obj.myFunction()), except we are using bracket notation (arguments[0]) rather than dot-notation (arguments.0) to access and call the function stored at the property 0 from the object myArguments. Thus, the calling context for foo in this case is myArguments, and as a result that is what this gets bound to within the function foo.
The same idea above applies to your code. When you call fn() without an explicit context the this within your foo() function will become undefined (if in strict-mode), thus causing your error:
TypeError: Cannot read property 'length' of undefined
However, when using arguments[0], the this gets bound to the arguments object, much like in the above two examples.

The this value used by an execution context depends on how a function was called.
1. fn() // TypeError: Cannot read property 'length' of undefined
2. arguments[0]() // 2
Line 1 shows a direct function invocation. For this style the this value will be undefined in strict mode and the global object in non-strict mode.
Line 2 shows a function being invoked as a method. It is a bit of a trick question because we are more used to seeing the dot operator used with methods, but the bracket property accessor notation works similarly. When you invoke a function as a method, the this value is set to be the object the method is invoked on. This behavior is designed to support object-oriented style programming.
It's akin to writing arguments.0(), but the JS syntax does not permit this sequence (presumably due to ambiguity with the decimal place syntax).
Sneaky interview question!

here obj work like an array, get value on given position and call it as a function
var length = 10;
function foo1() {
console.log(length);
}
function foo2() {
console.log(this.length);
}
const obj = {
length: 5,
0: foo1,
1: foo2,
};
obj[0](); // 10
obj[1](); // 5

Related

Accessing function return value through nested object inside IIFE

I'm trying to set an object value to a function call using another value (array) as its parameter, however, for some reason the value comes back as undefined, I'm then trying access that value right after that. It's inside an IIFE.
I am unsure why this is happening because to the best of my knowledge the scoping is okay and the value should be initialized and executed by that time?
Here's a simplified example:
(function(){
var minefield = [1,2,3,'M',4,5,6];
function findMachine(minefieldMachine) {
return minefieldMachine === 'M';
}
var terrain = {
start:{initialMachienLocation:minefield.findIndex(findMachine)},
currentMutable:{mutableIndex:terrain.start.initialMachineLocation}//Error: "Uncaught type error cannot read property 'start' of undefined"
//Why isn't the above accessing the value declared in terrain.start.initialMachineLocation through the function call?
}
}());
However, doing this works, out of context from above:
function findMachine(minefield){return minefield === 'M'}
[1,2,3,'M',4,5,6].findIndex(findMachine);
//above returns the proper index value, 3.
This has nothing to do with the function.
What you are attempting to do is, essentially, this:
var foo = {
a: 1,
b: foo.a
}
You need to consider the order of operations.
The object literal constructs an object which is then assigned to the variable foo.
… except that while the object is being constructed, you are trying to read the value of foo. It doesn't have one yet, so this fails.
You need to do this in two steps.
var foo = {
a: 1,
}
foo.b = foo.a;

What is the meaning of this.foo()?

I understand the concept of using this to assign values to a variable:
this.foo = 37;
But I am confused about using it in connection with a function invocation:
this.foo();
What is the meaning of this?
This is just the context of how a function was called. In the global execution context (outside of a function) its the global object. In your case this is just some type of object that foo is a property on it. The property can be a value like an integer or even function.
var someObject = {
foo1: 37,
foo2: function () {
return this.foo1;
}
};
someObject.foo2.apply(someObject);
In the example above I created an object with two properties. One is an integer value and the other is a function. Then I call the function on the object using apply which allows us to set the context of the function. I set the context as the object I created so once inside the function I can reference it as this.
Functions in JavaScript are first class objects. They can be treated like any other object.
They can be stored in variables. They can be stored in properties on other objects. They can be passed around as function arguments.
This is just an example of a property whose value is a function.
var obj = {};
obj.foo = myFunction;
obj.foo();
function myFunction () {
document.body.style.background = "red";
}

Global Object and assignment - why does it recognize the assignment?

When I run in the console, I don't quite follow why it yields a result of zero. I thought both MyObject and myFunc are global. What concept am I missing to understand this properly?
myObject = {
length: 400,
};
function myFunc () {
return console.log(this.length);
}
myFunc(myObject.length); // equals zero
It's showing you the value of window.length (which is the number of frames/iframes in the document). To have myFunc show you the value of the length of the object you pass into it, you'd have to use that argument:
function myFunc (obj) {
return console.log(obj.length);
}
If you're trying to explore implicit globals (your myObject is an implicit global; they're a Bad Thing(tm)), then myFunc should be called with no arguments and should use myObject.length or possibly this.myObject.length (which come to the same thing).
You're passing the object as a parameter, but you're not receiving it in the function. this in the context of myFunc is not the object myObject. It is the window object.
To achieve what you're trying to do, modify your code like so:
myObject = {
length: 400,
};
function myFunc (obj) {
return console.log(obj.length);
}
myFunc(myObject); // equals zero
Note:
To verify that this is the window object, see this fiddle where I've modified your console line to be return console.log(this);. The console outputs the Window object.
Demonstration Fiddle

How to check if a function is called using call/apply

function foo(){
console.log('foo', this);
}
foo();
foo.call({ bar: 1 });
foo.apply([{ bar: 1 }]);
Is there any way to know if foo() was called using a normal invoke or call/apply?
http://jsfiddle.net/H4Awm/1/
No. You can't detect if a function is called from call/apply or normally.
They're not magical beings, all they do is set the arguments and this value. There is a subtle difference when it comes to undefined/undeclared values but that's it.
All .apply and .call do in ES5 is:
Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
It sets the internal caller attribute correctly so something naive like this would not work.
Unless you redefine Function.prototype.call and Function.prototype.apply (and pass another argument to your function), there is no way of doing so - the actual internal mechanics (the [[Call]] internal function) do not provide any method of signalling that it is called using ().
Compare the specification for a general function call and for Function.prototype.apply - each call the internal code of the function in exactly the same manner, and there is no external property set that is able to give you whether is called using that or not.
See the specification for the internal function [[Call]]:
13.2.1 [[Call]]
When the [[Call]] internal method for a Function object F is called with a this value and a list of arguments, the following steps are taken:
Let funcCtx be the result of establishing a new execution context for function code using the value of F's [[FormalParameters]] internal property, the passed arguments List args, and the this value as described in 10.4.3.
Let result be the result of evaluating the FunctionBody that is the value of F's [[Code]] internal property. If F does not have a [[Code]] internal property or if its value is an empty FunctionBody, then result is (normal, undefined, empty).
Exit the execution context funcCtx, restoring the previous execution context.
If result.type is throw then throw result.value.
If result.type is return then return result.value.
Otherwise result.type must be normal. Return undefined.
There is no provision to change the running of a function from whether it is called using call/apply or not - the only thing that changes what it does are the arguments for the function itself and what is this meant to be within the function.
I hope this solve your problem:
function foo(){
console.log('foo', this);
if (typeof this.length === "number") {
//function has been apply
} else {
//function has been call
}
}
foo();
foo.call({ bar: 1 });
foo.apply([{ bar: 1 }]);
Try something like this:
function foo(){
console.log('foo', this);
console.log( arguments );
}
Function.prototype._apply = Function.prototype.apply;
Function.prototype.apply = function(ths,args){
args.unshift('apply');
this._apply(ths,args);
};
foo();
foo.call(this, { bar: 1 });
foo.apply(this, [{ bar: 1 }]);
Dirty, dirty hack:
function foo() {
var isNatural = !/foo\.(call|apply)/.test("" + foo.caller)
console.log(isNatural ? "foo()" : "foo.{call,apply}()")
}
function caller1() {
foo()
}
function caller2() {
foo.call(null, { bar: 1 })
}
function caller3() {
foo.apply(null, [{ bar: 1 }])
}
caller1()
caller2()
caller3()
It's just food for thought. Don't use it on production.
I can't think of a reason you should be checking for this but, you could check by comparing this === window, as that's the default scope (assuming browser based Javascript), but this could be faked by simply calling foo like so foo.call(window) which is basically what calling foo() is doing normally.
This also probably won't work using window for functions that are properties of other objects or prototypes.

Javascript: Re-assigning a function with another function

Let's say I have these two functions:
function fnChanger(fn) {
fn = function() { sys.print('Changed!'); }
}
function foo() {
sys.print('Unchanged');
}
Now, if I call foo(), I see Unchanged, as expected. However, if I call fnChanger first, I still see Unchanged:
fnChanger(foo);
foo(); //Unchanged
Now, I assume this is because foo is not being passed to fnChanger by reference, but I may be wrong.
Why does fnChanger not change foo to print Changed!?
Furthermore, how can I get fnChanger to change foo without too much messy syntax?
PS: I'm using node.js to test all this stuff, so that's where the sys.print comes from.
The assignment to the fn argument just makes that identifier to point to the anonymous function, foo in the outer scope is not affected.
When you pass an object as an argument, one can say "references are passed by value". The assignment just replaces the location where the fn identifier refers to.
That's how the evaluation strategy works in JavaScript.
Just before the assignment in the fnChanger functions, the two identifiers, the global foo and the fn argument, point to the same function object:
---------------------------------------------
foo -----> |function foo { sys.print('Un changed!'); } |
---------------------------------------------
^
|
fn -------------
After the assignment, fn will simply point to the new function:
---------------------------------------------
foo -----> | function foo { sys.print('Unchanged!'); } |
---------------------------------------------
---------------------------------------
fn ------> | function { sys.print('Changed!'); } |
---------------------------------------
How could you change it?
Well, assuming that foo is a function in the global scope, you could do something like this:
function fnChanger(obj, name) {
obj[name] = function() { sys.print('Changed!'); };
}
function foo() {
sys.print('Unchanged');
}
fnChanger(this, 'foo');
foo(); // Changed!
The above will work because in the fnChanger function, we require a base object and a property name, functions declared in the global execution context are bound as properties of the Global object, therefore we can re-assign its value in that way.
The line fnChanger(this, 'foo'); should be executed also in the Global scope, it will pass the this value (which refers to the Global object in this scope) and a property name, allowing you to make an assignment to the GlobalObject.foo identifier.
If that code were inside a function, there is no way we can get a base object, because in this "Function Code Execution Context", function declarations (variable declarations and function formal parameters also) are bound as properties of a non-accessible object, called the Variable Object (a chain of these Variable Objects, forms the Scope Chain), and if it were the case, the only workaround would be to use eval.
More info:
ECMA-262-3 in detail. Chapter 8. Evaluation strategy.
As #CMS pointed out you cannot assign it within the function due to the scope. However you could reassign it like this:
var fnChanger = function() {
return function() {
alert('changed!');
}
}
var foo = function() {
alert('Unchanged');
}
foo = fnChanger();
foo();
example
There is an elegant way around it
function fnChanger(fn_holder) {
fn_holder["fn"] = function() { console.log('Changed!'); }
}
function foo() {
console.log('Unchanged');
}
const foo_holder = {fn: foo}
fnChanger(foo_holder);
foo_holder["fn"](); // Changed!
Short explanation:
Pass a higher level reference foo_holder to fnChange like a hash (or an array).
Reassign your element in side the hash foo_holder["fn"] to a new value. This will change where the inner reference is pointing instead of creating a new reference.
Then you're good to have fun with your updated function.
In Javascript, functions are first class objects that can be treated just as another variable. But for the function to return its result, it has to be first invoked.
When you are invoking the fnChanger(foo), the fn variable actually gets overridden with foo().
But you are not getting the result because that function was never invoked. Try returning it and invoking as given below and you will the required answer.
function fnChanger(fn) {
fn = function() {
console.log('Changed!');
}
return fn;
}
function foo() {
console.log('Unchanged');
}
fnChanger(foo)();
foo();

Categories