I want to write several variables into an array for one call to the "addLetter" method, "push" and "concat" does not work
function Team(name) {
this.name = name;
this.letters = [];
}
Team.prototype.addLetter = function (letter) {
this.letters.push(letter).join('\n');
};
Team.prototype.toString = function () {
return "Name of team - " + this.name + '\n' +"ltters : " + this.letters;
};
var a = 's';
b='g';
v='d';
var team1 = new Team('letters');
team1.addLetter(a,b,v);
console.log(team1.toString());
If you're using ES6:
Team.prototype.addLetter = function() {
this.letters.push(...arguments);
};
With older syntax:
Team.prototype.addLetter = function() {
this.letters.push.apply(this.letters.push, arguments);
};
JavaScript allows any number of arguments to be passed to any function. All of these arguments are accessible via the arguments variable within the function, which is an array-like object, containing all arguments in the order they were passed.
Related
As of my knowledge, in javascript there are three concepts; call, apply and bind
I want to create these function with similar behavior.
Here is a polyfill for them (not accurate though, just what came into my mind):
Function.prototype.call = function(context, ...args) {
const fn = Symbol();
try {
context[fn] = this;
return context[fn](...args);
} catch(e) {
// Turn primitive types into complex ones 1 -> Number, thanks to Mark Meyer for this.
context = new context.constructor(context);
context[fn] = this;
}
return context[fn](...args);
};
Function.prototype.apply = function(context, args) {
return this.call(context, ...args);
};
Function.prototype.bind = function(context, ...args) {
return (...args2) => this.call(context, ...args, ...args2);
};
The only thing that is impossible to polyfill is fn.call(null), as that primitive can't be turned into a complex type, only native code can do this
Add your own call function like "_call"
Function.prototype._call = function(newcontext, ...arg){
var demoFn = new Function('tempfuncton', 'tempthis','arg' , '{ tempthis["f"]=tempfuncton; return tempthis.f(arg);}');
demoFn(this,newcontext,arg);
}
write a demo function
function anyfunction(args){
console.log(this,args)
}
call it like previous. First argument should be an object. Otherwise write a code to convert it into object.
anyfunction._call({'mm':'my provided this object'},"arg1","arg2")
function B(a,b,c){
console.log(a,b,c)
}
Function.prototype.OwnCallFunction = function(){
if( this.length == arguments.length)
this(...arguments)
else
console.error('Signature does not match')
}
B.OwnCallFunction(323,34,34)
I followed this approach to create own call function. With help of Function
Constructor, I add a function to it and it worked on firefox.
New approach, with more clarity
Function.prototype.call2 = function(context, ...args){
console.log(context)
const fn = Symbol();
context[fn] = this;
context[fn](...args);
}
In above answers I can see that spread operators has been used, but if we really want to make pollyfill of call then we should avoid spread
operator and latest concept of es6.I am sharing solution without es6.
Call Function:-
Function.prototype.myCall = function(obj) {
obj = obj || global;
var id = "00" + Math.random();
while (obj.hasOwnProperty(id)) {
id = "00" + Math.random();
}
obj[id] = this;
let arg=[];
for(let i=1;i<arguments.length;i++){
arg.push("arguments[" + i + "]");
}
result= eval("obj[id]("+arg+")");
delete obj[id];
return result;
}
Apply function:-
Function.prototype.myApply = function(obj, argArr) {
obj = obj || global;
var id = "00" + Math.random();
while (obj.hasOwnProperty(id)) {
id = "00" + Math.random();
}
obj[id] = this;
let arg=[];
let result
if(!argArr){
result= obj[id].fn();
}
else{
for(let i=0;i<argArr.length;i++){
arg.push("argArr[" + i + "]");
}
result= eval("obj[id]("+arg+")");
delete obj[id];
}
return result;
}
Bind function:-
Function.prototype.myBind2= function(){
let obj1= this;
const obj= Array.prototype.slice.call(arguments,0,1);
const arg= Array.prototype.slice.call(arguments,1);
return function(){
const arg2= Array.prototype.slice.call(arguments);
obj1.apply(obj[0],Array.prototype.concat(arg, arg2));
}
Another solution Bind: we can pass object argument of function
Function.prototype.myBind2 = function(obj) {
let fn = this;
const arg = Array.prototype.slice.call(arguments, 1);
return function() {
const arg2 = Array.prototype.slice.call(arguments);
fn.apply(obj, Array.prototype.concat(arg, arg2));
}
While each browser has its own source code for implementing Javascript, you can find how many of the native Javascript functions are implemented with the ECMA specifications found here:
http://www.ecma-international.org/ecma-262/10.0/index.html#sec-properties-of-the-function-prototype-object
For specs of apply, see: 19.2.3.1
For specs of bind, see: 19.2.3.2
For specs of call, see: 19.2.3.3
If you're interested for example, how Node implemented apply, you can dig into their source code on Github here: https://github.com/nodejs/node
Here is my sweet and simple solution. We are adding the ObjRef in prototypal chain to avoid any name conflicts with other properties
Function.prototype.call2 = function (objRef, ...args) {
otherObj = Object.create(objRef)
otherObj[this.name] = this;
otherObj[this.name](...args);
}
I don't think using Object.create makes sense here as when you will console this inside the function you won't see the desired object.
Here is my try(not accurate though) but will work.
Function.prototype.myCall = function (thisContext, ...param) {
let name = this.name;
thisContext[name] = this;
thisContext[name](...param);
}
Function.prototype.mycall = function(context, ...args){
context.fun= this; //add personal function to context
context.fun(...args);
}
function personal (msg){
alert(this.name + " " + msg);
}
let obj = {
name:'Ajinkya'
}
personal.mycall(obj,'Khandar'); // pass object and args in mycall
I have created a simple object that want to use as a blueprint for other objects. But something is not going well. Can anyone find out why? When I click one button it should change innerHTML of the div to display the properties of the new objects!
here is a fiddle
var objekt = {
name : "olsi",
surname : "Angjellari",
kot : function () {
return this.name + this.surname
},
print : function () {
id.innerHTML = this.kot()
}
};
http://jsfiddle.net/cTRuL/
There are a number of problems:
When you want to do:
var dana = new objekt();
Then, objekt must be a function, not just an object because an object is not a constructor, only a function can be a constructor. This is one way to do that:
function objekt() {
this.name = "olsi";
this.surname = "Angjellari";
};
objekt.prototype = {
kot : function () {
return this.name + this.surname
},
print : function () {
id.innerHTML = this.kot()
}
};
Second, in your jsFiddle if you're going to put function names in the HTML, then those functions have to be global. To make your code global in the jsFiddle, you have to set the upper left setting to "No wrap - in <body>" or "No wrap - in <head>". As you had your jsFiddle, your code was defined inside an onload handler and therefore NOT reachable by function calls from your HTML.
Third, once you make objekt into a function to fix the previous issue, then you can't call objekt.print() directly. You probably want to call .print on an actual object of type objekt like dana or boi.
You can see it all working here: http://jsfiddle.net/jfriend00/7TbEx/
you want to create a constructor so you can make instances using the new operator like you are trying
function objekt(name,surname){
this.name = name||"olsi";
this.surname = surname||"Angjellari";
this.kot = function () {
return this.name + this.surname
};
this.print = function () {
id.innerHTML = this.kot()
};
}
var obj = new objekt();
var obj2 = new objekt("First","Last");
obj.print();
obj2.print();
You could also make these through the prototype
function objekt(){}
objekt.prototype.name = "";
objekt.prototype.surname = "";
objekt.prototype.kot = function(){
return this.name + this.surname;
};
objekt.prototype.kot = function(){
id.innerHTML = this.kot();
};
var obj = new objekt();
obj.name = "First";
obj.surname = "Last";
var obj2 = new objekt();
obj2.name = "Last";
obj2.surname = "First";
obj.print();
obj2.print();
This is the most basic JS object example I can think of that illustrates my questions.
Question 1.
How can I reference functions within a class so that in other code I could call a method? This gives me an error.
var name1 = new Name();
name1.render();
Question 2.
What is the difference between declaring functions in-line like this vs. using var getByID = function() ...?
Example object:
function Name(user_id, container_id) {
this.userID = user_id;
this.containerID = container_id;
this.firstName = null;
this.lastName = null;
function getByID(userID) {
// An ajax call that takes a userID to get a name.
}
function setName() {
// An ajax call to get the name from db.
name_record = this.getByID(this.userID); ????? this returns an error that getByID is undefined.
this.firstName = name_record.firstName;
this.lastName = name_record.lastName;
}
function render() {
$(this.containerID).val(this.firstName + ' ' + this.lastName);
}
}
You can declare an object like you done in your second question, it's valid because a function is an object too. Or other ways like:
var Name = {
render: function() {
}
}
Name.render();
Or with prototype:
function Name() {
}
Name.prototype.render = function() {
}
// or
Name.prototype = {
getByID: function() {
},
setName: function() {
}
}
var n = new Name();
All these snipets are a valid object declaration.
Your second question may answer the first ones. When you declare a function like this:
function Name() {
function render() {
}
}
var n = new Name();
It is like render() be a private method. if you call outside the function name n.render(), you will see an error thrown because render is not recognized. But if you change to this...
function Name() {
this.render = function() {
}
}
... then n.render() will work like render() being a public method. See this and this for further information about public and private methods.
Now, the difference between declaring a function "in-line" or setting it to a variable is that with this:
function Name() {
}
You can do:
var n1 = Name();
var n2 = Name();
var nn = Name(); // and so on...
But with:
var n = function Name() {
}
Is that n() will work and Name() will not. Even var a = Name() will throw an exception.
Here's a good article about this subject that worth a read. I hope it can help.
Question 1:
A "Class" in Javascript is nothing more than an object. An object has properties which you can access. Those properties are either variables, functions, or other objects. By declaring your function like:
function render() {
$(this.containerID).val(this.firstName + ' ' + this.lastName);
}
You are declaring a function within the scope of function Name() but that function isn't a property of Name. It's just a private method. There are multiple ways you could make it a part of Name(), such as:
function Name(user_id, container_id) {
this.userID = user_id;
this.containerID = container_id;
this.firstName = null;
this.lastName = null;
this.render = function() {
console.log('hello world');
}
}
var name1 = new Name();
name1.render();
Question 2:
There is no difference. They are simply two different syntax's that achieve the same result. The second way (declaring a var and defining the function) immediately gives you a reference to the function, but that can be achieved just as well the first way.
Answer to your first question:
Functions getByID,setName and render are local to the constructor and cannot be called by class object. You have to use prototypal inheritance.
for eg.
function Name(user_id, container_id) {
this.userID = user_id;
this.containerID = container_id;
this.firstName = null;
this.lastName = null;
}
Name.prototype = {
getByID :function(userID) {
// An ajax call that takes a userID to get a name.
}
setName:function() {
// An ajax call to get the name from db.
name_record = this.getByID(this.userID);
this.firstName = name_record.firstName;
this.lastName = name_record.lastName;
}
render:function() {
$(this.containerID).val(this.firstName + ' ' + this.lastName);
}
};
Answer to your second question:
in case of
abc();//Error
function abc(){
}
this function is created at run time , so you can call it only after declaration
however, this
abc();
var abc = function(){
};
is created at parse time so you can call it before declaration.
I've been experimenting with QUnit tests and was looking for a good method to override functions with mocks so as to enable more atomic tests. There are good solutions out there for specific cases, for example overriding $.ajax (Simple jQuery (1.5+) AJAX Mocking), but I was looking for a more general approach and came up with this:
// Constructor for overrides.
function overrides(overrides) {
this.overrides = overrides;
}
// Implementation for overrides.
overrides.prototype = {
set: function () {
var functions = {};
$.each(this.overrides, function (key, value) {
eval("functions['" + key + "'] = " + key + ";");
eval(key + " = value;");
});
this.functions = functions;
},
reset: function () {
var functions = this.functions;
$.each(this.overrides, function (key, _) {
eval(key + " = functions['" + key + "'];");
});
}
}
Which can then be used like:
module("Comments", {
setup: function () {
this.overrides = new overrides({ "$.ajax": function (url, options) {
alert("ajax: " + url + ', ' + options);
}
});
this.overrides.set();
},
teardown: function () {
this.overrides.reset();
}
});
Now, that all appears to work fine, and although this may not be the worst possible use of eval(), I was wondering if this could indeed be written without using eval()? I've read up on a bunch of the other eval() questions here and tried various options like accessing the overrides using window[] but that does not work for the $.ajax case for example (window['$'].ajax works but not window['$.ajax']).
Perhaps I'm also thinking to hard and eval() can be used safely here or there is a better approach in general for function overrides?
Why can't you just treat the objects as objects?
functions[key] = key;
var arr = key.split('.');
var obj = window;
for (var i = 0; i < arr.length; i++){
if (obj) obj = obj[arr[i]];
}
obj = value;
The only way as far as I know is providing the object and property name separately. This is also the case with native functions such as Object.defineProperty, which takes the object as one argument and the property name as a string as another argument.
// overwrite properties inside `$`
new overrides($, {"ajax": function (url, options) {
alert("ajax: " + url + ', ' + options);
}});
And something like this:
function overrides(obj, overrides) {
this.obj = obj;
this.overrides = overrides;
}
and:
set: function () {
var functions = {};
var inst = this;
$.each(this.overrides, function (key, value) {
functions[key] = inst.obj[key]; // old function
inst.obj[key] = value; // overwrite function
});
this.functions = functions;
},
It works because inst.obj and $ refer to the same object - changing properties on inst.obj modifies $ as well in this case.
Is this possible? I am creating a single base factory function to drive factories of different types (but have some similarities) and I want to be able to pass arguments as an array to the base factory which then possibly creates an instance of a new object populating the arguments of the constructor of the relevant class via an array.
In JavaScript it's possible to use an array to call a function with multiple arguments by using the apply method:
namespace.myFunc = function(arg1, arg2) { //do something; }
var result = namespace.myFunc("arg1","arg2");
//this is the same as above:
var r = [ "arg1","arg2" ];
var result = myFunc.apply(namespace, r);
It doesn't seem as if there's anyway to create an instance of an object using apply though, is there?
Something like (this doesn't work):
var instance = new MyClass.apply(namespace, r);
Try this:
var instance = {};
MyClass.apply( instance, r);
All the keyword "new" does is pass in a new object to the constructor which then becomes the this variable inside the constructor function.
Depending upon how the constructor was written, you may have to do this:
var instance = {};
var returned = MyClass.apply( instance, args);
if( returned != null) {
instance = returned;
}
Update: A comment says this doesn't work if there is a prototype. Try this.
function newApply(class, args) {
function F() {
return class.apply(this, args);
}
F.prototype = class.prototype;
return new F();
}
newApply( MyClass, args);
Note that
new myClass()
without any arguments may fail, since the constructor function may rely on the existence of arguments.
myClass.apply(something, args)
will fail in many cases, especially if called on native classes like Date or Number.
I know that "eval is evil", but in this case you may want to try the following:
function newApply(Cls, args) {
var argsWrapper = [];
for (var i = 0; i < args.length; i++) {
argsWrapper.push('args[' + i + ']');
}
eval('var inst = new Cls(' + argsWrapper.join(',') + ');' );
return inst;
}
Simple as that.
(It works the same as Instance.New in this blog post)
Hacks are hacks are hacks, but perhaps this one is a bit more elegant than some of the others, since calling syntax would be similar to what you want and you wouldn't need to modify the original classes at all:
Function.prototype.build = function(parameterArray) {
var functionNameResults = (/function (.{1,})\(/).exec(this.toString());
var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
var builtObject = null;
if(constructorName != "") {
var parameterNameValues = {}, parameterNames = [];
for(var i = 0; i < parameterArray.length; i++) {
var parameterName = ("p_" + i);
parameterNameValues[parameterName] = parameterArray[i];
parameterNames.push(("parameterNameValues." + parameterName));
}
builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
}
return builtObject;
};
Now you can do either of these to build an object:
var instance1 = MyClass.build(["arg1","arg2"]);
var instance2 = new MyClass("arg1","arg2");
Granted, some may not like modifying the Function object's prototype, so you can do it this way and use it as a function instead:
function build(constructorFunction, parameterArray) {
var functionNameResults = (/function (.{1,})\(/).exec(constructorFunction.toString());
var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
var builtObject = null;
if(constructorName != "") {
var parameterNameValues = {}, parameterNames = [];
for(var i = 0; i < parameterArray.length; i++) {
var parameterName = ("p_" + i);
parameterNameValues[parameterName] = parameterArray[i];
parameterNames.push(("parameterNameValues." + parameterName));
}
builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
}
return builtObject;
};
And then you would call it like so:
var instance1 = build(MyClass, ["arg1","arg2"]);
So, I hope those are useful to someone - they allow you to leave the original constructor functions alone and get what you are after in one simple line of code (unlike the two lines you need for the currently-selected solution/workaround.
Feedback is welcome and appreciated.
UPDATE: One other thing to note - try creating instances of the same type with these different methods and then checking to see if their constructor properties are the same - you may want that to be the case if you ever need to check the type of an object. What I mean is best illustrated by the following code:
function Person(firstName, lastName) {
this.FirstName = firstName;
this.LastName = lastName;
}
var p1 = new Person("John", "Doe");
var p2 = Person.build(["Sara", "Lee"]);
var areSameType = (p1.constructor == p2.constructor);
Try that with some of the other hacks and see what happens. Ideally, you want them to be the same type.
CAVEAT: As noted in the comments, this will not work for those constructor functions that are created using anonymous function syntax, i.e.
MyNamespace.SomeClass = function() { /*...*/ };
Unless you create them like this:
MyNamespace.SomeClass = function SomeClass() { /*...*/ };
The solution I provided above may or may not be useful to you, you need to understand exactly what you are doing to arrive at the best solution for your particular needs, and you need to be cognizant of what is going on to make my solution "work." If you don't understand how my solution works, spend time to figure it out.
ALTERNATE SOLUTION: Not one to overlook other options, here is one of the other ways you could skin this cat (with similar caveats to the above approach), this one a little more esoteric:
function partial(func/*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(this, allArguments);
};
}
Function.prototype.build = function(args) {
var constructor = this;
for(var i = 0; i < args.length; i++) {
constructor = partial(constructor, args[i]);
}
constructor.prototype = this.prototype;
var builtObject = new constructor();
builtObject.constructor = this;
return builtObject;
};
Enjoy!
what about a workaround?
function MyClass(arg1, arg2) {
this.init = function(arg1, arg2){
//if(arg1 and arg2 not null) do stuff with args
}
init(arg1, arg2);
}
So how you can:
var obj = new MyClass();
obj.apply(obj, args);
One possibility is to make the constructor work as a normal function call.
function MyClass(arg1, arg2) {
if (!(this instanceof MyClass)) {
return new MyClass(arg1, arg2);
}
// normal constructor here
}
The condition on the if statement will be true if you call MyClass as a normal function (including with call/apply as long as the this argument is not a MyClass object).
Now all of these are equivalent:
new MyClass(arg1, arg2);
MyClass(arg1, arg2);
MyClass.call(null, arg1, arg2);
MyClass.apply(null, [arg1, arg2]);