-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFrontend_Questions_Answers
1806 lines (1672 loc) · 106 KB
/
Frontend_Questions_Answers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Specially refer:
https://github.com/darcyclarke/Front-end-Developer-Interview-Questions
(READ AT HOME)
http://www.quirksmode.org/js/contents.html (FOR IN PERSON)
http://blog.sourcing.io/interview-questions
http://www.careercup.com/page?pid=front-end-web-development-interview-questions
Javascript:
Good for reference:
https://developer.mozilla.org/enUS/docs/Web/JavaScript/Guide
(READ AT HOME) (FOR IN PERSON)
http://www.programmerinterview.com/index.php/javascript/javascript-introduction/ (EXCELLENT)
http://blog.kevinchisholm.com/object-oriented-javascript/javascript-interview-questions-object-oriented-javascript/ (EXCELLENT)
http://doppnet.com/2011/10/10-advanced-javascript-interview-questions/
http://stackoverflow.com/questions/2492123/pro-javascript-programmer-interview-questions-with-answers
What is the difference between using var keyword in java script & without using it for a variable?
For example:
var x = 14;
And x = 14;
Are these same or when we declare var x, its a local variable & when it doesn't have var keyword, then its global?
If var is used within a function or other non-global scope then the variable is not a global variable. If var is not used before a variable name, then you have created a global variable.
When would you use var in your declaration and when you wouldn’t?
Always use var. Not using var for variable declaration will traverse scopes all the way up till the global scope. If variable with that name is not found it will declare it in the global scope. Therefore not using var implicitly declares variable in the global scope (which, let me remind you, is a bad practice).
(function() {
baz = 5;
var bar = 10;
})();
console.log(baz); // outputs 5
//console.log(bar); // error: bar is not defined
Try it: http://jsfiddle.net/tnajdek/AKxn9/
A common mistake is to not use var in loops (e.g. for(i=0; i<10; i++) when i has not been previously declared) which will pollute the global scope and in some cases might bear unexpected results.
What does the attribute defer/async do when added to the script tag?
The defer attribute will cause browser to execute script after the document has been parsed. This attribute was first implemented in Internet Explorer 4, then added to HTML 4 and more recently HTML 5 spec. You might not have heard of it as it has not been supported by other browsers (Firefox support came in version 3.5 - Gecko 1.9.2). Async is another attribute that can affect how a script is loaded and executed, here is a quote from HTML 5 spec on how this is expected to work:
There are three possible modes that can be selected using these attributes. If the async attribute is present, then the script will be executed asynchronously, as soon as it is available. If the async attribute is not present but the defer attribute is present, then the script is executed when the page has finished parsing. If neither attribute is present, then the script is fetched and executed immediately, before the user agent continues parsing the page.
Note: A somewhat (but not exactly) similar defer behavior can be achieved by placing your script tags at the end of the body tag and that's what is considered to be modern 'best practice'
What is the difference between == and ===? Which one would you use?
The equality (==) operator will compare for equality after doing necessary type casting, the identity operator (===) doesn't do any conversions. A good practice suggested by Douglas Crockford is to always use strict equality, couple of examples from Douglas' book JavaScript: The Good Parts
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
How would you check if a variable is null/undefined?
//check if bar is null
bar === null
//check if bar is undefined
typeof bar === "undefined"
How do you check if a variable is an object
You can use typeof to determine if variable is an object, however bear in mind that null is actually an object! However null object is 'falsy' thus the following will work:
if(bar && typeof bar === "object") {
console.log('bar is object and is not null');
}
Discuss scoping in JavaScript.
JavaScript has lexical scoping based on functions but not blocks. Therefore:
//global scope
(function() {
//anonymous function scope
var foo = 1;
function bar() {
//bar function scope
var foo = 2;
}
bar();
console.log(foo); //outputs 1
if(true) {
var foo = 3; //redeclares foo
}
console.log(foo); //outputs 3
})();
Try it: http://jsfiddle.net/tnajdek/8y3XC/. Note: from within function scope everything in above scope(s) is available (see closures below)
Explain hoisting in JavaScript.
As some might not be familiar with the term 'hoisting' yet have the relevant experience this question could be asked indirectly
In JavaScript function declarations ( function foo() {} ) and variable declarations ( var bar ) are 'hoisted' i.e. are silently moved to the very top of the scope. Consider the following code:
(function() {
console.log(bar); //returns 'undefined'
//console.log(baz) // error: baz is not defined
foo(); // outputs 'aloha' to the console
//function declaration AND its body is hoisted
function foo() {
console.log('aloha');
}
//variable declaration is hoisted but value assignment stays here
var bar = 1;
baz = 2; //defines baz in global scope
})();
See for yourself: http://jsfiddle.net/tnajdek/FxDrj/
What are closures?
(function() {
function foo(x) {
var baz = 3;
return function (y) {
console.log(x + y + (++baz));
}
}
var moo = foo(2); // moo is now a closure.
moo(1); // 7
moo(1); // 8!
})();
The inner function inside foo will close-over the variables of foo before leaving creating a closure.
Try it: http://jsfiddle.net/tnajdek/Rj6mK/
Explain prototypal/differential inheritance
Conceptually this is very simple: A new object can inherit properties of an old object.
(function() {
var genericObject = {
bar : "Hello World",
get_bar : function() {
return this.bar;
}
};
var customObject = Object.create(genericObject);
customObject.bar = "Aloha folks!";
console.log(customObject.get_bar()); //outputs: "Aloha folks"
delete customObject.bar;
console.log(customObject.get_bar()); //fallbacks to the prototype's value, outputs: "Hello World"
})();
While JavaScript has always been a prototype-oriented language, tools to work with prototypes were somewhat missing. Object.create used in the code snipped above has been added in ECMAScript 5 and has not been supported prior to Firefox 4, Chrome 5, IE 9
What is Strict Mode in JavaScript
Strict Mode has been introduced as part of ECMAScript 5 and introduces new, restricted variant of JavaScript which has following aims:
• Throws errors for actions that are rather silly but previously didn't throw an error
• Throws errors for potentially unsafe actions
• Disables functions that are poorly thought out
• Potentially code in strict mode could run faster by eliminating mistakes that would make it difficult for JavaScript engines to perform optimizations
Strict mode can be enabled for the entire source file or on per function basis by adding a string literal "use strict" on top of the file/function i.e.
function foo(){
"use strict";
// ... your code ...
}
For more detailed information about the strict mode consult relevant article on MDN
Extra topics for discussion:
• What's your favorite browser, framework, JavaScript book
• How do you approach debugging in JavaScript
• What do you think of JSLint?
• Browser detection vs. feature sniffing
http://www.techrepublic.com/blog/software-engineer/javascript-interview-questions-and-answers/
http://ryanmorr.com/understanding-scope-and-context-in-javascript/
http://bonsaiden.github.io/JavaScript-Garden/
Objects
Object Usage and Properties
Everything in JavaScript acts like an object, with the only two exceptions being null and undefined.
false.toString(); // 'false'
[1, 2, 3].toString(); // '1,2,3'
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1
A common misconception is that number literals cannot be used as objects. That is because a flaw in JavaScript's parser tries to parse the dot notation on a number as a floating point literal.
2.toString(); // raises SyntaxError
There are a couple of workarounds that can be used to make number literals act as objects too.
2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first
Objects as a Data Type
Objects in JavaScript can also be used as Hashmaps; they mainly consist of named properties mapping to values.
Using an object literal - {} notation - it is possible to create a plain object. This new object inherits from Object.prototype and does not have own properties defined.
var foo = {}; // a new empty object
// a new object with a 'test' property with value 12
var bar = {test: 12};
Accessing Properties
The properties of an object can be accessed in two ways, via either the dot notation or the square bracket notation.
var foo = {name: 'kitten'}
foo.name; // kitten
foo['name']; // kitten
var get = 'name';
foo[get]; // kitten
foo.1234; // SyntaxError
foo['1234']; // works
The notations work almost identically, with the only difference being that the square bracket notation allows for dynamic setting of properties and the use of property names that would otherwise lead to a syntax error.
Deleting Properties
The only way to remove a property from an object is to use the deleteoperator; setting the property to undefined or null only removes the valueassociated with the property, but not the key.
var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, '' + obj[i]);
}
}
The above outputs both bar undefined and foo null - only baz was removed and is therefore missing from the output.
Notation of Keys
var test = {
'case': 'I am a keyword, so I must be notated as a string',
delete: 'I am a keyword, so me too' // raises SyntaxError
};
Object properties can be both notated as plain characters and as strings. Due to another mis-design in JavaScript's parser, the above will throw aSyntaxError prior to ECMAScript 5.
This error arises from the fact that delete is a keyword; therefore, it must be notated as a string literal to ensure that it will be correctly interpreted by older JavaScript engines.
The Prototype
JavaScript does not feature a classical inheritance model; instead, it uses aprototypal one.
While this is often considered to be one of JavaScript's weaknesses, the prototypal inheritance model is in fact more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model, while the other way around is a far more difficult task.
JavaScript is the only widely used language that features prototypal inheritance, so it can take time to adjust to the differences between the two models.
The first major difference is that inheritance in JavaScript uses prototype chains.
Note: Simply usingBar.prototype = Foo.prototype will result in both objects sharing the sameprototype. Therefore, changes to either object's prototype will affect the prototype of the other as well, which in most cases is not the desired effect.
function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};
function Bar() {}
// Set Bar's prototype to a new instance of Foo
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';
// Make sure to list Bar as the actual constructor
Bar.prototype.constructor = Bar;
var test = new Bar(); // create a new bar instance
// The resulting prototype chain
test [instance of Bar]
Bar.prototype [instance of Foo]
{ foo: 'Hello World' }
Foo.prototype
{ method: ... }
Object.prototype
{ toString: ... /* etc. */ }
In the code above, the object test will inherit from both Bar.prototype andFoo.prototype; hence, it will have access to the function method that was defined on Foo. It will also have access to the property value of the oneFoo instance that is its prototype. It is important to note that new Bar()does not create a new Foo instance, but reuses the one assigned to its prototype; thus, all Bar instances will share the same value property.
Note: Do not useBar.prototype = Foo, since it will not point to the prototype of Foo but rather to the function object Foo. So the prototype chain will go overFunction.prototype and notFoo.prototype; therefore,method will not be on the prototype chain.
Property Lookup
When accessing the properties of an object, JavaScript will traverse the prototype chain upwards until it finds a property with the requested name.
If it reaches the top of the chain - namely Object.prototype - and still hasn't found the specified property, it will return the value undefined instead.
The Prototype Property
While the prototype property is used by the language to build the prototype chains, it is still possible to assign any given value to it. However, primitives will simply get ignored when assigned as a prototype.
function Foo() {}
Foo.prototype = 1; // no effect
Assigning objects, as shown in the example above, will work, and allows for dynamic creation of prototype chains.
Performance
The lookup time for properties that are high up on the prototype chain can have a negative impact on performance, and this may be significant in code where performance is critical. Additionally, trying to access non-existent properties will always traverse the full prototype chain.
Also, when iterating over the properties of an object every property that is on the prototype chain will be enumerated.
Extension of Native Prototypes
One mis-feature that is often used is to extend Object.prototype or one of the other built in prototypes.
This technique is called monkey patching and breaks encapsulation. While used by popular frameworks such as Prototype, there is still no good reason for cluttering built-in types with additional non-standard functionality.
The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example, Array.forEach.
In Conclusion
It is essential to understand the prototypal inheritance model before writing complex code that makes use of it. Also, be aware of the length of the prototype chains in your code and break them up if necessary to avoid possible performance problems. Further, the native prototypes should neverbe extended unless it is for the sake of compatibility with newer JavaScript features.
hasOwnProperty
To check whether an object has a property defined on itself and not somewhere on its prototype chain, it is necessary to use the hasOwnPropertymethod which all objects inherit from Object.prototype.
Note: It is not enough to check whether a property isundefined. The property might very well exist, but its value just happens to be set toundefined.
hasOwnProperty is the only thing in JavaScript which deals with properties and does not traverse the prototype chain.
// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
Only hasOwnProperty will give the correct and expected result; this is essential when iterating over the properties of any object. There is no other way to exclude properties that are not defined on the object itself, but somewhere on its prototype chain.
hasOwnProperty as a Property
JavaScript does not protect the property name hasOwnProperty; thus, if the possibility exists that an object might have a property with this name, it is necessary to use an external hasOwnProperty to get correct results.
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // always returns false
// Use another Object's hasOwnProperty and call it with 'this' set to foo
({}).hasOwnProperty.call(foo, 'bar'); // true
// It's also possible to use hasOwnProperty from the Object
// prototype for this purpose
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
In Conclusion
Using hasOwnProperty is the only reliable method to check for the existence of a property on an object. It is recommended that hasOwnProperty is used inevery for in loop to avoid errors from extended native prototypes.
The for in Loop
Just like the in operator, the for in loop traverses the prototype chain when iterating over the properties of an object.
Note: The for in loop will notiterate over any properties that have their enumerable attribute set to false; for example, thelength property of an array.
// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // prints both bar and moo
}
Since it is not possible to change the behavior of the for in loop itself, it is necessary to filter out the unwanted properties inside the loop body; this is done using the hasOwnProperty method of Object.prototype.
Note: Since for in always traverses the complete prototype chain, it will get slower with each additional layer of inheritance added to an object.
Using hasOwnProperty for Filtering
// still the foo from above
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
This version is the only correct one to use. Due to the use of hasOwnProperty, it will only print out moo. When hasOwnProperty is left out, the code is prone to errors in cases where the native prototypes - e.g. Object.prototype - have been extended.
One widely used framework that extends Object.prototype is Prototype. When this framework is included, for in loops that do not usehasOwnProperty are guaranteed to break.
In Conclusion
It is recommended to always use hasOwnProperty. Assumptions should never be made about the environment the code is running in, or whether the native prototypes have been extended or not.
Functions
Function Declarations and Expressions
Functions in JavaScript are first class objects. That means they can be passed around like any other value. One common use of this feature is to pass ananonymous function as a callback to another, possibly an asynchronous function.
The function Declaration
function foo() {}
The above function gets hoisted before the execution of the program starts; thus, it is available everywhere in the scope it was defined, even if called before the actual definition in the source.
foo(); // Works because foo was created before this code runs
function foo() {}
The function Expression
var foo = function() {};
This example assigns the unnamed and anonymous function to the variablefoo.
foo; // 'undefined'
foo(); // this raises a TypeError
var foo = function() {};
Due to the fact that var is a declaration that hoists the variable name foobefore the actual execution of the code starts, foo is already declared when the script gets executed.
But since assignments only happen at runtime, the value of foo will default toundefined before the corresponding code is executed.
Named Function Expression
Another special case is the assignment of named functions.
var foo = function bar() {
bar(); // Works
}
bar(); // ReferenceError
Here, bar is not available in the outer scope, since the function only gets assigned to foo; however, inside of bar, it is available. This is due to howname resolution in JavaScript works, the name of the function is always made available in the local scope of the function itself.
How this Works
JavaScript has a different concept of what the special name this refers to than most other programming languages. There are exactly five different ways in which the value of this can be bound in the language.
The Global Scope
this;
When using this in global scope, it will simply refer to the global object.
Calling a Function
foo();
Here, this will again refer to the global object.
ES5 Note: In strict mode, the global case no longer exists.this will instead have the value of undefined in that case.
Calling a Method
test.foo();
In this example, this will refer to test.
Calling a Constructor
new foo();
A function call that is preceded by the new keyword acts as a constructor. Inside the function, this will refer to a newly created Object.
Explicit Setting of this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
When using the call or apply methods of Function.prototype, the value of this inside the called function gets explicitly set to the first argument of the corresponding function call.
As a result, in the above example the method case does not apply, and thisinside of foo will be set to bar.
Note: this cannot be used to refer to the object inside of an Object literal. So var obj = {me: this} will not result inme referring to obj, sincethis only gets bound by one of the five listed cases.
Common Pitfalls
While most of these cases make sense, the first can be considered another mis-design of the language because it never has any practical use.
Foo.method = function() {
function test() {
// this is set to the global object
}
test();
}
A common misconception is that this inside of test refers to Foo; while in fact, it does not.
In order to gain access to Foo from within test, it is necessary to create a local variable inside of method that refers to Foo.
Foo.method = function() {
var that = this;
function test() {
// Use that instead of this here
}
test();
}
that is just a normal variable name, but it is commonly used for the reference to an outer this. In combination with closures, it can also be used to pass this values around.
Assigning Methods
Another thing that does not work in JavaScript is function aliasing, which isassigning a method to a variable.
var test = someObject.methodTest;
test();
Due to the first case, test now acts like a plain function call; therefore, thisinside it will no longer refer to someObject.
While the late binding of this might seem like a bad idea at first, in fact, it is what makes prototypal inheritance work.
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
When method gets called on an instance of Bar, this will now refer to that very instance.
Closures and References
One of JavaScript's most powerful features is the availability of closures. With closures, scopes always keep access to the outer scope, in which they were defined. Since the only scoping that JavaScript has is function scope, all functions, by default, act as closures.
Emulating private variables
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
Here, Counter returns two closures: the function increment as well as the function get. Both of these functions keep a reference to the scope ofCounter and, therefore, always keep access to the count variable that was defined in that scope.
Why Private Variables Work
Since it is not possible to reference or assign scopes in JavaScript, there is noway of accessing the variable count from the outside. The only way to interact with it is via the two closures.
var foo = new Counter(4);
foo.hack = function() {
count = 1337;
};
The above code will not change the variable count in the scope of Counter, since foo.hack was not defined in that scope. It will instead create - or override - the global variable count.
Closures Inside Loops
One often made mistake is to use closures inside of loops, as if they were copying the value of the loop's index variable.
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
The above will not output the numbers 0 through 9, but will simply print the number 10 ten times.
The anonymous function keeps a reference to i. At the time console.loggets called, the for loop has already finished, and the value of i has been set to 10.
In order to get the desired behavior, it is necessary to create a copy of the value of i.
Avoiding the Reference Problem
In order to copy the value of the loop's index variable, it is best to use ananonymous wrapper.
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
The anonymous outer function gets called immediately with i as its first argument and will receive a copy of the value of i as its parameter e.
The anonymous function that gets passed to setTimeout now has a reference to e, whose value does not get changed by the loop.
There is another possible way of achieving this, which is to return a function from the anonymous wrapper that will then have the same behavior as the code above.
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
There's yet another way to accomplish this by using .bind, which can bind athis context and arguments to function. It behaves identially to the code above
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
The arguments Object
Every function scope in JavaScript can access the special variable arguments. This variable holds a list of all the arguments that were passed to the function.
Note: In case arguments has already been defined inside the function's scope either via avar statement or being the name of a formal parameter, the arguments object will not be created.
The arguments object is not an Array. While it has some of the semantics of an array - namely the length property - it does not inherit fromArray.prototype and is in fact an Object.
Due to this, it is not possible to use standard array methods like push, popor slice on arguments. While iteration with a plain for loop works just fine, it is necessary to convert it to a real Array in order to use the standardArray methods on it.
Converting to an Array
The code below will return a new Array containing all the elements of thearguments object.
Array.prototype.slice.call(arguments);
Because this conversion is slow, it is not recommended to use it in performance-critical sections of code.
Passing Arguments
The following is the recommended way of passing arguments from one function to another.
function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// do stuff here
}
Another trick is to use both call and apply together to create fast, unbound wrappers.
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// Create an unbound version of "method"
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {
// Result: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
Formal Parameters and Arguments Indices
The arguments object creates getter and setter functions for both its properties, as well as the function's formal parameters.
As a result, changing the value of a formal parameter will also change the value of the corresponding property on the arguments object, and the other way around.
function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3);
Performance Myths and Truths
The only time the arguments object is not created is where it is declared as a name inside of a function or one of its formal parameters. It does not matter whether it is used or not.
Both getters and setters are always created; thus, using it has nearly no performance impact at all, especially not in real world code where there is more than a simple access to the arguments object's properties.
ES5 Note: These getters andsetters are not created in strict mode.
However, there is one case which will drastically reduce the performance in modern JavaScript engines. That case is the use of arguments.callee.
function foo() {
arguments.callee; // do something with this function object
arguments.callee.caller; // and the calling function object
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // Would normally be inlined...
}
}
In the above code, foo can no longer be a subject to inlining since it needs to know about both itself and its caller. This not only defeats possible performance gains that would arise from inlining, but it also breaks encapsulation because the function may now be dependent on a specific calling context.
Making use of arguments.callee or any of its properties is highly discouraged.
ES5 Note: In strict mode,arguments.callee will throw aTypeError since its use has been deprecated.
Constructors
Constructors in JavaScript are yet again different from many other languages. Any function call that is preceded by the new keyword acts as a constructor.
Inside the constructor - the called function - the value of this refers to a newly created object. The prototype of this new object is set to theprototype of the function object that was invoked as the constructor.
If the function that was called has no explicit return statement, then it implicitly returns the value of this - the new object.
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
The above calls Foo as constructor and sets the prototype of the newly created object to Foo.prototype.
In case of an explicit return statement, the function returns the value specified by that statement, but only if the return value is an Object.
function Bar() {
return 2;
}
new Bar(); // a new object
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // the returned object
When the new keyword is omitted, the function will not return a new object.
function Foo() {
this.bla = 1; // gets set on the global object
}
Foo(); // undefined
While the above example might still appear to work in some cases, due to the workings of this in JavaScript, it will use the global object as the value ofthis.
Factories
In order to be able to omit the new keyword, the constructor function has to explicitly return a value.
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
Both calls to Bar return the same thing, a newly create object that has a property called method, which is a Closure.
It should also be noted that the call new Bar() does not affect the prototype of the returned object. While the prototype will be set on the newly created object, Bar never returns that new object.
In the above example, there is no functional difference between using and not using the new keyword.
Creating New Objects via Factories
It is often recommended to not use new because forgetting its use may lead to bugs.
In order to create a new object, one should rather use a factory and construct a new object inside of that factory.
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
While the above is robust against a missing new keyword and certainly makes the use of private variables easier, it comes with some downsides.
1. It uses more memory since the created objects do not share the methods on a prototype.
2. In order to inherit, the factory needs to copy all the methods from another object or put that object on the prototype of the new object.
3. Dropping the prototype chain just because of a left out new keyword is contrary to the spirit of the language.
In Conclusion
While omitting the new keyword might lead to bugs, it is certainly not a reason to drop the use of prototypes altogether. In the end it comes down to which solution is better suited for the needs of the application. It is especially important to choose a specific style of object creation and use it consistently.
Scopes and Namespaces
Although JavaScript deals fine with the syntax of two matching curly braces for blocks, it does not support block scope; hence, all that is left in the language is function scope.
function test() { // a scope
for(var i = 0; i < 10; i++) { // not a scope
// count
}
console.log(i); // 10
}
Note: When not used in an assignment, return statement or as a function argument, the{...} notation will get interpreted as a block statement and not as an object literal. This, in conjunction with automatic insertion of semicolons, can lead to subtle errors.
There are also no distinct namespaces in JavaScript, which means that everything gets defined in one globally shared namespace.
Each time a variable is referenced, JavaScript will traverse upwards through all the scopes until it finds it. In the case that it reaches the global scope and still has not found the requested name, it will raise a ReferenceError.
The Bane of Global Variables
// script A
foo = '42';
// script B
var foo = '42'
The above two scripts do not have the same effect. Script A defines a variable called foo in the global scope, and script B defines a foo in the currentscope.
Again, that is not at all the same effect: not using var can have major implications.
// global scope
var foo = 42;
function test() {
// local scope
foo = 21;
}
test();
foo; // 21
Leaving out the var statement inside the function test will override the value of foo. While this might not seem like a big deal at first, having thousands of lines of JavaScript and not using var will introduce horrible, hard-to-track-down bugs.
// global scope
var items = [/* some list */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// scope of subLoop
for(i = 0; i < 10; i++) { // missing var statement
// do amazing stuff!
}
}
The outer loop will terminate after the first call to subLoop, since subLoopoverwrites the global value of i. Using a var for the second for loop would have easily avoided this error. The var statement should never be left out unless the desired effect is to affect the outer scope.
Local Variables
The only source for local variables in JavaScript are function parameters and variables declared via the var statement.
// global scope
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// local scope of the function test
i = 5;
var foo = 3;
bar = 4;
}
test(10);
While foo and i are local variables inside the scope of the function test, the assignment of bar will override the global variable with the same name.
Hoisting
JavaScript hoists declarations. This means that both var statements andfunction declarations will be moved to the top of their enclosing scope.
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
The above code gets transformed before execution starts. JavaScript moves the var statements, as well as function declarations, to the top of the nearest surrounding scope.
// var statements got moved here
var bar, someValue; // default to 'undefined'
// the function declaration got moved up too
function test(data) {
var goo, i, e; // missing block scope moves these here
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // fails with a TypeError since bar is still 'undefined'
someValue = 42; // assignments are not affected by hoisting
bar = function() {};
test();
Missing block scoping will not only move var statements out of loops and their bodies, it will also make the results of certain if constructs non-intuitive.
In the original code, although the if statement seemed to modify the global variable goo, it actually modifies the local variable - after hoisting has been applied.
Without knowledge of hoisting, one might suspect the code below would raise a ReferenceError.
// check whether SomeImportantThing has been initialized
if (!SomeImportantThing) {
var SomeImportantThing = {};
}
But of course, this works due to the fact that the var statement is being moved to the top of the global scope.
var SomeImportantThing;
// other code might initialize SomeImportantThing here, or not
// make sure it's there
if (!SomeImportantThing) {
SomeImportantThing = {};
}
Name Resolution Order
All scopes in JavaScript, including the global scope, have the special namethis, defined in them, which refers to the current object.
Function scopes also have the name arguments, defined in them, which contains the arguments that were passed to the function.
For example, when trying to access a variable named foo inside the scope of a function, JavaScript will look up the name in the following order:
1. In case there is a var foo statement in the current scope, use that.
2. If one of the function parameters is named foo, use that.
3. If the function itself is called foo, use that.
4. Go to the next outer scope, and start with #1 again.
Note: Having a parameter called arguments will preventthe creation of the defaultarguments object.
Namespaces
A common problem associated with having only one global namespace is the likelihood of running into problems where variable names clash. In JavaScript, this problem can easily be avoided with the help of anonymous wrappers.
(function() {
// a self contained "namespace"
window.foo = function() {
// an exposed closure
};
})(); // execute the function immediately
Unnamed functions are considered expressions; so in order to be callable, they must first be evaluated.
( // evaluate the function inside the parentheses
function() {}
) // and return the function object
() // call the result of the evaluation
There are other ways to evaluate and directly call the function expression which, while different in syntax, behave the same way.
// A few other styles for directly invoking the
!function(){}()
+function(){}()
(function(){}());
// and so on...
In Conclusion
It is recommended to always use an anonymous wrapper to encapsulate code in its own namespace. This does not only protect code against name clashes, but it also allows for better modularization of programs.
Additionally, the use of global variables is considered bad practice. Any use of them indicates badly written code that is prone to errors and hard to maintain.
Arrays
Array Iteration and Properties
Although arrays in JavaScript are objects, there are no good reasons to use the for in loop. In fact, there are a number of good reasons against the use of for in on arrays.
Note: JavaScript arrays arenot associative arrays. JavaScript only has objects for mapping keys to values. And while associative arrayspreserve order, objects do not.
Because the for in loop enumerates all the properties that are on the prototype chain and because the only way to exclude those properties is to use hasOwnProperty, it is already up to twenty times slower than a normalfor loop.
Iteration
In order to achieve the best performance when iterating over arrays, it is best to use the classic for loop.
var list = [1, 2, 3, 4, 5, ...... 100000000];
for(var i = 0, l = list.length; i < l; i++) {
console.log(list[i]);
}
There is one extra catch in the above example, which is the caching of the length of the array via l = list.length.
Although the length property is defined on the array itself, there is still an overhead for doing the lookup on each iteration of the loop. And while recent JavaScript engines may apply optimization in this case, there is no way of telling whether the code will run on one of these newer engines or not.
In fact, leaving out the caching may result in the loop being only half as fastas with the cached length.
The length Property
While the getter of the length property simply returns the number of elements that are contained in the array, the setter can be used to truncatethe array.
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo.push(4);
foo; // [1, 2, 3, undefined, undefined, undefined, 4]
Assigning a smaller length truncates the array. Increasing it creates a sparse array.
In Conclusion
For the best performance, it is recommended to always use the plain for loop and cache the length property. The use of for in on an array is a sign of badly written code that is prone to bugs and bad performance.
The Array Constructor
Since the Array constructor is ambiguous in how it deals with its parameters, it is highly recommended to use the array literal - [] notation - when creating new arrays.
[1, 2, 3]; // Result: [1, 2, 3]
new Array(1, 2, 3); // Result: [1, 2, 3]
[3]; // Result: [3]
new Array(3); // Result: []
new Array('3') // Result: ['3']
In cases when there is only one argument passed to the Array constructor and when that argument is a Number, the constructor will return a newsparse array with the length property set to the value of the argument. It should be noted that only the length property of the new array will be set this way; the actual indexes of the array will not be initialized.
var arr = new Array(3);
arr[1]; // undefined
1 in arr; // false, the index was not set
Being able to set the length of the array in advance is only useful in a few cases, like repeating a string, in which it avoids the use of a loop.
new Array(count + 1).join(stringToRepeat);
In Conclusion
Literals are preferred to the Array constructor. They are shorter, have a clearer syntax, and increase code readability.
Types
Equality and Comparisons
JavaScript has two different ways of comparing the values of objects for equality.
The Equality Operator
The equality operator consists of two equal signs: ==
JavaScript features weak typing. This means that the equality operatorcoerces types in order to compare them.
"" == "0" // false
0 == "" // true
0 == "0" // true
false == "false" // false
false == "0" // true
false == undefined // false
false == null // false
null == undefined // true
" \t\r\n" == 0 // true
The above table shows the results of the type coercion, and it is the main reason why the use of == is widely regarded as bad practice. It introduces hard-to-track-down bugs due to its complicated conversion rules.
Additionally, there is also a performance impact when type coercion is in play; for example, a string has to be converted to a number before it can be compared to another number.
The Strict Equality Operator
The strict equality operator consists of three equal signs: ===.
It works like the normal equality operator, except that strict equality operator does not perform type coercion between its operands.
"" === "0" // false
0 === "" // false
0 === "0" // false
false === "false" // false
false === "0" // false
false === undefined // false
false === null // false
null === undefined // false
" \t\r\n" === 0 // false
The above results are a lot clearer and allow for early breakage of code. This hardens code to a certain degree and also gives performance improvements in case the operands are of different types.
Comparing Objects
While both == and === are called equality operators, they behave differently when at least one of their operands is an Object.
{} === {}; // false
new String('foo') === 'foo'; // false
new Number(10) === 10; // false
var foo = {};
foo === foo; // true
Here, both operators compare for identity and not equality; that is, they will compare for the same instance of the object, much like is in Python and pointer comparison in C.
In Conclusion
It is highly recommended to only use the strict equality operator. In cases where types need to be coerced, it should be done explicitly and not left to the language's complicated coercion rules.
The typeof Operator
The typeof operator (together with instanceof) is probably the biggest design flaw of JavaScript, as it is almost completely broken.
Although instanceof still has limited uses, typeof really has only one practical use case, which does not happen to be checking the type of an object.
Note: While typeof can also be called with a function like syntax, i.e. typeof(obj), this is not a function call. The parentheses behave as normal and the return value will be used as the operand of thetypeof operator. There is notypeof function.
The JavaScript Type Table
Value Class Type
-------------------------------------
"foo" String string
new String("foo") String object
1.2 Number number
new Number(1.2) Number object
true Boolean boolean
new Boolean(true) Boolean object
new Date() Date object
new Error() Error object
[1,2,3] Array object
new Array(1, 2, 3) Array object
new Function("") Function function
/abc/g RegExp object (function in Nitro/V8)
new RegExp("meow") RegExp object (function in Nitro/V8)
{} Object object
new Object() Object object
In the above table, Type refers to the value that the typeof operator returns. As can be clearly seen, this value is anything but consistent.
The Class refers to the value of the internal [[Class]] property of an object.
From the Specification: The value of [[Class]] can be one of the following strings.Arguments, Array, Boolean,Date, Error, Function,JSON, Math, Number, Object,RegExp, String.
In order to retrieve the value of [[Class]], one has to make use of thetoString method of Object.prototype.
The Class of an Object
The specification gives exactly one way of accessing the [[Class]] value, with the use of Object.prototype.toString.
function is(type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
}
is('String', 'test'); // true
is('String', new String('test')); // true
In the above example, Object.prototype.toString gets called with the value of this being set to the object whose [[Class]] value should be retrieved.
ES5 Note: For convenience the return value ofObject.prototype.toString for both null and undefined waschanged from Object toNull and Undefined in ECMAScript 5.
Testing for Undefined Variables
typeof foo !== 'undefined'
The above will check whether foo was actually declared or not; just referencing it would result in a ReferenceError. This is the only thingtypeof is actually useful for.
In Conclusion
In order to check the type of an object, it is highly recommended to useObject.prototype.toString because this is the only reliable way of doing so. As shown in the above type table, some return values of typeof are not defined in the specification; thus, they can differ between implementations.
Unless checking whether a variable is defined, typeof should be avoided.
The instanceof Operator
The instanceof operator compares the constructors of its two operands. It is only useful when comparing custom made objects. Used on built-in types, it is nearly as useless as the typeof operator.
Comparing Custom Objects
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
// This just sets Bar.prototype to the function object Foo,
// but not to an actual instance of Foo
Bar.prototype = Foo;
new Bar() instanceof Foo; // false
Using instanceof with Native Types
new String('foo') instanceof String; // true
new String('foo') instanceof Object; // true
'foo' instanceof String; // false
'foo' instanceof Object; // false
One important thing to note here is that instanceof does not work on objects that originate from different JavaScript contexts (e.g. different documents in a web browser), since their constructors will not be the exact same object.
In Conclusion
The instanceof operator should only be used when dealing with custom made objects that originate from the same JavaScript context. Just like thetypeof operator, every other use of it should be avoided.
Type Casting
JavaScript is a weakly typed language, so it will apply type coercionwherever possible.
// These are true
new Number(10) == 10; // Number.toString() is converted
// back to a number
10 == '10'; // Strings gets converted to Number
10 == '+10 '; // More string madness
10 == '010'; // And more
isNaN(null) == false; // null converts to 0
// which of course is not NaN
// These are false
10 == 010;
10 == '-10';
ES5 Note: Number literals that start with a 0 are interpreted as octal (Base 8). Octal support for these has beenremoved in ECMAScript 5 strict mode.
To avoid the issues above, use of the strict equal operator is highlyrecommended. Although this avoids a lot of common pitfalls, there are still many further issues that arise from JavaScript's weak typing system.
Constructors of Built-In Types
The constructors of the built in types like Number and String behave differently when being used with the new keyword and without it.
new Number(10) === 10; // False, Object and Number
Number(10) === 10; // True, Number and Number
new Number(10) + 0 === 10; // True, due to implicit conversion
Using a built-in type like Number as a constructor will create a new Numberobject, but leaving out the new keyword will make the Number function behave like a converter.
In addition, passing literals or non-object values will result in even more type coercion.
The best option is to cast to one of the three possible types explicitly.
Casting to a String
'' + 10 === '10'; // true
By prepending an empty string, a value can easily be cast to a string.
Casting to a Number
+'10' === 10; // true
Using the unary plus operator, it is possible to cast to a number.
Casting to a Boolean
By using the not operator twice, a value can be converted a boolean.
!!'foo'; // true
!!''; // false
!!'0'; // true
!!'1'; // true
!!'-1' // true
!!{}; // true
!!true; // true
Core
Why Not to Use eval
The eval function will execute a string of JavaScript code in the local scope.
var foo = 1;
function test() {
var foo = 2;
eval('foo = 3');
return foo;
}
test(); // 3
foo; // 1
However, eval only executes in the local scope when it is being called directlyand when the name of the called function is actually eval.
var foo = 1;
function test() {
var foo = 2;
var bar = eval;
bar('foo = 3');
return foo;
}
test(); // 2
foo; // 3
The use of eval should be avoided. 99.9% of its "uses" can be achievedwithout it.
eval in Disguise
The timeout functions setTimeout and setInterval can both take a string as their first argument. This string will always get executed in the global scope since eval is not being called directly in that case.
Security Issues
eval also is a security problem, because it executes any code given to it. It should never be used with strings of unknown or untrusted origins.
In Conclusion
eval should never be used. Any code that makes use of it should be questioned in its workings, performance and security. If something requireseval in order to work, it should not be used in the first place. A better designshould be used, that does not require the use of eval.
undefined and null
JavaScript has two distinct values for nothing, null and undefined, with the latter being more useful.
The Value undefined
undefined is a type with exactly one value: undefined.
The language also defines a global variable that has the value of undefined; this variable is also called undefined. However, this variable is neither a constant nor a keyword of the language. This means that its value can be easily overwritten.
ES5 Note: undefined in ECMAScript 5 is no longerwritable in strict mode, but its name can still be shadowed by for example a function with the name undefined.
Here are some examples of when the value undefined is returned:
Accessing the (unmodified) global variable undefined.
Accessing a declared but not yet initialized variable.
Implicit returns of functions due to missing return statements.
return statements that do not explicitly return anything.
Lookups of non-existent properties.
Function parameters that do not have any explicit value passed.
Anything that has been set to the value of undefined.
Any expression in the form of void(expression)
Handling Changes to the Value of undefined
Since the global variable undefined only holds a copy of the actual value ofundefined, assigning a new value to it does not change the value of the typeundefined.
Still, in order to compare something against the value of undefined, it is necessary to retrieve the value of undefined first.
To protect code against a possible overwritten undefined variable, a common technique used is to add an additional parameter to an anonymous wrapperthat gets no argument passed to it.
var undefined = 123;
(function(something, foo, undefined) {
// undefined in the local scope does
// now again refer to the value `undefined`
})('Hello World', 42);
Another way to achieve the same effect would be to use a declaration inside the wrapper.
var undefined = 123;
(function(something, foo) {
var undefined;
...
})('Hello World', 42);
The only difference here is that this version results in 4 more bytes being used in case it is minified, and there is no other var statement inside the anonymous wrapper.
Uses of null
While undefined in the context of the JavaScript language is mostly used in the sense of a traditional null, the actual null (both a literal and a type) is more or less just another data type.
It is used in some JavaScript internals (like declaring the end of the prototype chain by setting Foo.prototype = null), but in almost all cases, it can be replaced by undefined.
Automatic Semicolon Insertion
Although JavaScript has C style syntax, it does not enforce the use of semicolons in the source code, so it is possible to omit them.
JavaScript is not a semicolon-less language. In fact, it needs the semicolons in order to understand the source code. Therefore, the JavaScript parserautomatically inserts them whenever it encounters a parse error due to a missing semicolon.
var foo = function() {
} // parse error, semicolon expected
test()
Insertion happens, and the parser tries again.
var foo = function() {
}; // no error, parser continues
test()
The automatic insertion of semicolon is considered to be one of biggest design flaws in the language because it can change the behavior of code.
How it Works