using prototype method we can create new methods... like...
Object.prototype.newMethod=function(){
// do something
}
Here I am defining the newMethod with an anonymous function... now if I want to use this method, I have to use it like: <object>.newMethod();
But now I want to create a new method which I can use like: <object>.newMethod;... no brackets... How can I do that...??
please don't use any jQuery...
Erm, you can't. To call a method, you write parentheses after it. Otherwise you're just referencing it.
The only exception to this rule is when you write something like new Date, where the parentheses are implict due to the new keyword and only because there are no arguments given.
I can't really understand why you would want to do that, but it is possible, albeit with a nasty hacky workaround. What you're actually looking for, AFAIK, is a magic property (like the someArray.length property).
var foo = {val:'foo'};
foo.length = (function(that)
{
return function()
{
return that.val.length;
}
})(foo);
//at this point foo.length(); returns 3, but still requires parentheses
//so, build another closure, and assign a valueOf method to the lenth method:
foo.length.valueOf = (function(method)
{
return function()
{
return method();//call the length method
}
})(foo.length);
console.log(foo.length +1);//logs 4
foo.val += 'bar';
console.log(foo.length);//logs 6
//BUT:: be carefull!!
alert(foo.length);//coerces to string, we haven't redefined the toString method, so the function code will be alerted
alert(foo.length + '');//alerts 6
This is just to show you that, yes it is theoretically possible, but please, please, don't use this kind of overly polluted hacks... I haven't thoroughly tested this, but ATM, I've already noticed that console.log(foo.length); can return a different value, not sure why, yet:
foo = {val:'foo'};
foo.length = (function(that){return function(){ return that.val.length;};})(foo);
foo.length.valueOf = (function(method){return function(){return method();};})(foo.length);
foo.length;//returns 3, great
foo.val += 'bar';
console.log(foo.length);//logged 3 at first, now it's back to logging 6!<-- don't trust this is the conclusion
The only way to call a function without parenthesis would be to define it using getters and setters.
Note these are new to JavaScript 1.8 and are not supported by all browsers.
Related
Can someone explain to me the difference of when to use a function by feeding your variables into the parenthesis, and when to tack the function on after the variable with a period, like using the toString() function?
example code
function addMe(a){
a = a+1;
return a;
}
var num = 1;
addMe(num);
num.toString();
I'm not actually sure if my syntax is correct, but I want to know when to feed a variable as a parameter, like how I feed the variable num, to the addMe function. And when to use the function .toString() by putting a period after the variable and typing out the function.
could I have done something like this- provided I built my function correctly?
var num = 1;
num.addMe();
Thanks for the help!
The first is used for simple 'stand alone' functions, while the latter is used for object methods. E.g a number object by default has a toString() method. Some object methods may also require parameters to be passed between the parentheses.
Variables (a function declaration is just a function stored in a variable) are looked up in the scope chain (going up to the next outer scope until a variable with the name is found):
let a = 1; // outer scope
{ // inner scope
console.log(a); // looked up in "inner scope", than "outer scope"
}
Properties of an object are looked up in the objects prototype chain, so if you do
a.b
then a gets looked up in the scopes as explained above, then b is accessed on the resulting object (everything is an object in JavaScript, except for "nothing" (undefined, null)) by looking up the prototype chain. For a simple object, the chain is quite short:
const a = { b: 1 }; // object -> Object.prototype
Here b will be found in the object itself. However all objects inherit from the Object.prototype object, so if you add a property to that (please don't):
Object.prototype.test = 1;
you can then look it up on every object, as the lookup traverses up the prototype chain, and reaches Object.prototype:
console.log({}.test); // 1
Now for numbers (like in your case), they inherit the Number.prototype so you could do:
Number.prototype.addMe = function() {
console.log(this);
};
// two dots are needed to distinguish it from numbers with a fraction (e.g. 1.2)
1..addMe();
That said, now addMe can be called on every number, everywhere in your code. While that might seems useful, it is actually a pain as you don't know where a certain method was added
1..whereDoIComeFrom()
that makes code unreadable and unstructured. Instead if you need a certain functionality multiple times, abstract it into a function, don't touch the native prototypes.
I assume that addMe is just a simplified example, if it isn't, read on:
If you pass an argument to a function in JavaScript, the value will be copied (it is a bit more complicated with non primitives (everything except numbers, booleans etc.)) into the parameter variable of the function called so here:
function addMe(a){
a = a+1;
console.log(a); // 2
return a;
}
var num = 1;
addMe(num);
console.log(num); // 1 ... ?
you actually got two variables (a and num), changing a does not change num. But as you return a you can do:
num = addMe(num);
which copies the value of num into a, then increases a by one and then copues the value of a back to num.
When you did var num = 1 you created a JavaScript object. It looks just like a number but you can think of everything in JavaScript as an object (simplification) and all these objects have different features. So a number has some features, a string has some other features, etc.
You mentioned one feature: toString. Another feature would be toLowerCase.
toString and toLowerCase are functions that come with JavaScript. These functions are then "put on" all of these objects for us to use.
I can have a string variable like
var text = 'MY TEXT'
var lowercaseText = text.toLowerCase()
console.log(lowercaseText) // my text
This code will work because it was decided that the toLowerCase function should work on strings
I can also have an array (list of items)
const list = ['A', 'B', 'C']
const answer = list.toLowerCase()
console.log(answer)
But this code won't work because toLowerCase doesn't work on arrays. So you get the following error message: list.toLowerCase is not a function.
Basically its saying: I don't know what toLowerCase means when used on this list variable (array).
In JavaScript this is called prototypes. Prototype is a way for JavaScript to get some feature from another. Basically: I have all kinds of functions, what object can use what functions. This is called the prototype chain.
In both cases you are using a function. addMe is a function you created and toString is a function in JavaScript that has been placed on objects through this prototype-chain.
Im not actually sure if my syntax is correct
Yes your syntax is correct. Your addMe function is the standard way to create a function in JavaScript.
But i want to know when to feed a variable as a parameter, like how i
feed the variable num, to the addMe function.
Just like you did, you define a function and parameters like you did.
..and when to use the function .toString() by putting a period after
the variable and typing out the function.
When you want to place your function on a object so that all instances of that object can you that object.
In most cases, espcially when you are starting out. You don't have to worry about these prototypes. The way you did.
function addMe(number) {
return number+1
}
const answer = addMe(1) //2
Is a standard way of defining a function and calling it.
I am fully aware of other very similar posts about object.create but I really cannot arrive to answer on this particular question.
I saw a code once where it looks like it was using object.create to create new prototype and then adding back to constructor
function blah(){
// typical constructor
}
blah.prototype = Object.create(somethingelse.prototype);
blah.prototype.constructor = blah;
Something like that .. and I tried that in chrome dev tool but did not see them as equal.. Now trying to see what that code I did see was trying to accomplish? I assume there is more difference in Object create and new(than just missing constructor?)
function blah(){
//...
}
undefined
function somethingelse(){
//
}
undefined
somethingelse.prototype = {
// something else
}
Object {}
blah.prototype = Object.create(somethingelse.prototype);
Object {}
blah.prototype.constructor = blah;
blah(){
//...
}
blah == somethingelse;
false
You've created two different objects (blah and something).
I think you are confused about what inheritance means.
when you compare (==) 2 objects the reference is being evaluated - and it is different.
Sometimes you think you know something, and then it turns out you really don't...
I've been doing a lot of research into the Javascript inheritance over the last few days for a talk I'm giving at a local tech group. No problem I figured, I know this stuff, I'll just brush up...
Turns out, I've lots still to learn. Here is the first thing that surprised me:
function Vehicle() {
function move() {
console.log('moving');
}
return {move: move}
}
function OtherVehicle() {
this.alsoMove = function() {
console.log('also moving')
}
}
The first method is what I've always understood the module pattern to be. The second, though I don't know it's name, I understood to be basically equivalent - just a minor syntactic variation.
Except:
(new Vehicle()) instanceof Vehicle \\ returns false
(new OtherVehicle()) instanceof OtherVehicle \\ returns true
new Vehicle() returns an 'Object'.
Looking at this, that makes sense, but it was not what I was expecting.
Can anyone explain what is going on here, why, and maybe point me at a really good resource that gets into the guts of this?
As a follow-up.
var Car = function() {
function drive() { console.log('driving') };
return {drive: drive}
}
Car.prototype = new Vehicle() //I know, Object.create preferred
(new Car).move() //undefined
// However...
var Car = function() {
this.drive = function () { console.log('driving') };
}
Car.prototype = new Vehicle();
(new Car()).move() //output 'moving'
So, the module pattern can't be used in inheritance?
Or have I just woken from the wrong side of the bed today and should go for a run before I next touch a keyboard?
I don't know of any particularly good resource, but here's some feedback anyway.
new Vehicle() returns an 'Object'.
The return {}, overrides the default return value this, which is the instantiated object. Calling such functions with new is pointless as this gets discarded. As far as OOP languages go, JavaScript is a bit odd in that it allows you to return another value for this in a constructor.
So yes, inheritance used that way won't work: {} itself is not in any way linked to a parent/base class, which is why
(new Car).move() //undefined
is undefined.
Similar to this question, I am following Douglas Crockford's JavaScript, The Good Parts. In chapter 4, he talks about augmenting types, which I find very confusing. He writes this sample code:
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Number.method('integer', function ( ) {
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
He then tests the new integer method:
document.writeln((-10 / 3).integer()); // -3
I don't understand the syntax of this.prototype[name] (in particular, the brackets) or Math[this < 0 ? 'ceiling' : 'floor'](this) (again, the brackets and also where Math came from). More importantly, can someone explain how the code in general works, and why the test code works?
By extending Function.prototype you add a method available to every function. Remember that functions in JavaScript are objects, and as such they can have properties and methods eg. call or apply.
The method function method lets you add a method to the prototype of any given function. Number is a function; the constructor of numbers, which has a prototype. Functions that you add to the prototype are available for all instances of the object, in this case a number.
Math is a built-in object in JavaScript, by using bracket notation you can dynamically access the property of an object. Remember that object keys are just strings, even if you don't write them as string, they can still be accessed with bracket notation with a string, for example:
var obj = {
key: 'hello'
};
var k = 'key';
obj[k]; //=> 'hello'
In the case of Math it's simply deciding if it should use ceil (not "ceiling") or floor based on a condition, you could write like this:
if (this < 0) {
Math.ceil(this);
} else {
Math.floor(this);
}
Javascript is weird.
In most languages you do foo = someObject.someMember and JS allows that too.
But it also lets you do foo = someObject["someMember"] which seems silly... until you realize that the string could be replaced with a variable
var memberName="someMember"
foo = someObject[memberName]
Now you're able to write very dynamic code that can leverage object members without knowing the names of those members when you write the code. This is what the code you posted is doing. It feels dirty at first but that feeling will pass ;)
Is it possible to call a method from an object using a string?
var elem = $('#test'); //<div id="test"></div>
var str = "attr('id')";
//This is what I'm trying to achieve
elem.attr('id'); //test
//What I've tried so far
elem.str; //undefined
elem.str(); //Object [object Object] has no method 'str'
var fn = eval(str); //attr is not defined
eval(elem.toString()+'.'+str); //Unexpected identifier
//Only solution I've found so far,
//but is not an option for me
//because this code is in a function
//so the element and method call
//get passed in and I wouldn't know
//what they are
eval($('#test').attr('id')); //test
UPDATE
This is my final, working answer:After running this code in the console
theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));
The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:
theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));
That's the safest, most reliable approach I can think of, really
What ever you do DON'T USE EVAL:
var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method
It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:
$('#foo').attr('id') === $('#foo')['attr']('id');
So just break the string apart, and use the method name like you would an object property and you're all set to go.
Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich
If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):
theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));
This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.
You should use one of these methods:
apply
var result = function.apply(thisArg[, argsArray]);
call
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);
Here is the sample:
var Sample = function() {
var that = this;
this.sampleMethod = function() {
return alert("Hello!");
};
this.sampleMethod2 = function(){
that["sampleMethod"].apply(that);
};
};
var objImpl = new Sample();
objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
Eval does what you want to do. Eval is evil, however, because you should not do what you want to do.
Why is using the JavaScript eval function a bad idea?