refering to a method without creating an instance first / map a method with jQuery or any javascript map function - javascript

I have this:
var Coords = function(x, y){
this.x = x;
this.y = y;
}
Coords.prototype.toArray = function(){
return [this.x, this.y];
}
Now I have an array of Coords object. I'd like to convert each Coords instance into an array with the toArray method.
I could write a loop, but I'd rather use $.map, as it's shorter and more readable.
Unfortunately, this:
return $.map(coords_array, Coords.prototype.toArray);
doesn't work at all. It just stops the execution.
The problem might be about how to refer to a method independently of any object.
Any way of pointing to a method without creating an instance first?
Or to use $.map with a method?
Thanks for your insights.
EDIT: well, in fact, it doesn't stop the execution (this came from another problem) but $.map(coords_array, Coords.prototype.toArray); returns [null, null, null, null, null...].
I find this behavior strange.

Try something like:
return $.map(coords_array, function(val, i) { val.toArray(); });
And refer here for more reference on jQuery's map function.

Apparently, $.map does not set the context (this) to the element currently being processed (like e.g. $.each does).
You can either go with a wrapper:
$.map(coords_array, function(coord) { return coord.toArray(); });
or extend the toArray() method to also work with the first argument:
Coords.prototype.toArray = function() {
var self = this instanceof Coords ? this : arguments[0];
return [self.x, self.y];
}

The reason that:
> $.map(coords_array, Coords.prototype.toArray);
doesn't work as expected is that you are passing a reference to the function to map, so when it's called, its this keyword isn't set to the instance and defaults to the global obejct (or undefined in ES5 strict mode). You should be able to do:
$.map(coords_array, function(item, index) {
Coords.prototype.toArray.call(item);
});
so that the function's this is set to the instance.
Edit
See Jordan's answer.

Related

Using [].push.call() to modify an object's length

Cannot reproduce MDN's example («Using an object in an array-like fashion»).
let obj = {
length: 0,
addEl: function (element) {
[].push.call(this, element);
};
};
// Node REPL still expect me to do something, so there's an error. Why?
Could you, guys, explain what's wrong here? Also it seems that I don't get the point with the mechanics here:
// from the example:
obj.addElem({});
obj.addElem({});
console.log(obj.length);
// → 2
What if we call the function with some different agrument, not {}, will it work? And if it won't, then why we should use {} exactly? What is the this context here: addEl method or the object itself? If the second, why not addEl function: it's not an array function, so it should have its own this (and, I guess, I'd use something like objThis = this; property).
One more related question is here.
The code in your post has some typos:
let obj = {
length: 0,
addEl: function (element) {
[].push.call(this, element);
};
^ syntax error
};
// Node REPL still expect me to do something, so there's an error. Why?
As you suspected in your comment in the code,
there is a syntax error, which I marked for you.
Remove that semicolon.
And then, when trying the example you wrote obj.addElem,
but in the above object literal you have addEl.
The example should work just fine, if you simply copy-paste it.
var obj = {
length: 0,
addElem: function addElem(elem) {
// obj.length is automatically incremented
// every time an element is added.
[].push.call(this, elem);
}
};
// Let's add some empty objects just to illustrate.
obj.addElem({});
obj.addElem({});
console.log(obj.length);
// → 2
What if we call the function with some different argument, not {}, will it work?
Sure it will. Why wouldn't it? An array in JavaScript can contain values of different types.
It doesn't need to be homogeneous,
so yes, you can insert other things than {}.
What is the this context here: addEl method or the object itself?
It's the object on which the method is called. So it's obj.
This is how method invocation works.
When you call obj.something(), the this inside something will be the obj.
If you still have some doubts about this example, feel free to drop a comment.
Since an object is not an array, but can behave like an array you need to borrow push from the Array object.
But in this case this refers to the array object created with the shorthand []. So we need to change this into the scope for obj using call.
Because there is a length property defined, push will update this value.
An empty object is passed as an element {}, but any other will do:
let obj = {
length: 0,
addEl: function(element) {
Array.prototype.push.call(this, element); //also borrowing push from the array.prototype prevents an extra array to be made in memory every time we call upon this function.
} //« fixed the typo here
};
obj.addEl({});
obj.addEl(1);
obj.addEl('a');
obj.addEl(true);
console.log(obj);
var array = {
length: 0,
push: function(obj){
this[this.length] = obj;
this.length++;
}
}
array.push(23);
You can try this, this solves your problrm i guess.

Defining which method can be chained

I created a class that supports chaining by making use of return this;, and
now I need to make the current method tell what methods can be chained. Example:
class MyClass {
constructor(path) {
this.path = path;
}
method1() {
// some code here...
// i must make something here to only allow channing if it's method
// blabliblu
return this;
}
blabliblu() {
// some code here...
// same thing here, if the channing is with the method ar_2, it's ok.
return this;
}
ar_2() {
// And so on...
return this;
}
}
So, i can do: method1().blabliblu(), but i can't do method1().ar_2(). Is there lib to help me achieve this?
What you have asked for is not possible in Javascript. When you do:
return this;
in your methods, you are returning the same object. Any public method can be called on the object that is returned and there is no way to specify that because the object was returned from a method it should somehow not allow some methods to be called.
So, using the code in your example where you return this in .method1(), there's no difference between this:
obj.method1();
obj.ar_2();
and this:
obj.method1().ar_2();
That's because the chained version is essentially this internal to the JS interpreter:
let temp = obj.method1();
temp.ar_2();
So, if temp is the same as obj which it is when you return this from method1(), then obj.method1().ar_2(); is just the same as obj.method1(); obj.ar_2(); (with a little less typing). Thus, you can't prevent the calling of .ar_2().
Both just call .method1() and then .ar_2() on the same object. So you can't prevent one scheme, but allow the other. ar_2 is either a public method or it isn't. You can't have it callable in one place and not callable in another on the same object.
Now, you could make obj.method1() return a different object. If you did that, then that different object could have different methods on it and could be an object that does not have a .ar_2() method.
When you chain array methods like this:
let result = [1,2,3].map(...).filter(...);
Each step in that chain is returning a different object (they are not doing a return this, but are creating a new object and returning it. In this specific Array example, these are returning different objects, but of the same type, but you could return different objects of different types. For example:
let result = ["a","b","c"].join("").toUpperCase();
Here, .join() is an Array method, but returns a string object which you can then only call string methods on. So, you could do something like that where you return a different type of object.

Call a function with a variable number of arguments in JavaScript (similar to call())

I'm familiar with the way call(), which you can pass a variable number of arguments that will be loaded into a function's parameters when called. I'm trying to do something related where I recurse through nested set objects in RaphaelJS with forEach (analogous to jQuery's each), determine whether the child element is another set, and apply a function with a variable number of arguments if not. I want to make it generic so that I can apply any function, but make the functions that I pass have simple parameter constructors without having to access the arguments property of the function.
function recursiveFncApply(set, fnc, args) {
set.forEach(function(item) {
if (item.type == 'set') {
recurseApplyFncToSets(item, fnc, args);
} else {
fnc(item, args);
}
});
}
function translateOperation(element, operation, x, y)
// do stuff to element with operation, x, and y args without accessing
// accessing arguments property
}
recursiveFncApply(passedSet, translateOperation, [arg1, [arg2, ...]]);
I want to do this so that I can use multiple functions without having to repeat myself with code that determines arguments and properly assigns them before usage. I'm not sure whether there's some kind of functionality or language utility that I'm missing that would enable me to do this, or somehow to programmatically "construct" a function call from the remaining arguments passed to recursiveFncApply. Is this possible in JavaScript?
Clarification: I want to pass a variable number of arguments to my recursive function that will be passed to any function that I want to be applied to the contents of the sets my recursive function is working on. So I want to be able to make recursiveFncApply work generically with any function while still using an argument structure that works like a function being executed via call().
Say I have another function in addition to translateOperation:
function anotherFunction(element, differentArg) {
// do something with one argument
}
Ideally I could then use my recursiveFncApply in this way:
recursiveFncApply(passedSet, translateOperation, operation, x, y);
recursiveFncApply(passedSet, anotherFunction, singleArg);
As well as this way:
recursiveFncApply(passedSet, anotherFunction, singleArg);
I believe that this is similar to how call() works in that I could do:
anotherFunction.call(this, element, differentArg);
.. without having to change the structure of anotherFunction to sort out the arguments property, or pass an object/array.
It turns out that Felix King had the right idea/was the closest. I found a direct answer to my question as soon as I realized what I was actually trying to do, which is pass forward arguments from function to function (found the answer here). So I got this to work with this code:
function recursiveSetFncApply(set, fnc/*, variable */) {
var me = this;
var parentArgs = arguments;
set.forEach(function(element) {
if (element.type == 'set') {
parentArgs[0] = element;
me._recursiveSetFncApply.apply(me, parentArgs);
} else {
// Generate args from optional arguments and pass forward; put element in args at front
var args = Array.prototype.slice.call(parentArgs, 2);
args.unshift(element);
fnc.apply(element, args);
}
});
}
I make a reference to arguments with parentArgs because I need to update the set property in arguments before I pass it forward to the next recursive loop, otherwise I hit an infinite loop because it hasn't updated at all because it's using the original arguments set. I was under the impression that apply() will not actually pass forward arguments, but simply pop an array into the new function that you have to access by index--this isn't the case. When I used apply() on translateElementOperation, I had all the arguments I needed in their exact places. Here's the updated function I ran through the recursive apply:
function translateElementOperation(element, operation, x, y) {
var currentPath = element.attr('path');
translatedPath = Raphael.transformPath(currentPath, [operation, x, y]);
element.attr('path', translatedPath);
}
Thanks for the help, everyone!
Use .apply instead of .call
functionName.apply(element, [any, number, of, variables, ...]);
// instead of this
functionName.apply(element, set, of, variables, ...);
This is more useful like so:
var fnVars = [];// fill this anyway you want.
functionName.apply(element, fnVars);

How to create a custom javascript in-bulit apply method

Lets consider this example:-
function X(){
var Y = function(arg1,arg2){
document.write(arguments.length);
document.write(arg2);
};
Y(arguments);
}
x(1,2,3,4,5);
/*Outputs 1 and undefined respectively.
Because here i am actually passing an array like-object to Y. */
By using apply here i am getting the desired results.
function X(){
var Y = function(arg1,arg2){
document.write(arguments.length);
document.write(arg2);
};
Y.apply(this,arguments);
}
x(1,2,3,4,5) //outputs 5 and 2
I want to create an apply like method that takes an Array of argument and invoke that function by passing arguments as seperate parameter values.
Like:
var arr=[1,2,3,4];
Y.apply_like_method(arr);
//and returns like Y(1,2,3,4)
Given this code:
var arr=[1,2,3,4];
Y.apply_like_method(arr);
//and returns like Y(1,2,3,4)
To make that work:
Function.prototype.apply_like_method = function(args) {
return this.apply(this, args);
}
Disclaimer: For illustration purposes only.
In other words, there's no way around .apply().
Just for shits and giggles using eval.
function myApply(fun, ar){
var i, r = [];
for(i=0; i<ar.length; ++i)
r[i] = 'ar['+i+']';
eval('fun('+r.join(',')+');');
}
You want to use the call method instead. See the MDN. What you are describing though is a hybrid of the call method and apply method; you want the ability to supply parameters individually, but to supply them to the function as an array. That, to my knowledge, doesn't exist currently and it would be easier to use apply/call as it was originally intended, or use a javascript object to pass the params into the function.

Get name of prototype object

This question just got upvoted so can update question with what I did
I solved it by iterating over the window object (or user specified object root) and when I found the correct instance I backtracked and got the name from the index. The final solution can be found here
https://github.com/AndersMalmgren/Knockout.BindingConventions
Update end
I'm planning on writing a convention over configuration template source engine for KnockoutJS / MVC.
I'm started with a little client side POC and ran into a show stopper right away
My plan is use this syntax or something similar
MyApp.EditCustomersViewModel = function() {
ko.templates.loadView(this);
};
When doing this it will check the tamplate cache or fetch the templates from server using the object name as key.
The problem is I cant get the name of the prototype object, i tried this
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
If works for objects defined like this
function MyClass() {
}
If you add a prototype to the above object it will not work, or if you define it like this
MyApp = {};
MyApp.MyClass = function() {
};
Prototype and scoping is two musts so this is a showstopper, any ideas?
Fiddle: http://jsfiddle.net/aRWLA/
edit: The background for this is like this.
On the server you have structure like this
Templates\ [ViewName]\index.html
Templates\ [ViewName]\sub-model-template.html
on the client you will do
MyApp.EditCustomersViewModel = function() {
ko.templates.loadView(this);
};
which will generate a ajax request with the objects name as key, which will fetch all the templates for the view in question
Only hoisted functions (function someFunc() {) have a retrievable name.
Assigned functions do not, because you are not technically naming the function but creating an anonymous function and assigning a reference to it (in the memory) to a named variable.
So it's the var, not the function, that is named.
This makes the very idea of retrieving function names pretty much a none-starter, since in any vaguely mature pattern you'll be writing methods, not hoisted functions - and methods of course are assigned functions.
Named expressions (see other answers) are a partial workaround but these have other issues - not least lack of support in older IEs.
(Sidenote: I've long expected browser vendors to build around this such that the names of assigned functions became retrievable, but no joy yet AFAIK.)
I think you problem in improper replacing function prototype: if you replace function prototype object then you must preserve constructor member in prototype:
function Test1() {
}
Test1.prototype={
constructor: Test1
};
MyApp={};
MyApp.MyClass=function MyClass(){
};
MyApp.MyClass.prototype={
constructor: MyApp.MyClass
};
Your example: http://jsfiddle.net/aRWLA/1/
Modified example: http://jsfiddle.net/aRWLA/2/
You can make use of named function expressions:
MyApp.MyClass = function MyClass() { ... };
But note that (suprise) they don't work correctly in all versions of IE.
See: http://kangax.github.com/nfe/
THIS DOES NOT ANSWER THE QUESTION
However, the code might be useful to other people, so I'm leaving it here, just in case. I don't expect upvotes, but please don't abuse it for downvoting either. Thanks.
I don't know your use case, as such I think you've got a design issue - the problem you describe shouldn't happen in practice.
But let's say you do need to have this working. An easy way to do what you need would be something like:
function findNamed(obj, fn){
for(var p in obj)
if(obj[p] === fn)
return p;
return false;
}
var m = {};
m.MyClass = function() {};
console.log(findNamed(m, m.MyClass));
Of course, the solution could be made into a more appropriate OOP form, but this is just to give an idea.
To replicate your use case, it would look like:
m.MyClass = function() {
findNamed(this, arguments.callee);
};
So, the final code is:
Object.prototype.getNameOfCall = function(fn) {
for(var p in this)
if(this[p] === fn)
return p;
throw "Callback not in object.";
};
var m = {};
m.MyClass = function() {
console.log(this.getNameOfCall(arguments.callee)); // MyClass
};
m.MyClass(); // test it out

Categories