literals
[] and {} are called array and object literals, respectively.
var x = [] not suitable for var x = new Array();
and var y = {} not enough for var y = new Object();
Arrays
Arrays are structures with the length property. You can access values through their numerical index.
var x = [] or var x = new Array(); x[0] = 'b'; x[1] = 'c';
And if you want to list all the properties that you execute:
for(var i = 0; i < x.length; i++) console.log(x[i]);
Performance Tricks and Gotchas
1. Internal caching of the length property
Iterate over a standard array:
for (var i = 0; i < arr.length; i++) {
A little-known fact: in the above scenario, the arr.length property arr.length read at each step of the for loop. Like any function you call there:
for (var i = 0; i < getStopIndex(); i++) {
This reduces performance for no reason. Internal caching to help:
for (var i = 0, len = arr.length; i < len; i++) {
Here is the confirmation above.
2. Do not specify the length of the array 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 are available to define the array 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 Array.prototype.push(); .
myArray[i] = 0; faster than myArray.push(0); , according to jsPerf.com test cases .
4. It is incorrect to use arrays for associative assignments.
The only reason this works is because Array extends the Object class inside the core of the JS language. For example, you can use the Date(); object Date(); or RegEx(); . This will not change the situation. x['property'] = someValue MUST always use Objects .
Arrays should have only numeric indices. SEE THIS , Google JS development recommendations! Avoid for (x in arr) or arr['key'] = 5; loops arr['key'] = 5; .
It can be easily copied, see HERE for an example.
var x = []; console.log(x.prototype.toString.call);
will output: [object Array]
This shows the inheritance pattern for the class class.
var x = new String(); console.log(x.prototype.toString.call);
outputs [object String] .
5. Getting the minimum and maximum from the 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); };
The objects
Using an object, you can only do:
var y = {} or var y = new Object();
y['first'] = 'firstValue' same as y.first = 'firstValue' , which you cannot do with an array. Objects are intended for associative access using the String keys.
The iteration looks something like this:
for (var property in y) { if (y.hasOwnProperty(property)) { console.log(y.property); }; };
Performance Tricks and Gotchas
1. Check for the presence of an object property.
Most people use Object.prototype.hasOwnProperty . Unfortunately, this often leads to erroneous results, leading to unexpected errors.
Here is a good way to do this:
function containsKey(obj, key) { return typeof obj[key] !== 'undefined'; };
2. Replacing switch statements.
One of JS’s simple but effective tricks is to replace switch .
switch (someVar) { case 'a': doSomething(); break; case 'b': doSomethingElse(); break; default: doMagic(); break; };
On most JS engines, the above is painfully slow. When you look at the three possible outcomes, it doesn't make any difference, but what if you had tens or hundreds?
The above can be easily replaced by an object. Do not add a trailing () , this does not perform functions, but simply stores links to them:
var cases = { 'a': doSomething, 'b': doSomethingElse, 'c': doMagic };
Instead of switch :
var x = ???; if (containsKey(cases, x)) { c(x); } else { console.log("I don't know what to do!"); };
3. Deep cloning has become easier.
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 a function of deep cloning in action.
Auto Box
As a dynamically typed language, JavaScript is limited in terms of native object types:
- An object
- Array
- amount
- Boolean
- date of
- RegEx
- Mistake
Null is not a type, typeof null is an object.
What is 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, you can call all string methods on s and s2 . However:
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 can hear in JS all that is Object . This is not entirely true, although this is a very simple mistake.
Actually there are 2 types, primitives and objects, and when s.indexOf("c") called, the JS engine automatically converts s to its non-primitive wrapper type, in this case object String , where all methods are defined on String.prototype .
This is called auto-boxing . The Object.prototype.valueOf(obj) method is a way to force a cast from primitive to non-primitive. This is the same behavior as a language, such as Java, for many of its own primitives, in particular, pairs: int - Integer, double - Double, float - Float, etc.
Why do you need this?
Plain:
function isString(obj) { return typeof obj === "string"; } isString(s);
So, if s2 was created using var s2 = new String("test") , you get a false negative even for a simple, apparently simple type check. More complex objects also bring heavy performance.
Micro-optimization, as some would say, but the results are really great even for extremely simple things like string initialization. Let me compare the following two in terms of performance:
var s1 = "this_is_a_test"
and
var s2 = new String("this_is_a_test")
You probably expect performance matching across the board, but, surprisingly, the last statement using new String is 92% slower than the first, as proven here .
Functions
1. Default Settings
Operator || is the easiest way to default. Why does it work? Because of true and false meanings.
When evaluating in a logical state, undefined and null will autoload to false .
Simple example (code HERE ):
function test(x) { var param = x || 5;
2. OO JS
The most important thing to understand is that this JavaScript object is not immutable. This is just a link that can be easily changed.
At OO JS, we rely on the new keyword to guarantee implicit coverage in all members of the JS class. However, you can easily change the scope using Function.prototype.call and Function.prototype.apply .
Another important thing is Object.prototype . Non-primitive values embedded in the prototype objects are common, but primitive ones are not.
Code with examples HERE .
The simplest 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 the class definition, no matter what this in front of it, it will create a new link for each instance of Size.
Adding methods to classes and why the "closure" pattern and another "fantasy name pattern" are pure fantasy
Perhaps the most malicious JavaScript anti-patterns have been discovered here.
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);
The area Size2 method is created for each instance. It is completely useless and slow, LOTS slower. 89% to be exact. Take a look HERE .
The above statement is valid for approximately 99% of all known "trendy name patterns." Remember the most important thing in JS, all this is nothing but fiction.
There are strong architectural arguments that can be made that are mainly related to data encapsulation and the use of closures.
Such things, unfortunately, are absolutely useless in JavaScript; performance loss is simply not worth it. We are talking about 90% and above, this is nothing but insignificant.
3. Limitations
Since prototype definitions are shared between all instances of the class, you cannot place a non-primitive settings object there.
Size.prototype.settings = {};
Why? size.settings will be the same for each individual instance. So what about the primitives?
Size.prototype.x = 5;
Point:
The average JS guy will write JS as follows:
var x = { doStuff: function(x) { }, doMoreStuff: 5, someConstant: 10 }
What is excellent (excellent = poor quality, it is difficult to maintain code) if you understand that this is a Singleton object and these functions should be used only in the global area without this links inside them.
But then it gets into absolutely terrible code:
var x = { width: 10, height: 5 } var y = { width: 15, height: 10 }
You could leave with: var x = new Size(10, 5); var y = new Size(15, 5); var x = new Size(10, 5); var y = new Size(15, 5); .
Takes more time, you need to enter the same thing every time. And again, this is a TRAY. Take a look HERE .
Bad standards in everything
This can be seen almost everywhere:
function() {
Again with an alternative:
function() { var x = 10 / 2; var y = 5; return new Size(10, 5); };
Point: USE CLASSES WHICH ARE AVAILABLE!
Why? Example 1 93% slower . Look HERE , The examples here are trivial, but they illustrate that something is ignored in JS, OO.
It’s a good rule not to use people who think that JS doesn’t have classes, and to get work from recruiters talking about “Object Orientated” JS.
Shutters
Many people prefer them higher because it gives them a sense of data encapsulation. Besides a sharp 90% drop in productivity, something equally easy to lose sight of here. Memory leak.
function Thing(someParam) { this.someFn = function() { return someParam; } }
You just created a closure for someParam . Why is that bad? First, it forces you to define class methods as instance properties, which leads to a big performance hit.
Secondly, he eats memory, because the closure will never be dereferenced. Look here for confirmation. Of course, you get some fake data encapsulation, but you use memory three times with a 90% performance drop.
Or you can add @private and get a method named underscore function.
Other very common ways to create closures:
function bindSomething(param) { someDomElement.addEventListener("click", function() { if (param)
param now a closure! How do you get rid of it? There are various tricks, some are found here . The best possible approach, albeit a more rigorous one, is to avoid using anonymous functions together, but this would require a way to define areas for event callbacks.
Such a mechanism is available only in Google Closure, as far as I know.
Syntax pattern
So what should I do for singles? I do not want to store random links. Here's a wonderful idea shamelessly stolen from Google Closure base.js
function addSingletonGetter(ctor) { ctor.getInstance = function() { if (ctor.instance_) { return ctor.instance_; } return ctor.instance_ = new ctor; }; };
This is Java-esque, but it is a simple and powerful trick. Now you can:
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? Simply. 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 amazing than it sounds.
You can have a global state with the advantages of class definition (inheritance, members with state, etc.) and without polluting the global namespace.
Single Instance Template
You may be tempted to do this:
function Thing() { this.someMethod = function() {..} }
This is another big no-no in JavaScript. Remember that the this object is guaranteed to remain unchanged when using the new keyword. The magic behind the above code is interesting. this is actually a global scope, so without the need to add methods to a global object. And you guessed that these things are never going to collect garbage.
Nothing is said that JavaScript uses something else. A function in itself has no scope. Be very careful what you do with static properties. To reproduce the quote I once read, a global JavaScript object is like a public toilet. Sometimes you have no choice but to go there, but try to minimize contact with surfaces.
Either stick to the Singleton pattern above, or use the settings object nested in the namespace.
JavaScript garbage collection
JavaScript is a garbage collection, but JavaScript GC is often pretty poorly understood. The point is speed again. This may be too familiar.
This is a bad, bad performance code. The reason is simple. JS will garbage collect a variable and free up the heap memory it holds only when this variable becomes de-copied, for example. there are no references to it in memory.
For example:
function test(someArgs) { var someMoreStuf =
Three things:
- Function arguments are converted to local definitions.
- Internal ads are up .
- All heap memory allocated for internal variables is freed when the function completes execution.
Why? Because they no longer belong to the “current” reach. They are created, used and destroyed. There are also no closures, so all the memory you use is freed through garbage collection.
For this reason, you should never, your JS files will never look like this, because the global area will just save polluting memory.
var x = 5; var y = {..};
Ok now what?
Namespaces .
JS does not have namespaces for each word, so this is not exactly the equivalent of Java, but from the point of view of code administration, you get what you want.
var myProject = {}; myProject.settings = {}; myProject.controllers = {}; myProject.controlls.MainController = function() {
Beautiful. One global variable. Proper project structure. With the build phase, you can split the project into files and get the proper development environment.
There is no limit to what you can achieve.
Count your libraries
Having enjoyed working with countless codebases, the last and most important argument should be very attentive to your code dependencies. I saw programmers accidentally add jQuery to the stack mix for a simple animation effect, etc.
Dependency and package management is that in the JavaScript world there have been no addresses for the longest time before creating tools like Bower. Browsers are still somewhat slow, and even when they are fast, Internet connections are slow.
In the Google world, for example, they look at the writing lengths of entire compilers just to save bytes , and this approach has a lot of good thinking in web programming. And I support Google in a very high regard, because their JS libraries support applications like Google Maps, which are not only insanely complex, but they work everywhere.
JavaScript may have a huge variety of tools available, given its popularity, accessibility and, to some extent, the very low level of quality that the ecosystem as a whole is ready to accept.
For Hacker News subscribers, the day does not pass without a new JS library, and they are certainly useful, but one cannot ignore the fact that many of them re-implement the same problems without any strong idea of novelty or any ideas and improvements of the killer .
This is a strong rule of thumb to counter the urge to mix in all new toys before they have time to prove their novelty and utility to the entire ecosystem and clearly distinguish between Sunday coding and production deployments.
If the <head></head> longer than this message, you are doing it all wrong.
Testing Your JavaScript Knowledge
Several tests of "perfectionists":