I read this article http://web.archive.org/web/20110725013125/http://www.digital-web.com/articles/scope_in_javascript/.
In the last example, he provided the code:
var first_object = {
num: 42
};
var second_object = {
num: 24
};
function multiply(mult) {
return this.num * mult;
}
Function.prototype.bind = function (obj) {
//console.log(this); -> TypeError: Object #<Object> has no method 'log'
var method = this,
temp = function () {
//console.log(this); -> RangeError: Maximum call stack size exceeded
return method.apply(obj, arguments);
};
return temp;
}
var first_multiply = multiply.bind(first_object);
console.log(first_multiply(5)); // returns 42 * 5
var second_multiply = multiply.bind(second_object);
console.log(second_multiply(5)); // returns 24 * 5
Although he explained it, I still don't understand several things.
First, why we need do method = this, why this refers to the multiply function here and why this will change when the function temp is created on the next line? Second, why we need to create a function temp here? Third, I tried to print out the this by using console.log(). The strange thing is they both show some errors, could you tell me why?
PS: I used WebStorm IDE.
UPDATE: Please don't ignore the third question, why there are two errors when using console.log, thanks
var method = this;
Might be more clear if written:
var bindObj = this;
The bind function is really to assign which object to multiply function.
Lookup JavaScript Mixin might help explain more.
For regular functions, in JavaScript, what decides what this is going to be is how it's called. So it could be anything.
For example:
Calling it as a property of an obj, object is "this"
var obj = {run: multiply}; obj.run() // "this" will be "obj"
Calling it direct
multiply(); // "this" will be the global context, or null in strict mode
Using call or apply
multiply.call(something, 5); // "this" is "something"
Fat arrow functions, however, keep the same this as its containing function.
Function.prototype.bind = function (obj) {
return () => {
return this.apply(obj, arguments);
};
}
Also, you do not need a temporary function in either case. You could just inline the temp variable.
Related
for example, i have an object where the value of a key is yet to be known/computed but when i call that key for the first time it computes the value and replaces the calculation / function with the computed value. The point being that the value is only worked out when needed and then can be reused again without having to work it out again...
this is what i have so far...
function newobj() {
this.test=function(){
this.test=[1,2,3,4]//complex function in real world use
return this.test
}
}
a = new newobj()
a.test() //[1,2,3,4]
a.test() //not a function!
a.test //[1,2,3,4]
as you can see this requires parenthesis to call initially but not the second time, so this requires knowing whether it's already been called or not - not ideal
version 2
function newobj() {
this.test=function(){
var a=[1,2,3,4]
this.test=function(){return a}
return a
}
}
a = new newobj()
a.test() //[1,2,3,4]
a.test() //[1,2,3,4]
this just somehow doesn't seem the correct way to go about this.. but maybe it is?
apologies if this is a dumb question and thanks in advance for your help
You can use the ability of a getter to overwrite itself to create a "lazy" property whose value is only calculated the first time it's accessed:
There's a good example at MDN.
get test() {
delete this.test;
return this.test = someExpensiveFunctionCall();
}
Subsequent accesses to the property don't even use the getter - they retrieve the value direct from the object.
In your case since you're not using the usual getter syntax, you'd have to modify your function thus:
function newobj() {
Object.defineProperty(this, 'test', {
get: function() {
delete this.test;
return this.test = someExpensiveFunctionCall();
},
configurable: true
});
}
I think what you need is the Module Pattern. You could do
let Module = (function(){
let test = null;
return function(){
if(test == null){
console.log('initialized test');
test = [1, 2, 3, 4];
}
return test;
}
})();
let a = new Module();
console.log(a);
console.log(new Module());
You will have to forgive me, as I am sure this is addressed elsewhere, but I have no idea what to call this bit of syntax or the concept that this example illustrates. How does this get() function know that the parameter I am passing into it refers to an index of the adjacent array? I am not giving get() any indication of what object I am referring to, unless putting an object in parenthesis directly next to it has this effect. Is this correct? Is there a broader concept here that I am unaware of involving proximity and function calls?
function get(prop) {
return function(obj) {
return obj[prop]
}
}
get(1)([1,2,3]);
// output = 2
It might make more sense if you split get(1)([1,2,3]) out into 2 lines like this:
var get1Function = get(1);
get1Function([1,2,3])
// output = 2
Note: after execution of this line
var get1Function = get(1);
get1Function is now set to function(obj) { return obj[1] }
There's no magic here. You have a function that returns a function. When you call get(2), the returned function has 2 for the value of prop. Your code is essentially equivalent to this:
var fn = get(2);
// fn = function(obj) {
// return obj[2]
// }
fn([1,2,3]);
// => 3
You've just encountered one of the most confusing concepts for early JS programmers. It's called a "closure". MDN has a good article on this topic.
In general though, you can think of get returning a new method where the value of prop has been "saved" to the same value it was when get was first invoked.
The code example you provided would probably be best understood if we renamed some things and broke it down:
function createFunctionToReturnValueForProp(prop) {
return function(obj) {
return obj[prop]
}
}
var getPropertyFor1 = createFunctionToReturnValueForProp(1);
var array1 = [1,2,3];
var array2 = [4,5,6];
var obj = { "0": "foo", "1": "bar" };
getPropertyFor1(array1); // 2
getPropertyFor1(array2); // 5
getPropertyFor1(obj); // "bar"
I'm stuck in some issue, a had a function like this:
var sayHi = function(string){
console.log(string + '' + this.name);
};
then i need to do :
sayHi = giveContext(sayHi,{"name":"moe"});
and then I do :
function giveContext(func,obj){
var fn = func;
fn.prototype.name = obj.name;
var myFn = new fn;
return myFn;
}
and the expected behavior would it be :
sayHi('Hello') // ==> "Hello moe"
the thing is that the "new" keyword in givecontext returns an object instead of a function.
and I'm only getting a
undefined moe
Uncaught TypeError: object is not a function
I'm missing something ?
When you are using new fn it will call the function fn as the constructor of an object, and the result is the object that was created.
Basically this:
var myFn = new fn;
works as:
var myFn = {}; // create an object
fn.call(myFn); // call the constructor with the object as context
(There are more things going on of course, but that is the important stuff for now.)
So, the function giveContext doesn't give a context to a function and return it, instead it will call the function as a constructor of an object and return the object. The code inside the function will be called already (that's why there is a console output at all), and when you are trying to use the return vale from giveContext as a function you will get an error as it's not a function at all.
There is already a built in method bind that sets the context for a function:
sayHi = sayHi.bind({"name":"moe"});
(Note the support information for the method though, it's not supported in iE 8 for example.)
You can also do the same without the bind method by creating a function that calls the function:
function giveContext(f, obj) {
return function(){
return f.apply(obj, arguments);
};
}
Yes, JavaScript is object-oriented, so when you say new fn, it creates a new object. In your setup, your function has a name property, so you should be using "Hello " + sayHi.name to get the results you expect.
You're going to want to read though this to get a good overview: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
Is it possible to create an alternate of Array.forEach that automatically sets the context "this" to be the same context as when the method was invoked?
For example (not working, not sure why):
Array.prototype.each = function(fn) {
return this.forEach(fn, arguments.callee.caller);
}
function myFunction() {
this.myVar = 'myVar';
[1,2,3].each(function() {
console.log(this.myVar); // logs 'myVar'
});
}
Array.forEach already takes a context argument as the optional last parameter,
(function() {
this.myvar = "myvar";
[1,2,3,4].forEach(function(v) {
console.log("v:"+v);
console.log("myvar="+this.myvar);
}, this);
})();
See MDN forEach
Also, the above examples (if we're not dealing with methods on instances regarding this) work without using bind or the optional context argument for forEach, the following also works correctly:
function myFunction() {
this.myVar = 'myVar';
[1,2,3].forEach(function() {
console.log(this.myVar); // logs 'myVar'
});
}
myFunction();
Because javascript is functionally scoped, so the anonymous function can access the parent function's scope using this and it logs correctly. this only really becomes problematic as a context when dealing with instance methods.
The answer is no, a JavaScript function cannot determine the value of this in the caller.
You can bind the function passed with the current object, like this
function myFunction() {
this.myVar = 'myVar';
[1,2,3].forEach(function() {
console.log(this.myVar); // logs 'myVar'
}.bind(this));
}
In ECMA Script 6, you can use an Arrow function, like this
[1,2,3].forEach(() => {
console.log(this.myVar); // logs 'myVar'
});
An alternative to messing with the this variable when passing around callbacks, you could always just assign this to a new variable so child scoped functions can access it:
Array.prototype.each = function(fn) {
return this.forEach(fn, arguments.callee.caller);
}
function myFunction() {
var me = this;
me.myVar = 'myVar';
[1,2,3].each(function() {
console.log(me.myVar); // logs 'myVar'
});
}
now you don't have to remember to pass this as a second parameter
Firstly, it must be pointed out that myFunction is a constructor. Yet, the first letter in the identifier is not capitalized. Please call it MyFunction.
If a constructor is called without the new operator, this is bound to the global object, i.e. window in browsers. This makes the capitalization convention our only way of spotting such mishaps.
The following lines of code demonstrate this:
// After the original code...
myFunction();
console.log(window.myVar); // logs "myVar"
Secondly, to be able to apply functions on any array, instead of changing Array.prototype, consider the following:
var utils = {array: {}}; // utils.array is a container for array utilities.
utils.array.each = function (array, func) {
var i;
for (i = 0; i < array.length; i += 1) { func(array[i]); }
};
utils.write = function (s) {
console.log(s); // Alternatively, document.write(s);
};
utils.array.each([1, 2, 3], utils.write); // prints 1 2 and 3 (on separate lines)
Notice that we didn't use this and new. They make JavaScript look like Java, apart from that, they rarely serve a useful purpose.
While libraries may modify Object.prototype and Array.prototype, end-developers shouldn't.
Also, we should (ideally) be able to do something like:
utils.array.each([1, 2, 3], console.log); or
utils.array.each([1, 2, 3], document.write);.
But most browsers won't allow it.
Hope this helped.
If I understand your requirement correctly, then you are trying to override the "this".
I think this can help you.
I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)