Adding methods to custom objects in Javascript [duplicate] - javascript

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Use of 'prototype' vs. 'this' in Javascript?
I went to various websites but not able to understand the difference between the following ways of adding methods to custom objects:
Method 1:
function circle(radius){
this.radius = radius;
this.area = function(){ return 3.14*this.radius*this.radius;}
}
Method 2:
function circle(radius){
this.radius = radius;
}
circle.prototype.area = function(){ return 3.14*this.radius*this.radius; }
Are there any performance or design issues that one of the methods has and the other does not?

Here is one way to see the difference:
var circle1 = circle(1);
var circle2 = circle(1);
alert(circle1.area == circle2.area);
For Method1 you will see false while Method2 yields in true. That's because in the first case you assign a new closure function to each object created, two objects end up with different area functions even though both do the same thing. In the second case the objects share the same prototype object with its area method, that method is of course identical in both cases.
Usually Method2 is preferable for several reasons:
Object creation is faster because only custom properties need to be initialized.
You don't waste memory on instantiating multiple copies of the same thing.
You can easily find out which of the object properties have custom values by calling Object.hasOwnProperty.
There are some disadvantages as well to keep in mind.
You always spend time on setting up the prototype even if you never create any objects.
Property access is slightly slower because the JavaScript engine needs to check object properties first and then the properties of the prototype object (modern JavaScript engines optimize this pretty well however).
There is a common fall trap of putting objects or arrays on the prototype, these will be shared between all object instances.
Here is an example of the common mistake:
function NumberCollection()
{
}
NumberCollection.prototype = {
numbers: [],
sum: function()
{
var result = 0;
for (var i = 0; i < this.numbers.length; i++)
result += this.numbers[i];
return result;
}
}
var c1 = new NumberCollection();
c1.numbers.push(5);
alert(c1.sum()); // Shows 5
var c2 = new NumberCollection();
c2.numbers.push(6);
alert(c2.sum()); // Oops, shows 11 because c1.numbers and c2.numbers is the same
The correct approach here would be:
function NumberCollection()
{
this.numbers = [];
}
NumberCollection.prototype = {
numbers: null,
sum: function()
{
var result = 0;
for (var i = 0; i < this.numbers.length; i++)
result += this.numbers[i];
return result;
}
}

The first method (let's call it type 1) adds the area() function to the object itself. Every time you construct a circle object with new, its body will be copied (!) to the new instance.
The second method (type 2) adds the area() function to the object prototype (the prototype is "one level up" in the hierarchy). Every time you create a new instance of circle only the radius property will be copied.
Now when you call area() on an instance of the second object, JavaScript cannot find that function on the object itself. Now it "goes up" the prototype chain and uses the first function of that name it finds.
Now this has several implications:
New objects instances will be smaller (less physical code)
You can change the implementation of area for all existing objects of type 2 at once and while they live (by changing the prototype), you can't do that for type 1
You can override the functionality of area in a single instance of type 2 while keeping the "original implementation" of the function around

Related

How to write JavaScript objects? [duplicate]

Is there any difference in how these functions operate? The first one is more typically of what I think about when thinking of a constructor.
Example 1: using this to name and set properties. Then using new to create a new Book object.
function Book(name, numPages) {
this.name = name;
this.numPages = numPages;
}
var myBook = new Book('A Good Book', '500 pages');
Example 2: returning a object by using new and just calling the function itself.
function Movie(name, numMinutes) {
return { name:name, numMinutes:numMinutes };
}
var best = new Movie('Forrest Gump', '150');
var other = Movie('Gladiator', '180');
I guess what I'm trying to figure out is if these are different in the way they create an object? If so is one better than the other? Are there different situations where one would work better over the other?
The first one is a constructor, and can therefore be extended by a prototype, and you can test via instanceof wether the result is an Instance of this type.
Downside: if you forget the new-keyword your code will blow up (unless you write a workaround for that into each constuctor)
And you can't really use apply() with a constructor to pass an array of arguments, when you instantiate a new Object; on the other hand, don't do that, even if you can/could.
The second one is a factory, not a constructor. Independant wether you use the new-keyword or not.
with this implementation it creates Objects that look the same but don't share a type or prototype (although the underlying JS-engine recognizes them as similar and so they share the same hidden Class as long as they have the same properties, added in the same order, ... different topic)
long story short, neither performance nor memory-footprint suffer from this approach (anymore)
But you can't check wether they are of the same type, and you don't have a shared prototype that may affect all instances (maybe a pro or a con.)
My goto-approach If I need inheritance, is kind of a mix of both:
(if I just need a data-object I usually use a factory and plain objects).
function Book(conf) {
var book = Object.create(Book.prototype);
//a book like this usually has multiple configs/properties
if(typeof conf === "object"){
for(var k in conf) book[k] = conf[k];
}else if(conf){
//assuming that I get at least the name passed
book.name = String(conf);
}
return book;
}
//I have a prototype that can be extended
//with default-values for example; no idea for a good method
//to add to the prototype in this example ;)
Book.prototype.numPages = 0;
//but I can also use it like a plain function; no error if you
var myBook1 = Book("Peter Pan");
var myBook2 = Book({
name: "American Gods",
author: "Neil Gaiman"
});
If I add the following line to the top of the function I can also use that as a method to cast anything into an Instance of Book without cloning already existing instances
function Book(conf) {
//with this simple line I can also use this as a function to cast anything into a "Book"
if(conf instanceof Book) return conf;
var book = Object.create(Book.prototype);
//...
return book;
}
var data = [
"Peter Pan",
{name: "American Gods"},
//...
];
var books = data.map(Book);
In my opinion, I have the benefits of both worlds with this approach.
Basically, when you use new, the JS engine makes a brand new object for you and injects that as the value of this. It also automatically gives you any methods attach to the prototype of the constructor. Using a constructor also allows you to check if an object is an instanceof something more easily.
function MovieA(title) {
this.title = title;
}
MovieA.prototype.getTitle = function() {
return this.title;
};
function MovieB(title) {
return {
title: title
};
}
MovieB.prototype.getTitle = function() {
return this.title;
};
var a = new MovieA('A');
console.log(a instanceof MovieA); // true
console.log(a.getTitle()); // 'A'
var b = MovieB('B');
console.log(b instanceof MovieB); // false
console.log(b.getTitle()); // Uncaught TypeError: b.getTitle is not a function
Everything that new offers you can be attained through other methods but requires more manual labor.
The second method, factories, tend to work better for unit testing, custom object creation and functional programming. It works better for unit testing because if you have a factory producing all of your objects, you can just replace that factory with a mock-up to test different cases.
var Factory = {
makeThing: function() {
return { name: 'thing' };
}
};
// Want to test the case for if makeThing fails inside of some other code
var MockFactory = {
makeThing: function() {
return null;
};
};
As for when you use either, it all depends. Some people don't use new at all. Others exclusively use new. It all depends on if you need any of the things listed above, how much control you need over the creation of objects, when you want to use this or not, etc. In the end, it's all a matter of preference.
The difference is the constructor used to create the object returned.
new Book('A Good Book', '500 pages');
creates a Book object instance, with the instance inheriting properties from Book.prototype, including a constructor property value of Book. The Book.prototype object itself inherits from Object.prototype.
var other = Movie('Gladiator', '180');
uses Movie as a factory function (new not required) and returns an Object object instance, with the instance inheriting properties directly fromObject.prototype, including a constructor property value of Object.
More briefly stated, Object literal syntax creates an Object object.

call some methods in one line

I build a objects source in JavaScript.
Is there any way to call some methods in one line like this:
var x = new object("aaa").method_a().method_b().method_c();
If you want to chain function call's you need to return this from your functions
function method_a(){
// do something
return this;
}
Same for other functions -
then you can do var x = new object("aaa").method_a().method_b().method_c();
The way to do that is making each method to return the object itself. For example:
function Person() {};
Person.prototype.setName=function(n){
this.name=n;
return this;
}
Person.prototype.setAge=function(a) {
this.age=a;
return this;
}
var p= new Person().setName("John").setAge(20);
The obvious gotcha is you cannot do that if the method has to return any other value (you can do it with setters but not with getters)
If your object doesn't support a fluid interface you can always wrap that functionality on top of it:
function FluidWrapper(obj)
{
var o = {};
for (var p in obj) {
if (typeof obj[p] == 'function') {
o[p] = function(method) {
return function() {
obj[method].apply(obj, [].slice.call(arguments, 0));
return o;
};
}(p);
}
}
return o;
}
var x = new object("aaa");
FluidWrapper(x).method_a().method_b().method_c();
Demo
While Mohammad Adil's answer is the most common scenario, i feel that the possibilities haven't been explored properly.
A function returns a value. In JavaScript you can call methods on any value except null and undefined. This means that this is perfectly acceptable:
var x = 987654321;
var y = x.toString().split('').sort().join('0');
In this scenario,
the toString() method was called on a Number who's internal value is 987654321 and returns a string.
the split('') method was called on a String who's internal value is '987654321' and returns an Array.
the sort() method was called on an Array holding the following values:['9','8','7','6','5','4','3','2','1'] and returns the same Array (but sorted).
The join('0') method was called on the same Array, but holding the values ['1','2','3','4','5','6','7','8','9'] and returns a string.
finally, after all these operations, y contains the value '10203040506070809';
So it is not necessary for the object the chained methods act on to be the same, as long as you are aware at every step of what that object is.
When you have a method called on an object, inside that method this will refer to the object. So if you return this;, then another method of that object can be called afterwards.
It is important to note that sometimes you want to return a new object of the same type rather than change the object and return it. Both work equally well when chaining, but the results are different when not. Consider the following jQuery example:
var divs = $('div'); // all divs on the page
var marked = divs.filter('.marked'); // all marked divs on the page
marked.css('color', 'red'); // make marked divs red
Because the filter method returns a new jQuery object, the initial divs variable still contains all the divs on the page. If the filter method were to eliminate things from the jQuery object it was called on and return it, then divs would point to the same object as marked and therefore would no longer have all divs on the page.
From a chaining perspective, nothing changes between the two potential implementations (except for some throw-away objects):
$('div').filter('.marked').css('color', 'red');

IE9 does not recognize prototype function?

I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.
I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.
If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.
Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.
How can I make this work in IE9 ?
More comment than answer
The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.
Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.
function getName(obj) {
return obj.name;
}
So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.
Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.
So if obj represents data for a person, you might do:
// Setup
function Person(obj){
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
this[p] = obj[p];
}
}
}
Person.prototype.getName = function(){
return this.name;
};
// Object generated from JSON
var dataFred = {name:'fred'};
// Create a new Person based on data
var p = new Person(dataFred);
You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.
This is how I solved it at the end:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
if (!isIE9()) {
obj.__proto__ = proto;
} else {
/** IE9 fix - copy object methods from the protype to the new object **/
for (var prop in proto) {
obj[prop] = proto[prop];
}
}
return obj;
};
var isIE9 = function() {
return navigator.appVersion.indexOf("MSIE 9") > 0;
};

JavaScript OOP: Objects change their prototype (for all other objects using the same prototype) [duplicate]

This question already has answers here:
What does "the prototype belongs to the class not the instance" mean in javascript?
(3 answers)
Closed 9 years ago.
function NamedRoundedBlock(){
var name = this.makeFeild("name");
name.className = "Block NamedRound name";
this.element.className = "Block NamedRound root";
this.element.appendChild(name);
}
NamedRoundedBlock.prototype = new Block();
NamedRoundedBlock.prototype.constructor = NamedRoundedBlock;
There's the code, anyone know what I've done wrong?
My problem is that if I make two NamedRoundedBlock objects and change the this.element property of one (by changing an attribute or something) the other's will change too.
Also, a little additional detail, this.makeFeild and this.element are both set in Block
The prototype is the place for shared properties.
Consider:
var protoCircle - {x: 0, y: 0, radius: 1}
This means every circle will have these properties as defaults. So if I make circle with that prototype:
var c1 = Object.create(protoCircle);
c1.x = 3;
var c2 = Object.create(protoCircle);
c2.y = 5;
then circle c1 is at (3,0) with radius 1 and c2 is at (0,5) with radius 1. That's because c1 has only one own property (x) and two inherited properties (y and radius) which it picks up from its prototype. If I change protoCircle.radius then that change will be seen by the two circles. That is the way JavaScript is designed! This design allows a whole bunch of objects to share a default value so you don't have to store it in each object. You just put the properties unique to each object inside each object and let the default properties be shared in the prototype. In the case above, if 99% of the circles have a radius of 1, then we don't have to store radii in our individual circles.
In your case you put a property called element in a prototype. All objects that share that prototype will have the same value for element. So if you say
x.element.className = 'something'
then that does effectively make y.element.className be something as well, assuming y shares the same prototype as x.
If you want each of the named rounded blocks to have different elements then you need to do this:
function NamedRoundedBlock(){
...
this.element = {}
this.element.className = "Block NamedRound root";
this.element.appendChild(name);
}
Check out Traditional OOP Inheritance in JavaScript
The above blog post may help answer some of your questions.
It elaborates particularly on defining a new constructor for an object, which "inherits" from a parent. Is that one of your questions? Take this for example:
NamedRoundedBlock.prototype = Object.create(
Block.prototype,
{
"constructor": {
configurable: true,
enumerable: false,
writable: true,
value: 'NamedRoundedBlock'
}
}
);
The above created an object based on Block's prototype, but it defines its own constructor, and also sets it as not enumerable.
Also, should the first line be something like this?:
function NamedRoundedBlock() {
this.name = this.makeFeild("name");
//etc.
Additionally, independent of the issue, I'd be careful with the spelling of "Feild" (spelled "Field"), as spelling issues can sometimes cause hard-to-find bugs later in the code.

Javascript collection

sorry for noobie question. Can you explain please, what is the difference between:
1. var a = [];
a['b'] = 1;
2. var a = {};
a['b'] = 1;
I could not find article in the internet so wrote here.
Literals
The [] and {} are called the array and object literals respectively.
var x = [] is short for var x = new Array();
and var y = {} is short for var y = new Object();
Arrays
Arrays are structures with a length property. You can access values via their numeric index.
var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';
And if you want to list all the properties you do:
for(var i = 0; i < x.length; i++)
console.log(x[i]);// numeric index based access.
Performance tricks and gotchas
1. Inner-caching the length property
The standard array iteration:
for (var i = 0; i < arr.length; i++) {
// do stuff
};
Little known fact: In the above scenario, the arr.length property is read at every step of the for loop. Just like any function you call there:
for (var i = 0; i < getStopIndex(); i++) {
// do stuff
};
This decreases performance for no reason. Inner caching to the rescue:
for (var i = 0, len = arr.length; i < len; i++) {
// enter code here
};
Here's proof of the above.
2. Don't specify the Array length in the constructor.
// doing this:
var a = new Array(100);
// is very pointless in JS. It will result in an array with 100 undefined values.
// not even this:
var a = new Array();
// is the best way.
var a = [];
// using the array literal is the fastest and easiest way to do things.
Test cases for array definition are available here.
3. Avoid using Array.prototype.push(arr.push)
If you are dealing with large collections, direct assignment is faster than using the Array.prototype.push(); method.
myArray[i] = 0; is faster than myArray.push(0);, according to jsPerf.com test cases.
4. It is wrong to use arrays for associative assignments.
The only reason why it works is because Array extends the Object class inside the core of the JS language. You can just as well use a Date(); or RegEx(); object for instance. It won't make a difference.
x['property'] = someValue MUST always be used with Objects.
Arrays should only have numeric indexes. SEE THIS, the Google JS development guidelines! Avoid for (x in arr) loops or arr['key'] = 5;.
This can be easily backed up, look HERE for an example.
var x = [];
console.log(x.prototype.toString.call);
 will output: [object Array]
This reveals the core language's 'class' inheritance pattern.  
var x = new String();
console.log(x.prototype.toString.call);
will output [object String].
5. Getting the minimum and maximum from an array.
A little known, but really powerful trick:
function arrayMax(arr) {
return Math.max.apply(Math, arr);
};
, respectively:
function arrayMin(arr) {
return Math.min.apply(Math, arr);
};
Objects
With an object you can only do:
var y = {} or var y = new Object();
y['first'] = 'firstValue' is the same as y.first = 'firstValue', which you can't do with an array. Objects are designed for associative access with String keys.
And the iteration is something like this:
for (var property in y) {
if (y.hasOwnProperty(property)) {
console.log(y.property);
};
};
Performance tricks and gotchas
1. Checking if an object has a property.
Most people use Object.prototype.hasOwnProperty. Unfortunately that often gives erroneous results leading to unexpected bugs.
Here's a good way to do it:
function containsKey(obj, key) {
return typeof obj[key] !== 'undefined';
};
2. Replacing switch statements.
One of the simple but efficient JS tricks is switch replacement.
switch (someVar) {
case 'a':
doSomething();
break;
case 'b':
doSomethingElse();
break;
default:
doMagic();
break;
};
In most JS engines the above is painfully slow. When you are looking at three possible outcomes, it doesn't make a difference, but what if you had tens or hundreds?
The above can easily be replaced with an object. Don't add the trailing (), this is not executing the functions, but simply storing references to them:
var cases = {
'a': doSomething,
'b': doSomethingElse,
'c': doMagic
};
Instead of the switch:
var x = ???;
if (containsKey(cases, x)) {
cases[x]();
} else {
console.log("I don't know what to do!");
};
3. Deep-cloning made easy.
function cloneObject(obj) {
var tmp = {};
for (var key in obj) {
tmp[key] = fastDeepClone(obj[key];
};
return tmp;
}
function cloneArr(arr) {
var tmp = [];
for (var i = 0, len = arr.length; i < len; i++) {
tmp[i] = fastDeepClone(arr[i]);
}
return tmp;
}
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
};
function isArray(obj) {
return obj instanceof Array;
}
function isObject(obj) {
var type = typeof obj;
return type === 'object' && obj !== null || type === 'function';
}
function fastDeepClone(obj) {
if (isArray(obj)) {
return cloneArr(obj);
} else if (isObject(obj)) {
return cloneObject(obj);
} else {
return obj;
};
};
HERE is the deep clone function in action.
Auto-boxing
As a dynamically typed language, JavaScript is limited in terms of native object types:
Object
Array
Number
Boolean
Date
RegEx
Error
Null is not a type, typeof null is object.
What's the catch? There is a strong distinction between primitive and non-primitive objects.
var s = "str";
var s2 = new String("str");
They do the same thing, you can call all string methods on s and s2.
Yet:
type of s == "string"; // raw data type
type of s2 == "object" // auto-boxed to non-primitive wrapper type
s2.prototype.toString.call == "[object String]";
You may hear in JS everything is an object. That's not exactly true, although it's a really easy mistake to make.
In reality there are 2 types, primitives and objects, and when you call s.indexOf("c"), the JS engine will automatically convert s to its non-primitive wrapper type, in this case object String, where all the methods are defined on the String.prototype.
This is called auto-boxing. The Object.prototype.valueOf(obj) method is a way to force the cast from primitive to non-primitive. It's the same behaviour a language like Java introduces for many of it's own primitives, specifically the pairs: int - Integer, double - Double, float - Float, etc.
Why should you care?
Simple:
function isString(obj) {
return typeof obj === "string";
}
isString(s); // true
isString(s2); // false
So if s2 was created with var s2 = new String("test") you are getting a false negative, even for an otherwise conceivably simple type check. More complex objects also bring with themselves a heavy performance penalty.
A micro-optimization as some would say, but the results are truly remarkable, even for extremely simple things such as string initialisation. Let's compare the following two in terms of performance:
var s1 = "this_is_a_test"
and
var s2 = new String("this_is_a_test")
You will probably expected matching performance across the board, but rather surprisingly the latter statement using new String is 92% slower than the first one, as proven here.
Functions
1. Default parameters
The || operator is the simplest possible way of defaulting. Why does it work? Because of truthy and falsy values.
When evaluated in a logical condition, undefined and null values will autocast to false.
A simple example(code HERE):
function test(x) {
var param = x || 5;
// do stuff with x
};
2. OO JS
The most important thing to understand is that the JavaScript this object is not immutable. It is simply a reference that can be changed with great ease.
In OO JS, we rely on the new keyword to guarantee implicit scope in all members of a JS Class. Even so, you can easily change the scope, via Function.prototype.call and Function.prototype.apply.
Another very important thing is the Object.prototype. Non-primitive values nested on an objects prototype are shared, while primitive ones are not.
Code with examples HERE.
A simple class definition:
function Size(width, height) {
this.width = width;
this.height = height;
};
A simple size class, with two members, this.width and this.height.
In a class definition, whatever has this in front of it, will create a new reference for every instance of Size.
Adding methods to classes and why the "closure" pattern and other "fancy name pattern" are pure fiction
This is perhaps where the most malicious JavaScript anti-patterns are found.
We can add a method to our Size class in two ways.
Size.prototype.area = function() {
return this.width * this.height;
};
Or:
function Size2(width, height) {
this.width = width;
this.height = height;
this.area = function() {
return this.width * this.height;
}
}
var s = new Size(5, 10);
var s2 = new Size2(5, 10);
var s3 = new Size2(5, 10);
var s4 = new Size(5, 10);
// Looks identical, but lets use the reference equality operator to test things:
s2.area === s3.area // false
s.area === s4.area // true
The area method of Size2 is created for every instance.
This is completely useless and slow, A LOT slower. 89% to be exact. Look HERE.
The above statement is valid for about 99% of all known "fancy name pattern". Remember the single most important thing in JS, all those are nothing more than fiction.
There are strong architectural arguments that can be made, mostly revolved around data encapsulation and the usage of closures.
Such things are unfortunately absolutely worthless in JavaScript, the performance loss simply isn't worth it. We are talking about 90% and above, it's anything but negligible.
3. Limitations
Because prototype definitions are shared among all instances of a class, you won't be able to put a non-primitive settings object there.
Size.prototype.settings = {};
Why? size.settings will be the same for every single instance.
So what's with the primitives?
Size.prototype.x = 5; // won't be shared, because it's a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it's the same as int and Integer.
The point:
The average JS guy will write JS in the following way:
var x = {
doStuff: function(x) {
},
doMoreStuff: 5,
someConstant: 10
}
Which is fine (fine = poor quality, hard to maintain code), as long as you understand that is a Singleton object, and those functions should only be used in global scope without referencing this inside them.
But then it gets to absolutely terrible code:
var x = {
width: 10,
height: 5
}
var y = {
width: 15,
height: 10
}
You could have gotten away with: var x = new Size(10, 5); var y = new Size(15, 5);.
Takes longer to type, you need to type the same thing every time. And again, it's A LOT SLOWER. Look HERE.
Poor standards throughout
This can be seen almost anywhere:
function() {
// some computation
var x = 10 / 2;
var y = 5;
return {
width: x,
height: y
}
}
Again with the alternative:
function() {
var x = 10 / 2;
var y = 5;
return new Size(10, 5);
};
The point: USE CLASSES WHEREVER APPROPRIATE!!
Why? Example 1 is 93% Slower. Look HERE.
The examples here are trivial, but they illustrate something being ignored in JS, OO.
It's a solid rule of thumb not to employ people who think JS doesn't have classes and to get jobs from recruiters talking about "Object Orientated" JS.
Closures
A lot of people prefer them to the above because it gives them a sense of data encapsulation. Besides the drastic 90% performance drop, here's something equally easy to overlook. Memory leaks.
function Thing(someParam) {
this.someFn = function() {
return someParam;
}
}
You've just created a closure for someParam. Why is this bad? First, it forces you to define class methods as instance properties, resulting in the big performance drop.
Second, it eats up memory, because a closure will never get dereferenced. Look here for proof. Sure, you do get some fake data encapsulation, but you use three times the memory with a 90% performance drop.
Or you can add #private and get a way with an underscore function name.
Other very common ways of generating closures:
function bindSomething(param) {
someDomElement.addEventListener("click", function() {
if (param) //do something
else // do something else
}, false);
}
param is now a closure! How do you get rid of it? There are various tricks, some found here. The best possible approach, albeit more rigorous is to avoid using anonymous functions all-together, but this would require a way to specify scopes for event callbacks.
Such a mechanism is only available in Google Closure, as far as I know.
The singleton pattern
Ok, so what do I do for singletons? I don't want to store random references. Here's a wonderful idea shamelessly stolen from Google Closure's base.js
/**
* Adds a {#code getInstance} static method that always return the same instance
* object.
* #param {!Function} ctor The constructor for the class to add the static
* method to.
*/
function addSingletonGetter(ctor) {
ctor.getInstance = function() {
if (ctor.instance_) {
return ctor.instance_;
}
return ctor.instance_ = new ctor;
};
};
It's Java-esque, but it's a simple and powerful trick. You can now do:
project.some.namespace.StateManager = function() {
this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);
How is this useful? Simple. In all other files, every time you need to reference project.some.namespace.StateManager, you can write:
project.some.namespace.StateManager.getInstance(). This is more awesome than it looks.
You can have global state with the benefits of a class definition (inheritance, stateful members, etc.) and without polluting the global namespace.
The single instance pattern
You may now be tempted to do this:
function Thing() {
this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();
That is another big no-no in JavaScript. Remember, the this object is only guaranteed to be immutable when the new keyword is used. The magic behind the above code is interesting. this is actually the global scope, so without meaning to you are adding methods to the global object. And you guessed it, those things never get garbage collected.
There is nothing telling JavaScript to use something else. A function on it's own doesn't have a scope. Be really careful what you do with static properties. To reproduce a quote I once read, the JavaScript global object is like a public toilet. Sometimes you have no choice but to go there, yet try and minimise contact with the surfaces as much as possible.
Either stick to the above Singleton pattern or use a settings object nested under a namespace.
Garbage collection in JavaScript
JavaScript is a garbage collected language, but JavaScript GC is often rather poorly understood. The point is again speed. This is perhaps all too familiar.
// This is the top of a JavaScript file.
var a = 5;
var b = 20;
var x = {};//blabla
// more code
function someFn() {..}
That is bad, poor performance code. The reason is simple. JS will garbage collect a variable and free up the heap memory it holds only when that variable gets de-scoped, e.g. there are no references to it anywhere in the memory.
For example:
function test(someArgs) {
var someMoreStuf = // a very big complex object;
}
test();
Three things:
Function arguments are transformed into local definitions
Inner declarations are hoisted.
All the heap memory allocated for inner variables is freed up when the function finishes execution.
Why?
Because they no longer belong to the "current" scope. They are created, used, and destroyed. There are no closures either, so all the memory you've used is freed up through garbage collection.
For that reason, you should never, your JS files should never look like this, as global scope will just keep polluting memory.
var x = 5;
var y = {..}; //etc;
Alright, now what?
Namespaces.
JS doesn't have namespaces per say, so this isn't exactly a Java equivalent, yet from a codebase administration perspective you get what you want.
var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
// some class definition here
}
Beautiful. One global variable. Proper project structure.
With a build phase, you can split your project across files, and get a proper dev environment.
There's no limit to what you can achieve from here.
Count your libraries
Having had the pleasure of working on countless codebases, the last and most important argument is to be very mindful of your code dependencies. I've seen programmers casually adding jQuery into the mix of the stack for a simple animation effect and so forth.
Dependency and package management is something the JavaScript world hadn't addresses for the longest time, until the creation of tools like Bower. Browsers are still somewhat slow, and even when they're fast, internet connections are slow.
In the world of Google for instance, they go through the lengths of writing entire compilers just to save bytes, and that approach is in many ways the right mentality to have in web programming. And I uphold Google in very high regard as their JS library powers apps like Google Maps, which are not only insanely complex, but also work everywhere.
Arguably JavaScript has an immense variety of tools available, given its popularity, accessibility, and to some extent very low quality bar the ecosystem as a whole is willing to accept.
For Hacker News subscribers, a day doesn't go by without a new JS library out there, and they are certainly useful but one cannot ignore the fact that many of them re-implement the exact same concerns without any strong notion of novelty or any killer ideas and improvements.
It's a strong rule of thumb to resist the urge of mixing in all the new toys before they have the time to prove their novelty and usefulness to the entire ecosystem and to strongly distinguish between Sunday coding fun and production deployments.
If your <head></head> tag is longer than this post, you're doing it all wrong.
Testing your knowledge of JavaScript
A few "perfectionist" level tests:
http://perfectionkills.com/javascript-quiz/, thanks to Kangax.
http://javascript-puzzlers.herokuapp.com/
A collection of objects? Use this notation (JavaScript arrays):
var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];
To put a new element into your collection:
collection.push( {name:"object 4"} );
In JavaScript all objects are associative arrays. In first case you create an array in the second case you created an empty object which is array too :).
So in JS you can work with any object as with array:
var a = {};
a["temp"] = "test";
And as object:
var a = {};
a.temp = "test";
I would use an array of objects:
collection = [
{ "key":"first key", "value":"first value" },
{ "key":"second key", "value":"second value" }
];
etc
1) Is an Array
2) Is an Object
With Array all is usual as in other languages
With Object also.
- You can get value a.b == 1
- But in JS you can also get value with such syntax a["b"] == 1
This could be usefull when key look like something this "some key", in this case you can't use "chaining"
also this usefull if key is the variable
you can write like this
function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
key = "name";
}else{
key = "age";
}
return Object[key];
}
but I want to use it as collection, which I have to choose?
This depends of what data you want to store

Categories

Resources