I´m trying to understand how Function.prototype.call() works.
I know what it does, and I can work with it, but I´m curious about how this method is implemented.
Is it possible to write a javascript-method from scratch that does exactly the same?
It's not possible to "unwrap" variable arguments without eval. If it's fine with you, you can try this:
function myCall(fun, obj) {
var args = [].slice.call(arguments, 2);
var arglist = args.map(function(_, n) { return "args[" + n + "]" }).join(',');
obj._tmp = fun;
return eval("obj._tmp(" + arglist + ")")
}
Example:
foo = {
x: 123
}
bar = function(y) { return this.x + y }
console.log(myCall(bar, foo, 444))
Related
I would like to override toString using ES5. Given the following function which returns a complex number:
function complex(real, imaginary){
var z = Object.create(complex.prototype);
z.real = real;
z.imaginary = imaginary;
return z;
}
In order to override toString inherited from Object I do the following:
complex.prototype.toString = function(){ return this.real + " + " + this.imaginary + "*i";};
TEST:
var z = complex(1,1);
console.log(z);
RESULT:
{ real: 1, imaginary: 1 }
Do I miss something?
There are two things going on:
In many environments (including most browsers), console.log doesn't use toString, and
Your complex function doesn't return an object that uses complex.prototype as its prototype, so it doesn't have your toString. You're creating the object you return using {}, which creates an object directly inheriting from Object.prototype.
If you want to call complex without new, you need to explicitly use complex.prototype when creating your object, for instance via Object.create(complex.prototype). (And then when logging, trigger toString on purpose, either calling it directly or converting to string in various other ways.)
Example:
function complex(real, imaginary){
var z = Object.create(complex.prototype);
z.real = real;
z.imaginary = imaginary;
return z;
}
complex.prototype.toString = function(){ return this.real + " + " + this.imaginary + "*i";};
var z = complex(1, 1);
console.log(String(z));
You could call complex via new if you like (but I'm guessing you chose not to on purpose). Just FWIW:
function Complex(real, imaginary){
this.real = real;
this.imaginary = imaginary;
}
Complex.prototype.toString = function(){ return this.real + " + " + this.imaginary + "*i";};
var z = new Complex(1, 1);
console.log(String(z));
or even
class Complex {
constructor(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
}
toString() {
return this.real + " + " + this.imaginary + "*i";
}
}
const z = new Complex(1, 1);
console.log(String(z));
Side note: I've changed "imagenary" to "imaginary" above, which is the correct spelling in English. (Most of the text in your code seemed to be in English, but apologies if it's just that you translated the code in the question from another language and just forgot to translate "imagenary".)
Let me propose an example that works, then follow up with what fails, highlighting the point to my question.
Here, we have 3 functions being called (1 named, 2 anonymous):
var add = function(a, b) {return a+b};
var multiply = function(a, b) {return a*b};
function myFunction(fxn) {
return function(x) {
return function(y) {
return fxn(x,y);
}
}
}
myFunction(add)(2)(3)
Understandably, this call fails:
myFunction(add)(2)(3)(4)
How would I detect how many functions are being called? In the 2nd call, I'm calling 4 functions (1 named, 3 anonymous).
How would I rewrite the myFunction function in a way that compensated for any given amount of calls? I know we can detect how many arguments a function was given, but is there a way to detect how many functions are being called? I hope I worded this correctly. Thanks.
To find out if a variable contains a reference to a function you can use below code:
if (typeof(v) === "function") alert("This is a function")
Based on above you can find out on how many nested functions there are
function myFunction() {
return function() {
return function() {
return 1 + 2;
}
}
}
var count = 0;
var v = myFunction();
while (typeof(v) === "function") {
count++;
v = v();
}
alert("Nr of nested functions: " + count)
Even if this has no practical use case I can think of, this is a possible solution:
var add = function(a, b) {
return a + b
};
var multiply = function(a, b) {
return a * b
};
var counter = 0;
var result = 0;
function myFunction(fxn) {
counter = 1;
result = 0;
return function first(x) {
++counter;
return function second(y) {
++counter;
x = result ? result : x;
result = fxn(x, y);
return second;
}
}
}
myFunction(add)(1)(2)(3)(4);
alert('Result is: ' + result + '; Parentheses count: ' + counter);
I'm new on Javascript. I'm trying to do this exercise, but i cannot found anything to help me with that.
I created this object
var Foo = function(value) {
this.val = value;
}
And now I need sum two Foo objects with + operator.
var first = new Foo(2);
var second = new Foo(3);
var result = first + second; //should be 5
Do I have a way to overload operator + (like c++) , declare my Foo object as an Integer or something like that?
var result = first.val + second.val;
alert(result); // 5
jsfiddle: http://jsfiddle.net/czhE3/
UPD
OK, without val:
var Foo = function(value) {
this.val = value;
this.toString = function() {
return this.val;
}
}
var first = new Foo(2);
var second = new Foo(3);
var result = first + second;
alert(result); // 5
http://jsfiddle.net/czhE3/1/
As the previous people have mentioned you cannot override operators in javascript, but you could implement a valueOf which is the arithmetic version of a toString().
Add the follow code line:
Foo.prototype.valueOf = function () { return this.val; };
or
this.valueOf = function () { return this.val; };
Working fiddle http://jsfiddle.net/fJJL9/
There is no way in Javascript to overload operators as in C++. The closest thing you can make is a sum function for your objects, like
function sum ( a, b ) { return a.value + b.value; }
This is similar to two other questions I've asked today, but I'm still trying to understand how to assign variables correctly in JavaScript.
The output to my code is this:
x: 3
x: undefined // I was expecting 3 here
And here's my code:
var myApplication = {};
(function() {
function beep(x) {
console.log('x: ' + x);
var closure = {};
return function() {
console.log('return function() {');
if (arguments.length) {
console.log('setter: ' + x);
closure.result = x;
} else {
console.log('getter: ' + closure.result);
return closure.result;
}
}
}
myApplication.beep = beep;
})();
myApplication.beep(3);
RESULT = myApplication.beep();
I think the problem is where I say: myApplication.beep = beep;
I think that I've got to assign it either via the prototype or some other way.
First of all, functions are first class citizens in javascript.
So when you do
return function() {
console.log('return function() {');
if (arguments.length) {
console.log('setter: ' + x);
closure.result = x;
} else {
console.log('getter: ' + closure.result);
return closure.result;
}
}
This function is not executed, you are only returning as the value of your beep function.
So, in our case, the only code that really gets executed is :
var myApplication = {};
(function() {
function beep(x) {
console.log('x: ' + x);
}
myApplication.beep = beep;
})();
myApplication.beep(3);
RESULT = myApplication.beep();
In this case you are only logging the first argument passed to beep, so 3 then undefined.
Now for what you want to do here, no need to use closures, or prototypes :
var myApplication = {
x : null,
beep : function (x) {
if (typeof x != 'undefined') {
this.x = x;
} else {
return this.x;
}
}
};
// set x
myApplication.beep(3);
// get x
var x = myApplication.beep();
console.log('x :', x);
I would avoid messing with closures too early.
When you call beep(3) the first time, it's returning a function - but you aren't actually doing anything with that function. I think you might have meant this on the second-to-last line?...:
myApplication.beep = myApplication.beep(3);
As it is, I think the second call to beep is just returning another function, but with its 'x' argument set to undefined.
Also: To save some code-writing, rather than declaring and then assigning 'beep', you could write this:
myApplication.beep = function(x) { ...
Or, the whole object can be declared at once from the beginning:
myApplication = {
beep: function(x) {
},
otherFn: function(y) {
}
}
Please consider the following code:
<html>
<head>
<script type="text/javascript">
function a(){
var v = 9;
var w = 2;
var x = 7;
var template = '{w} + {x} = {v}';
var func = eval('(' + c.toString() + ')');
func(template);
}
function b(){
var v = 1;
var y = 'hello';
var z = 'world';
var template = '{v}. {y} {z}';
var func = eval('(' + c.toString() + ')');
func(template);
}
function c(template){
var re = /{(.+?)}/;
var match = template.match(re);
while (match != null){
template = template.replace(re, eval(match[1]));
match = template.match(re);
}
alert(template);
}
</script>
</head>
<body>
<input type="button" value="a" onclick="a()"/><br/>
<input type="button" value="b" onclick="b()"/><br/>
</body>
</html>
This code has two functions (a and b) and a parsing function c that receives a string template as parameter and parses it, using variables that are scoped in the calling function (a or b).
This means that function c has to 'know' all the variables that are known to whichever function was calling it.
What I want is for c to 'know' all the variables in the scope of its caller.
My solution was this line of code in a and b:
var func = eval('(' + c.toString() + ')');
What this does is redefine c as func inside the calling function, so in effect making it a sub function of the caller and thus bringing it into the same scope.
This solution works great, but the problem with it is that it's ugly. I have to turn c into a string and re-eval it to a function every time I want to use it. I'm hoping someone can suggest a better solution, if such exists.
I don't want to pass all the variables as parameters to c because:
The template to parse can be very big and include anywhere from1 to dozens of variables.
If I pass all the variables as parameters to c and access them using the arguments array in c it means I have to use array notation inside the template which is bad practice for obvious reasons.
Putting all the variables into a hash map object and passing that object as parameter to c is possible, but makes for a huge coding overhead to create this hash map from the caller's variables before any call to c.
Note:
Please don't bother pointing out to me that the parsing function is not perfect, it's just a simplified example of my actual code.
You're overcomplicating things. You can eliminate the need to cross scopes by packing your replacement values as an object rather than as individual variables, and using the g flag and a replacement function allow you to greatly simplify c(). Give this a try:
function a(){
var values = {
v: 9,
w: 2,
x: 7
};
func(c('{w} + {x} = {v}', values));
}
function b(){
var values = {
v: 1,
y: 'hello',
z: 'world'
};
func(c('{v}. {y} {z}', values));
}
function c(template, values) {
return template.replace(/{(.*?)}/g, function(match) {
return values[match[1]];
});
}
After playing with it for a while, this is the closest I've been able to come to passing the local scope into another function. It's seriously hacky, involves a fair bit of code duplication, and still needs eval() (though not as much), but it may be what you're looking for.
Basically, this involves declaring all of your local variables as function parameters (instead of using var statements) so that their names can be extracted by converting the function back to source via .toString(). These parameters are not supplied when calling a() and b()!
(Note that the c() function here is identical to the one in my other answer.)
rxArgs = /^[^(]+\(([^)]+)\)/;
function a(v, w, x){
v = 9;
w = 2;
x = 7;
var args = rxArgs.exec(arguments.callee.toString())[1].split(", ");
var i = args.length, values = {};
while (i--) values[args[i]] = eval(args[i]);
func(c('{w} + {x} = {v}', values));
}
function b(v, y, z){
v = 1;
y = 'hello';
z = 'world';
var args = rxArgs.exec(arguments.callee.toString())[1].split(", ");
var i = args.length, values = {};
while (i--) values[args[i]] = eval(args[i]);
func(c('{v}. {y} {z}', values));
}
function c(template, values) {
return template.replace(/{(.*?)}/g, function(match) {
return values[match[1]];
});
}
At this point, however, you're introducing so much boilerplate into each function that you're probably better off simply inlining c() instead.
function a(){
var v = 9;
var w = 2;
var x = 7;
func('{w} + {x} = {v}'.replace(/{(.*?)}/g, function(match) {
return eval(match[1]);
}));
}
function b(){
var v = 1;
var y = 'hello';
var z = 'world';
func('{v}. {y} {z}'.replace(/{(.*?)}/g, function(match) {
return eval(match[1]);
}));
}
I would strongly suggest having your code really parse the templates and interpret the "{foo}" references explicitly in your own code, instead of using eval() for everything.
It's not really clear why code like your "a()" and "b()" examples even need a template mechanism. In a language with first-class function objects like Javascript, what your code seems suspiciously desirous of achieving can be done much better by just programming functionally.
EDIT:
Your question seems to imply that the values will be coming from the caller. If they're all coming that way, you could just pass along the arguments object to c.
Then in the c function, grab the next item in the arguments object you passed for each match in the template.
Example: http://jsfiddle.net/u99Bj/1/
function a(){
var template = '{w} + {x} = {v}';
c(template,arguments);
}
function b(){
var template = '{v}. {y} {z}';
c(template,arguments);
}
function c( template, args ){
var re = /{(.+?)}/;
var i = 0;
var match = template.match(re);
while (match != null){
template = template.replace(re, args[i++]);
match = template.match(re);
}
alert(template);
}
If some of the functions will have some static values, then you would need to convert the arguments into an Array, and supplement the Array as needed.
Can't you do that :
function c(template,caller)
{ ...
}
and call
c(template,this)
then you could just get the variables as members of this (passing the function as an object instead of passing its scope)
EDIT
What about this approach?
function a(){
this.v = 9;
this.w = 2;
this.x = 7;
this.template = '{w} + {x} = {v}';
}
function c(obj){
var template = obj.template;
var re = /{(.+?)}/;
var match = template.match(re);
while (match != null){
template = template.replace(re, obj[match[1]]);
match = template.match(re);
}
alert(template);
}
<input type="button" value="a" onclick="c(new a())"/><br/>
What about this, using simply this keyword
<script type="text/javascript">
function a(){
this.v = 9;
this.w = 2;
this.x = 7;
var template = '{w} + {x} = {v}';
c(template);
}
function b(){
this.v = 1;
this.y = 'hello';
this.z = 'world';
var template = '{v}. {y} {z}';
c(template);
}
function c(template){
var re = /{(.+?)}/;
var match = template.match(re);
while (match != null){
template = template.replace(re, eval(match[1]));
match = template.match(re);
}
alert(template);
}
</script>
All variables will be initialized in document by default. You can encapsulate in other objects. Be careful about optional variables, as they may not be cleared on method call, and may interfere with parsing.