What's the difference between these two method of defining a 'class' in JavaScript?
Method One
Define method within the constructor:
function MyClass()
{
this.foo = function() { console.log('hello world!'); };
}
Method Two
Define method on the prototype:
function MyClass()
{}
MyClass.prototype.foo = function() { console.log('hello world!'); };
The first will create a new function object on each instantiation of your object, the second will assign a reference to a prototype method to each instance. In short: the second is more efficient, because all instances will share a single function object.
That's just the logic of a prototype chain, you can try and access anything via any object:
var objLiteral = {foo:'bar'};
When accessing objLiteral.foo JS will first look at the properties that the object itself has defined, and return the value if it is found. If JS can't find the property on the object itself, it'll check the object's prototype, hence:
objLiteral.valueOf();//method defined #Object.prototype
objLiteral.valueOf === Object.prototype.valueOf //true
But when you use your first method:
function SomeConstructor()
{
this.methd = function()
{
return true;
}
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd === g.methd;//FALSE!
That shows that we're dealing with 2 separate function objects. Move the function definition to the prototype and f.methd === g.methd; will be true:
function SomeConstructor()
{
}
SomeConstructor.prototype.methd = function()
{
return true;
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd === g.methd;//true!
In response to your comment:
Defining a method on a prototype-level allows you to change a method for a specific task, and then "reset" it back to it's default behaviour. Suppose you're in a function that's creating an AJAX request:
someObject.toString = function(){ return JSON.stringify(this);}
//when concatinating this object you'll get its json string
//do a lot of stuff
delete (someObject.toString);
Again JS will check if the object has the toString property defined on itself, which it has. So JS will delete the function you've assigned to the toString property. Next time the toString will be invoked, JS will start scanning the prototype chain all over again, and use the first occurance of the method (in the prototype). Let's clarify:
function SomeConstructor()
{
}
SomeConstructor.prototype.methd = function()
{
return true;
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd = function(){return false;};
g.methd();//returns true, still <-- method is gotten from the prototype
f.methd();//returns false <-- method is defined # instance level
delete (f.methd);
f.methd();//returns true, f doesn't have the method, but the prototype still does, so JS uses that.
Or even better, you can even replace an instance's method by a method from another prototype:
f.methd = Object.prototype.valueOf;//for as long as you need
the last example is pointless, because f has the valueOf method already: its inheritance chain looks like this: var f ---> SomeConstructor ---> Object, giving you access to all Object.prototype methods, too! Neat, isn't it?
These are just dummy examples, but I hope you see this is one of those features that make JS an incredibly flexible (sometimes too flexible, I must admit) and expressive language.
In first case the function will be created for each instance and set to the foo property in the object. In second case it is shared function. When you call obj.prop then it looks for it in object itself, if it is not there, then it looks for it in proto object and so on, it is called chain of prototypes.
For example this code provides foo:
function MyClass() {
this.foo = function () {};
}
var myVariable = new MyClass();
for (var i in myVariable) if (myVariable.hasOwnProperty(i)) console.log(i);
But this not:
function MyClass() {
}
MyClass.prototype.foo = function () {};
var myVariable = new MyClass();
for (var i in myVariable) if (myVariable.hasOwnProperty(i)) console.log(i);
Related
With respect to JS, what's the difference between the two? I know methods are associated with objects, but am confused what's the purpose of functions? How does the syntax of each of them differ?
Also, what's the difference between these 2 syntax'es:
var myFirstFunc = function(param) {
//Do something
};
and
function myFirstFunc(param) {
//Do something
};
Also, I saw somewhere that we need to do something like this before using a function:
obj.myFirstFunc = myFirstFunc;
obj.myFirstFunc("param");
Why is the first line required, and what does it do?
Sorry if these are basic questions, but I'm starting with JS and am confused.
EDIT: For the last bit of code, this is what I'm talking about:
// here we define our method using "this", before we even introduce bob
var setAge = function (newAge) {
this.age = newAge;
};
// now we make bob
var bob = new Object();
bob.age = 30;
// and down here we just use the method we already made
bob.setAge = setAge;
To answer your title question as to what is the difference between a 'function' and a 'method'.
It's semantics and has to do with what you are trying to express.
In JavaScript every function is an object. An object is a collection of key:value pairs. If a value is a primitive (number, string, boolean), or another object, the value is considered a property. If a value is a function, it is called a 'method'.
Within the scope of an object, a function is referred to as a method of that object. It is invoked from the object namespace MyObj.theMethod(). Since we said that a function is an object, a function within a function can be considered a method of that function.
You could say things like “I am going to use the save method of my object.” And "This save method accepts a function as a parameter.” But you generally wouldn't say that a function accepts a method as a parameter.
Btw, the book JavaScript Patterns by Stoyan Stefanov covers your questions in detail, and I highly recommend it if you really want to understand the language. Here's a quote from the book on this subject:
So it could happen that a function A, being an object, has properties and methods, one of which happens to be another function B. Then B can accept a function C as an argument and, when executed, can return another function D.
There is a slight difference -
Method : Method is a function when object is associated with it.
var obj = {
name : "John snow",
work : function someFun(paramA, paramB) {
// some code..
}
Function : When no object is associated with it , it comes to function.
function fun(param1, param2){
// some code...
}
Many answers are saying something along the lines that a method is what a function is called when it is defined on an object.
While this is often true in the way the word is used when people talk about JavaScript or object oriented programming in general (see here), it is worth noting that in ES6 the term method has taken on a very specific meaning (see section 14.3 Method Definitions of the specs).
Method Definitions
A method (in the strict sense) is a function that was defined through the concise method syntax in an object literal or as a class method in a class declaration / expression:
// In object literals:
const obj = {
method() {}
};
// In class declarations:
class MyClass {
method() {}
}
Method Specificities
This answer gives a good overview about the specificities of methods (in the strict sense), namely:
methods get assigned an internal [[HomeObject]] property which allows them to use super.
methods are not created with a prototype property and they don't have an internal [[Construct]] method which means that they cannot be called with new.
the name of a method does not become a binding in the method's scope.
Below are some examples illustrating how methods (in the strict sense) differ from functions defined on objects through function expressions:
Example 1
const obj = {
method() {
super.test; // All good!
},
ordinaryFunction: function ordinaryFunction() {
super.test; // SyntaxError: 'super' keyword unexpected here
}
};
Example 2
const obj = {
method() {},
ordinaryFunction: function ordinaryFunction() {}
};
console.log( obj.ordinaryFunction.hasOwnProperty( 'prototype' ) ); // true
console.log( obj.method.hasOwnProperty( 'prototype' ) ); // false
new obj.ordinaryFunction(); // All good !
new obj.method(); // TypeError: obj.method is not a constructor
Example 3
const obj = {
method() {
console.log( method );
},
ordinaryFunction: function ordinaryFunction() {
console.log( ordinaryFunction );
}
};
obj.ordinaryFunction() // All good!
obj.method() // ReferenceError: method is not defined
A method is a property of an object whose value is a function. Methods are called on objects in the following format: object.method().
//this is an object named developer
const developer = {
name: 'Andrew',
sayHello: function () {
console.log('Hi there!');
},
favoriteLanguage: function (language) {
console.log(`My favorite programming language is ${language}`);
}
};
// favoriteLanguage: and sayHello: and name: all of them are proprieties in the object named developer
now lets say you needed to call favoriteLanguage propriety witch is a function inside the object..
you call it this way
developer.favoriteLanguage('JavaScript');
// My favorite programming language is JavaScript'
so what we name this: developer.favoriteLanguage('JavaScript');
its not a function its not an object? what it is? its a method
Your first line, is creating an object that references a function. You would reference it like this:
myFirstFunc(param);
But you can pass it to another function since it will return the function like so:
function mySecondFunction(func_param){}
mySecondFunction(myFirstFunc);
The second line just creates a function called myFirstFunc which would be referenced like this:
myFirstFunc(param);
And is limited in scope depending on where it is declared, if it is declared outside of any other function it belongs to the global scope. However you can declare a function inside another function. The scope of that function is then limited to the function its declared inside of.
function functionOne(){
function functionTwo(){}; //only accessed via the functionOne scope!
}
Your final examples are creating instances of functions that are then referenced though an object parameter. So this:
function myFirstFunc(param){};
obj.myFirst = myFirstFunc(); //not right!
obj.myFirst = new myFirstFunc(); //right!
obj.myFirst('something here'); //now calling the function
Says that you have an object that references an instance of a function. The key here is that if the function changes the reference you stored in obj.myFirst will not be changed.
While #kevin is basically right there is only functions in JS you can create functions that are much more like methods then functions, take this for example:
function player(){
this.stats = {
health: 0,
mana: 0,
get : function(){
return this;
},
set : function( stats ){
this.health = stats.health;
this.mana = stats.mana;
}
}
You could then call player.stats.get() and it would return to you the value of heath, and mana. So I would consider get and set in this instance to be methods of the player.stats object.
A function executes a list of statements example:
function add() {
var a = 2;
var b = 3;
var c = a + b;
return c;
}
1) A method is a function that is applied to an object example:
var message = "Hello world!";
var x = message.toUpperCase(); // .toUpperCase() is a built in function
2) Creating a method using an object constructor. Once the method belongs to the object you can apply it to that object. example:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.name = function() {return this.firstName + " " + this.lastName;};
}
document.getElementById("demo").innerHTML = person.fullName(); // using the
method
Definition of a method: A method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object.
var myFirstFunc = function(param) {
//Do something
};
and
function myFirstFunc(param) {
//Do something
};
are (almost) identical. The second is (usually) just shorthand. However, as this jsfiddle (http://jsfiddle.net/cu2Sy/) shows, function myFirstFunc will cause the function to be defined as soon as the enclosing scope is entered, whereas myFirstFunc = function will only create it once execution reaches that line.
As for methods, they have a this argument, which is the current object, so:
var obj = {};
obj.func = function( ) {
// here, "this" is obj
this.test = 2;
}
console.log( obj.test ); // undefined
obj.func( );
console.log( obj.test ); // 2
The exact syntax you showed is because you can also do this:
function abc( ) {
this.test = 2;
}
var obj = {};
obj.func = abc;
obj.func( ); // sets obj.test to 2
but you shouldn't without good reason.
ecma document
4.3.31method :
function that is the value of a property
NOTE When a function is called as a method of an object, the object is
passed to the function as its this value.
It is very clear: when you call a function if it implicitly has a this (to point an object) and if you can't call the function without an object, the function deserves to name as method.
OK! First of all this question comes from a man who digs too deep (and posibly get lost) in the jQuery universe.
In my reserch I discovered the jquery's main pattern is something like this (If needed correction is wellcomed):
(function (window, undefined) {
jQuery = function (arg) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init(arg);
},
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function (selector, context, rootjQuery) {
// get the selected DOM el.
// and returns an array
},
method: function () {
doSomeThing();
return this;
},
method2: function () {
doSomeThing();
return this;,
method3: function () {
doSomeThing();
return this;
};
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function () {
//defines the extend method
};
// extends the jQuery function and adds some static methods
jQuery.extend({
method: function () {}
})
})
When $ initiates the jQuery.prototype.init initiates and returns an array of elements. But i could not understand how it adds the jQuery method like .css or .hide ,etc. to this array.
I get the static methods. But could not get how it returns and array of elements with all those methods.
I don't like that pattern either. They have an init function, which is the constructor of all jQuery instances - the jQuery function itself is just a wrapper around that object creation with new:
function jQuery(…) { return new init(…); }
Then, they add the methods of those instances to the init.prototype object. This object is exposed as an interface at jQuery.fn. Also, they set the prototype property of the jQuery function to that object - for those who don't use the fn property. Now you have
jQuery.prototype = jQuery.fn = […]init.prototype
But they also do two [weird] things:
overwriting the constructor property of the prototype object, setting it to the jQuery function
exposing the init function on jQuery.fn - its own prototype. This might allow Extending $.fn.init function, but is very confusing
I think they need/want to do all this to be fool-proof, but their code is a mess - starting with that object literal and assigning the init prototype things afterwards.
It’s easier to digest if you think of the API as an external collection of methods, and the jQuery function as the wrapper.
It’s basically constructed like this:
function a() { return new b();}
a.prototype.method = function() { return this; }
function b() {}
b.prototype = a.prototype;
Except that a is jQuery and b is jQuery.prototype.init.
I’m sure Resig had his reasons for placing the api constructor in the init prototype, but I can’t see them. A couple of more strangeness besides the ones Bergi mentioned:
1) The patterns requires a reference copy from jQuery.fn.init.prototype to jQuery.prototype, wich allows a weird endless loop:
var $body = new $.fn.init.prototype.init.prototype.init.prototype.init('body');
2) Every jQuery collection is actually an instance of jQuery.fn.init, but since they reference the same prototype object, it tricks us to "think" that the collection is an instance of jQuery. You can do the same sorcery like this:
function a(){}
function b(){}
a.prototype = b.prototype;
console.log( new b instanceof a); // true
console.log( new a instanceof b); // true
Sidenote: I have personally used the following constructor pattern with similar results without the weirdness:
var a = function(arg) {
if (!(this instanceof a)) {
return new a(arg);
}
};
a.prototype.method = function(){ return this; };
I've recently started reading up on OOP javascript and one thing that authors seem to skip over is when an object A has been declared and suddenly I see "A.prototype.constructor =A;
For example,
var A = function(){}; // This is the constructor of "A"
A.prototype.constructor = A;
A.prototype.value = 1;
A.prototype.test = function() { alert(this.value); }
var a = new A(); // create an instance of A
alert(a.value); // => 1
So I run the command in firebug "var A = function(){};"
and then "A.Constructor" Which reveals it's a function. I understand this.
I run the code "A.prototype.constructor = A;" and I thought this changes the A constructor from Function to A.
The constructor property of A has been changed right? Instead when I run "A.constructor" it gives me function () still.
What's the point?
I also see A.constructor.prototype.constructor.prototype.. what is going on?
If A inherit B using A.prototype = new B();, you need to reset the constructor property for the class A using A.prototype.constructor=A;, otherwise instances of A would have a constructor of B.
In your case, A.prototype.constructor === A will return true, so A.prototype.constructor = A did nothing.
You can quickly test out that that additional assignment does absolutely nothing:
var A = function() {};
A.prototype.constructor === A; // true -- why assign then?
Resetting the constructor property only makes sense if you've assigned a new prototype object to the class, overwriting the original constructor:
var A = function() {};
A.prototype = protoObject; // some object with members that you'd like to inherit
A.prototype.constructor = A; // reset constructor
In your case, the author might be blindly doing this as good practice, even in cases where it's not necessary.
This code if often use in JS classic inheritance pattern (the code is from JavaScript Patterns by Stoyan Stefanov):
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
C.prototype.constructor = C;
}
to assign right constructor to the child class.
In your case it did nothing, since A.prototype.constructor === A before assignment.
You might want to see my answer to similar question:
https://stackoverflow.com/a/19616652/207661
TL;DR: constructor is not an own property of an instance. So to make things look consistent JavaScript interpreter needs to set prototype.constructor to function itself. This feature can be used in functions that operates generically on many different types of objects.
According to MDN, All objects inherit a constructor property from their prototype:
Example 1:
var o = {};
o.constructor === Object; // true
..
Example2:
function Tree() {
}
var theTree = new Tree();
console.log(theTree.constructor === Tree ); // true
At runtime, it does not make any difference based on the value of the constructor property.
However, as the constructor property returns a reference to the Object function that created the instance's prototype, one should reset the constructor property when they assign a new prototype to the Object function.
var Forest = function() {};
Forest.prototype = theTree;
console.log(new Forest().constructor === Tree ); // true
Forest.prototype.constructor = Forest;
console.log(new Forest().constructor === Forest ); // true
https://jsfiddle.net/j1ub9sap/
For details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor
How can this object be rewritten so you don't need to declare it with "new"?
var Lang = new function(){
this.get = function(str, trans){
if(TRANSLATE[str]){
var str = TRANSLATE[str][LANG];
if(count_obj(trans) > 0){
for(var key in trans){
str = str.replace('%'+key+'%', trans[key]);
}
}
}
return str;
};
};
To something like this:
var Lang = {
get : function(){}
};
You wrote the solution to your own question in the question...this is a perfectly valid way to create an object in javascript:
var Lang = {
get: function(str, trans){
if(TRANSLATE[str]){
var str = TRANSLATE[str][LANG];
if(count_obj(trans) > 0){
for(var key in trans){
str = str.replace('%'+key+'%', trans[key]);
}
}
}
return str;
};
};
If you want private variables, the usual way to do that is to create a function with local variables that return the object with those variables encapsulated in a closure like so:
var Lang = (function() {
var p1; // private variable
var p2; // another private variable
return {
getP1: function () {
return p1;
},
setP1: function(value) {
p1 = value;
}
};
})();
Note that the function that creates the object you want is executed right away and returns the object with two private variables captured in the closure. Those variables will not be accessible from outside the object.
Patterns for Enforcing new
As mentioned already, constructors are still just functions but invoked with new.
What
happens if you forget new when you invoke a constructor? This is not going to cause
syntax or runtime errors but might lead to logical errors and unexpected behavior.
That’s because when you forget new, this inside the constructor will point to the global
object. (In browsers this will point to window.)
When your constructor has something like this.member and you invoke the constructor
without new, you’re actually creating a new property of the global object called
member and accessible through window.member or simply member. This behavior is highly
undesirable, because you know you should always strive for keeping the global namespace
clean.
// constructor
function Waffle() {
this.tastes = "yummy";
}
// a new object
var good_morning = new Waffle();
console.log(typeof good_morning); // "object"
console.log(good_morning.tastes); // "yummy"
// antipattern:
// forgotten `new`
var good_morning = Waffle();
console.log(typeof good_morning); // "undefined"
console.log(window.tastes); // "yummy"
Self-Invoking Constructor
To address the drawback of the previous pattern and have prototype properties available
to the instance objects, consider the following approach. In the constructor you
check whether this is an instance of your constructor, and if not, the constructor invokes
itself again, this time properly with new:
function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = "yummy";
}
Waffle.prototype.wantAnother = true;
// testing invocations
var first = new Waffle(),
second = Waffle();
console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"
console.log(first.wantAnother); // true
console.log(second.wantAnother); // true
Another general-purpose way to check the instance is to compare with
arguments.callee instead of hard-coding the constructor name.
if (!(this instanceof arguments.callee)) {
return new arguments.callee();
}
This pattern uses the fact that inside every function, an object called arguments is created
containing all parameters passed to the function when it was invoked. And arguments
has a property called callee, which points back to the function that was called. Be
aware that arguments.callee is not allowed in ES5’s strict mode, so it’s best if you limit
its future use and also remove any instances should you find them in existing code.
“JavaScript Patterns, by Stoyan Stefanov (O’Reilly). Copyright 2010 Yahoo!, Inc., 9780596806750.”
just leave the "new" out, rest is the same. :)
var Lang = function(){
...
}
Edit: example copy-paste from firebug, "dsa" is just an object, "dsaFunc" is a function, but you can do the same with them:
>>> var dsa = {};
undefined
>>> dsa.get = function(a){ return a+1;}
function()
>>> dsa.get(2)
3
>>> var dsaFunc = function(){};
undefined
>>> dsaFunc.get = function(a){ return a+1;}
function()
>>> dsaFunc.get(2)
3
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(this._instance);
}
} (); //note that it is self invoking function
var l = new Dog(); //#> undefined
In the above case I was expecting an output of:
'hello world'
Why is this._instance not accessing the the variable which should be accessible by virtue of closure? I tested this in FF and am getting undefined.
You don't assign _instance to the object, it's just a closure variable, and should be accessed without using this:
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(_instance);
}
} (); //note that it is self invoking function
var l = new Dog();
I'd probably write it like so instead:
var Dog = (function() {
var defaults = {
name: 'Rags'
};
var Dog = function (options) {
// Take options as a constructor argument, this
// allows you to merge it with defaults to override
// options on specific instances
this.setOptions(options);
};
Dog.prototype = {
// Common methods for Dogs here
setOptions: function (options) {
// Declare all variables in the beginning of the method because
// JavaScript hoists variable declarations
var key = null;
// First assign all the defaults to this object
for ( key in defaults) {
this[key] = defaults[key];
}
// Now override with the values in options:
if (options && options.hasOwnProperty) {
for ( key in options ) {
this[key] = options[key];
}
}
}
};
return Dog; // Return the constructor method
} ()); // wrap the self-invoked function in paranthesis to visualize that
// it creates a closure
var buster = new Dog({name: 'Buster'}),
unnamed = new Dog();
alert(buster.name); // Alerts 'Buster'
alert(unnamed.name); // Alerts 'Rags'
Note that I have not tried to compile the above code, so it might contain a few mistakes. Nothing JsLint can't handle though!
You might want to consider adding filtering to the setOptions method so that it doesn't assign properties you don't want, or filter out methods etc declared in the options-parameter.
Additionally, if you use JQuery, or similar library, there are (often) utility functions for merging objects, making it trivial to write the setOptions-method:
function setOptions (options) {
// I assume JQuery here
// true as the first argument gives us a recursive merge
var mergedOptions = $.extend(true, defaults, options);
for (var key in mergedOptions ) {
if(this.checkAllowedProperty(key, typeof(mergedOptions[key])) {
this[key] = mergedOptions[key];
}
}
}
/**
* This method checks if propertyName is an allowed property on this object.
* If dataType is supplied it also checks if propertyName is allowed for
* dataType
* #return true if propertyName, with type dataType, is allowed on this object,
* else false
*/
function checkAllowedProperty (propertyName, dataType);
Your problem is this.
Change this._instance to _instance. You may also want to wrap your self-invoking function in parentheses like (function() { ... })(); for maximum browser compatibility.
As the others have said, you need to remove "this." from your function.
The reason for the problem is down to the binding of the "this" keyword in the two function contexts. Inside the closure, "this" refers to the function that is being returned, and not to the outer function. You could resolve this by doing the following:
var Dog = function() {
var _instance = 'hello world';
var that = this; //Assign "this" to "that"
return function() {
console.log(that._instance); //Use reference to "that"
}
} ();
var l = new Dog();
You could also probably do something closer with the function.apply() method, but I'll leave that to you.
I hope that helps.
Perhaps you are satisfied by removing "this.", but you may be interested to learn that "this" doesn't refer to what you wanted it to anyway. What it refers to really depends on how the function is called. It does not necessarily refer to an instance of an object constructed by the function you returned, or its container function, or to any other object. By default, if you merely call the function as a normal function, "this" will refer to the global window context.
What you must do to have "this" be bound to any specific object is to call the function as a method of that object, or of its prototype. e.g. foo.myMethod(). Another way is that you can use the apply or call method, passing in the object you want it to apply to. e.g. anyFunction.apply(foo).