Consider this:
var Foo = function Foo () {
var numberVar = 0;
fooPrototype = {
getNumberVar: function () {
return numberVar;
},
setNumberVar: function (val) {
this.numberVar = val;
}
}
return fooPrototype;
};
var foo = new Foo();
Alternatively, look at this:
var Bar = function Bar() {
var numberVar = 0;
};
Bar.prototype = {
getNumber: function () {
return this.numberVar;
},
setNumber: function (val) {
this.numberVar = val;
}
};
var bar = new Bar();
They both do the same thing, in that they allow for public / private members. Is there any benefit to doing this one way or the other?
Your logic here is based on a faulty assumption. In your second implementation, the constructor variable numberVar is never used. You have no code that can reach it and thus you are not using a private variable in the second code block.
Your methods in that second implementation are accessing an object property named numberVar which is publicly accessible as a property on the object which is different than the local variable of the same name in your constructor. You cannot have private variables in your second implementation because your prototype-declared methods are not declared in a private scope.
Here's what happens in your second code block:
var Bar = function Bar() {
// this variable is never used as there is no code in this scope
// that can reach this variable. In fact, it is garbage collected
// immediately
var numberVar = 0;
};
Bar.prototype = {
getNumber: function () {
return this.numberVar;
},
setNumber: function (val) {
// sets a public property on the object
this.numberVar = val;
}
};
var bar = new Bar();
bar.setNumber(10);
console.log(bar.numberVar); // 10, this property is public
For a general discussion of methods declared in the constructor vs. prototype-defined methods, see this discussion:
Advantages of using prototype, vs defining methods straight in the constructor?
Related
I can do this in JavaScript:
var output = String(result);
And I can do this with the same object that is referenced using String:
var character = String.fromCharCode(10);
String can be used as a function to construct an object and members can be called on it without using it as a constructor. How do I make an object usable in both these ways? What is this called?
You are talking about class methods.
function Foo() {
this.bar = 3
}
Foo.baz = function() {
console.log('hi');
}
or in ES 2015
class Foo {
static baz () {
console.log('hi');
}
}
Response to the comments
You can define a static method in the constructor function because the constructor function is necessarily in scope:
function Foo() {
Foo.method = function () {
// do stuff
}
}
There are a couple of problems with this approach though:
Foo.method('stringy string'); // TypeError: cannot read property 'method' of undefined
Because the static method is defined in the constructor, it won't be there until the constructor function runs at least once:
const foo = new Foo();
Foo.method('stringy string'); // now we're good
which leads to another problem, now we're wastefully reassigning that method every time the constructor runs. You can avoid that with a conditional check:
function Foo() {
if (!Foo.method) Foo.method = function....
}
But that's a lot of weird stuff just to avoid defining the class method after the constructor, and it still doesn't solve the first problem.
You can make a class with static methods:
class Foo {
constructor(bar) {
this.bar = bar;
}
length() {
return this.bar.length;
}
static fromThing(thing) {
return new Foo(thing.bar);
}
}
Foo.fromThing() is analogous to String.fromCharCode()
Simply
function MyClass () {
this.val = 1;
}
MyClass.staticMethod = function () {/* code here */};
I've this parser class in a file:
module.exports = function Parser() {
this.myVar = "";
var parse = function (inputString) {
this.myVar = "somethingAfterParse";
}
return {
initWithString: function(inputString) {
parse(inputString)
},
name: this.myVar
};
};
Then, say in "main" file js, I use parser in this way:
var Parser = require("./Parser.js");
var parserInstance = new Parser();
parserInstance.initWithString("somestring");
console.log("Parser var:", parserInstance.myVar);
No errors, but log print an empty name.. why?
Here's what I would do:
function Parser() {
this.myVar = "";
}
Parser.prototype.initWithString = function (inputString) {
this.myVar = "somethingAfterParse";
};
module.exports = Parser;
your approach, annotated
module.exports = function Parser() {
// public property "myVar" of any new Parser object
this.myVar = "";
// anonymous function assigned to private variable "parse"
var parse = function (inputString) {
// "this" will likely be the global object here, dangling error
this.myVar = "somethingAfterParse";
}
// return API object - unnecessary and makes debugging harder (*)
return {
// anonymous function that calls private "parse" function
initWithString: function(inputString) {
// calling parse without defining "this", definitely an error (**)
parse(inputString)
},
// the above is equivalent to and therefore better written as
// initWithString: parse,
// copy of (!) public property myVar, definitely an error (***)
name: this.myVar
};
};
When you leave out the redundant bits (and make use of the prototype chain) you end up with what I suggest above.
* I know this is a popular approach for defining the public interface of an object. Personally I don't like it very much. Returning an object from a JS constructor function effectively breaks the usefulness of the new operator:
You make inaccessible for the object's user everything that was assigned to this before inside the constructor, like your this.myVar.
You get a bag of properties with no type information. Technically this works, but it's not nice during debugging. Compare:
function A() {
this.a = "A";
return {
a: "a"
};
}
function B() {
this.b = "B";
}
new A(); // Object {a: "a"}
new B(); // B {b: "B"}
** this inside a function will refer to whatever object that function was called on (the context).
Usually dot notation does that for you: object.method() sets this to object inside method.
You however do not call the parse() function on any object. Any function called without context runs in the context of the global object (in browsers that's Window).
You would have needed to specify the context by using var self = this; earlier and parse.apply(self, arguments) inside the initWithString body.
In the end the whole "let's return an object as the API" approach makes things more difficult than they need to be.
*** Primitive types are always assigned by value in JavaScript. Just like with numbers or Booleans, you cannot reference strings. Assigning them to other variables makes copies of them.
you have to use .call or .apply
module.exports = function Parser() {
this.name = "";
var parse = function(inputString) {
this.name = "somethingAfterParse";
}
return {
initWithString: function(inputString) {
parse.call(this, inputString)
},
name: this.name
};
};
Instead of returning an anonymous object you can return a Parser type.
module.exports = function Parser() {
var self = this;
self.name = "";
self.parse = function (inputString) {
self.name = inputString + " parsed";
}
return self;
};
Then, say in "main" file js, I use parser in this way:
var Parser = require("./Parser.js");
var parserInstance = new Parser();
parserInstance.parse("something");
console.log("Parser name:", parserInstance.name); // Parser name: something parsed
Let's say I have the following code, which I CANNOT modify
var namespace = {};
function() {
var MyConstructorFunction = function() {
alert("default behavior");
};
namespace.MyConstructorFunction = MyConstructorFunction;
setTimeout(function() {
var instance = new MyConstructorFunction();
}, 1000)
}();
I would like to externally add some code in the global scope from which I only have access to namespace for making instance to be constructed with alert("custom behavior");
Just to clarify my intentions, let's say I could think of these two aproaches:
namespace.MyConstructorFunction = function() {
alert("custom behavior");
};
or
namespace.MyConstructorFunction.prototype.constructor = function() {
alert("custom behavior");
};
But obviously they don't work. Is there any way to do this?
You can use the prototype chain to override methods in a namespace.
// create an object that inherits from namespace
var o = Object.create(namespace);
// Override the MyConstructorFunction property
o.MyConstructorFunction = function () {
alert("custom behavior");
}
And you can reuse the namespace token
namespace = o;
Or you can use a different namespace if you would prefer.
Object.create is an ES5 feature which cannot be emulated entirely in ES3, but in this use case it should work with a basic polyfill.
But I understand you want to call a different constructor from the setTimeout, which in this example is impossible. The function references a local variable which cannot be altered. While you can change the global behaviour of an object like in this example, you cannot alter the variables inside a closure except through functions that can see those variables. If the function were to reference the globally scoped variable and not the locally scoped variable you would be in luck.
i.e:
var namespace = {};
function() {
var MyConstructorFunction = function() {
alert("default behavior");
};
namespace.MyConstructorFunction = MyConstructorFunction;
setTimeout(function() {
var instance = new namespace.MyConstructorFunction(); // reference global
}, 1000)
}();
If it is an instance, you can't override constructor. And in a real application, you don't want to, for security reasons.
But you can override or add specific methods:
var F = function() {
this.foo = 'bar';
}
var f = new F();
typeof f.foo; // "string"
f.foo = function() { return 'Bar' };
typeof f.foo; // "function"
I always have difficulty grasping new concepts without seeing a real, basic, working example of what I am reading about. While I like the other explanation on stackoverflow, I'd really like to see a very basic example showing the difference between methods and functions in JavaScript that I can quickly run to learn more.
A method is just a function that is a property of an object. It's not a different type of object in javascript, but rather method is just the descriptive name given to a function that is defined as a property of an object.
var myObj = {};
myObj.go = function() {alert("hi");}
myObj.go();
In this example, go is a method on the myObj object.
When a method is called as in the above example myObj.go(), then the value of the this pointer is set to the object that was involved in the invocation of the method (in this case myObj).
Since global functions are also implicitly properties on the window object, one could say that global functions are also methods on the window object, but you do not need the window designation in order to call them.
Local functions like inner() in this function are just functions and not methods as they are not attached to a particular object:
function main() {
function inner() {
alert("hi");
}
inner();
}
This is a function and a function call:
function myFunction(){
alert("This is a function!");
}
myFunction();
This, on the other end, is a method call, because it is a member function of an object.
message.toUpperCase();
Here's the full code to create a class/methods and a call:
function Circle(x,y,r) {
this.xcoord = x;
this.ycoord = y;
this.radius = r;
}
Circle.prototype.retArea = function () {
return ( Math.PI * this.radius * this.radius );
};
var aCircle = new Circle(1,2,3);
var a = aCircle.retArea();
example:
function:
var f1 = function fBase() { ... }
function f2() { ... }
var f3 = function() { ... }
f1()
f2()
f3()
method:
var c = function oBase() {
this.method1 = function() { ... };
}
c.prototype.method2 = function() { ... }
var o = new c()
o.method1()
o.method2()
method json:
var o = { method1: function() { ... } }
o.method2 = function() { ... }
o.method1()
o.method2()
A function is a type which can be used to define a piece of code that can be executed by using call ("()") operator and may return data to the caller.
e.g.
define
function sayHello(){
return "Hello";
}
use
var result = sayHello();
Now, result will contian "Hello".
A method is a function that is defined inside an object and accessible through a property. For example, slice is function defined over all string instances
e.g.
define
var obj = {
sayHello : function(){
return "Hello";
}
};
you can also define methods outside the object definition
var obj = {};
obj.sayHello = function(){
return "Hello";
};
use
var result = obj.sayHello();
We use methods in object oriented programming.
Refer:
Functions at MDN
Objects at MDN
In javascript, what is the difference between:
var singleton = function(){ ... }
and
var singleton = new function(){ ... }
?
Declaring priviliged functions as described by crockford (http://www.crockford.com/javascript/private.html) only works using the latter.
The difference is mainly that in your second example, you are using the Function Expression as a Constructor, the new operator will cause the function to be automatically executed, and the this value inside that function will refer to a newly created object.
If you don't return anything (or you don't return a non-primitive value) from that function, the this value will be returned and assigned to your singleton variable.
Privileged methods can also be used in your second example, a common pattern is use an immediately invoked function expression, creating a closure where the private members are accessible, then you can return an object that contains your public API, e.g.:
var singleton = (function () {
var privateVar = 1;
function privateMethod () {/*...*/}
return { // public API
publicMethod: function () {
// private members are available here
}
};
})();
I think that a privileged function as described by crockford would look like this:
function Test() {
this.privileged = function() {
alert('apple');
}
function anonymous() {
alert('hello');
}
anonymous();
}
t = new Test; // hello
t.privileged(); // apple
// unlike the anonymous function, the privileged function can be accessed and
// modified after its declaration
t.privileged = function() {
alert('orange');
}
t.privileged(); // orange