Javascript Technical Interview Questions Prepare Yourself for That Dream Job
Total Page:16
File Type:pdf, Size:1020Kb
Javascript Technical Interview Questions Prepare Yourself For That Dream Job Xuanyi Chew This book is for sale at http://leanpub.com/jsinterviewquestions This version was published on 2014-03-19 This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do. ©2014 Xuanyi Chew Contents Sample of Underhanded JavaScript ............................. 1 Introduction .......................................... 2 On IIFE Behaviour ...................................... 3 The Explanation ....................................... 4 Exceptions .......................................... 5 During The Interview .................................... 6 On The Comma Operator ................................... 7 The Explanation ....................................... 7 Miscelleny .......................................... 9 Practice Questions ...................................... 10 During The Interview .................................... 11 On Object Coercion ...................................... 12 The Explanation ....................................... 13 Practice Questions ...................................... 15 During The Interview .................................... 16 Sample of Underhanded JavaScript Hi there! This is the sample of Underhanded JavaScript: How to Be A Complete Arsehole With Bad JavaScript. Included are three chapters (there are 12 more in the proper book when published). I hope you have fun reading it, and eventually, do buy it. Introduction The motivation for me to write this book was originally boredom. I was joking with my colleagues at my startup that JavaScript has so many pitfalls that I could write a book on it. I was then egged on to actually write it, and so I started drafting a series of blog posts when Hacker News went down and I had no other entertainment. Eventually, it became obvious that a collection of JavaScript quirks would be better suited in a book format than a blog post. At the same time, I was doing a few friends a favour by helping them out with interviewing a few candidates for a JavaScript position. I found that the candidates were not able to answer questions derived from the first three chapters of the book. To be fair, I did ask rather obscure questions about JavaScript and I didn’t expect the candidates to do well, for even the most experienced of JavaScript developers fall for some of these. For example: Do you know why "true" == true will return false? What really happens behind the scenes when variables are “hoisted”? Why do numbers in JavaScript act so strangely? This book aims to help potential candidates on their job interview. I do so by debunk the strangeness, and make it not strange. JavaScript doesn’t have quirks once you understand the language. And it is only with mastery of the language will a technical interview go over smoothly. In the book, I also suggest ways to further a conversation in an interview. I had fun writing this book. And I sincerely hope that it will, to some extent help you in getting that dream job of yours. On IIFE Behaviour Immediately Invoked Functions, in particular Immediately Invoked Function Expressions¹ are a commonly used Javascript design pattern. An IIFE is basically where a function is defined, and then immediately called. It is quite useful in compressing code and reducing scope lookups for more effective execution of code. Many people also use IIFEs to prevent pollution of the global namespace. If function foo() is ever going to be called once, and it isn’t ever going to be referenced again, why add foo to the global object? Another oft-quoted reason is for scope privacy. This is especially true in the case of Javascript libraries. By using IIFEs, the variables are all contained within the library’s local scope. Question: What do these return? Example 1 function foo(x) { console.log(arguments) return x } foo(1, 2, 3, 4, 5) Example 2 function foo(x) { console.log(arguments) return x }(1, 2, 3, 4, 5) Example 3 (function foo(x) { console.log(arguments) return x })(1, 2, 3, 4, 5) Answer: ¹http://benalman.com/news/2010/11/immediately-invoked-function-expression/ On IIFE Behaviour 4 Example 1 Example 2 Example 3 [1, 2, 3, 4, 5] 5 [1, 2, 3, 4, 5] 1 1 Why? The Explanation The reason why Example 2 returned 5 is because the function definition is actually a type of func- tion definition called a FunctionDeclaration. The ECMAScript specification discriminates be- tween two different kinds of function definitions: FunctionDeclaration, and FunctionExpression. Both are function definitions, and a FunctionDeclaration and a FunctionExpression can both have the same syntax. However, how a function definition is parsed as a FunctionDeclaration depends on a few things. Firstly, the function definition must be named - anonymous functions are not FunctionDeclarations. The function definitions in all three examples above has a name each. Check. Secondly, the function definition cannot be part of an expression. This means that the function definition in a = function a(){} is a FunctionExpression and not a FunctionDeclaration because it’s part of an assignment expression. In Example 3, the addition of the parenthesis before the function definition makes it part of an expression, specifically a grouping expression. However, both Example 1 and 2 are function definitions that stand on their own. Thirdly, FunctionDeclarations cannot be nested inside non-function blocks². In V8/Chrome, the function definitions in Examples 1-3 are actually wrapped in a global function block. This is invisible to the end user, and it’s only when one uses V8’s AST output (d8 --print-ast is the command), one sees the global wrapping block. These are defined in §13 of the ECMAScript 5.1 specification³, for those who are interested in reading more. However the specification isn’t very clear on the latter about FunctionExpression, and much of it was intuited from reading V8’s source code. Another factor in play is the role of scopes. Scopes in Javascript are called Environments. There are different kinds of Environments⁴, but for simplicity, let’s just treat them all the same. Environments hold all the variables defined in it. All code belong to an environment or another. If Example 1 is all there is to the code (i.e. foo() is not defined inside another function or object), then the environment that foo() belongs to is the environment of the global object (in most browsers, this would be window). So, when the code is parsed, the definition of foo() in both Example 1 and 2 is treated as a FunctionDeclaration. What this means is that the function definition is “hoisted” to the top. This makes foo() a property of the global object. ²Not really, as we will see in a later chapter of this book. ³http://www.ecma-international.org/ecma-262/5.1/#sec-13 ⁴There are two kinds of environments: Lexical Environments and Variable Environments. In general, Variable Environments is used to manage variable use and scope at execution time, while Lexical Environments are used at definition time. This is also commonly known in various other forms in other languages as static scoping. We’re only concerned about the Lexical Environment. Lexical environments can be made of two different types of EnvironmentRecord: DeclarativeEnvironmentRecord and ObjectEnvironmentRecord. For the most part, most code languish in the DeclarativeEnvironmentRecord. The Global Object has its own ObjectEnvironmentRecord though, and when variables are “hoisted”, they are hoisted into the Global Object’s ObjectEnvironmentRecord On IIFE Behaviour 5 Why does this matter? Because when the code in Example 2 is parsed, it’s essentially being parsed as if you did this: function foo(x){ // code }; (1, 2, 3, 4, 5) // group expression Chrome and Firefox will happily parse it as a function definition followed by a group expression, which returns 5 in the above case. So, the solution is to make your function definition as an expression (hence the term Immediately Invoked Function Expression). Expressions are evaluated and the results are output, then promptly thrown away (or its values are assigned to a variable). So you’d get a ReferenceError if you did this in Chrome: Example 4 (function foo(x) { console.log(arguments) return x }(1, 2, 3, 4)) foo() // this throws ReferenceError It is precisely this behaviour which makes IIFEs so well-loved. An IIFE protects the variables inside it, and provides privacy from the external environments. An IIFE cannot have its name shared with another function declaration, and hence name collisions will not happen - it cannot be accidentally called. This is what is meant by preventing pollution of the global namespace. Keen-eyed readers will note that Example 4 above is slightly different from Example 3 and wonder why. Example 4 uses the syntax that Douglas Crockford recommends and therefore more commonly seen in the wild. However, Example 3 is still correct, and does make clear the structural difference of a FunctionExpression. Exceptions The Node.js REPL console actually treats Example 2 as a FunctionExpression by wrapping it in parentheses. In fact, if you did call foo after executing that, a ReferenceError will be thrown. When executing a script in the file, however, Node.js will not treat Example 2 as a FuncionExpression. In fact nothing will be returned if you had put Example 2 in a file and run using node foo.js, exactly because there is nothing to be returned. This is also the reason why after a function has been defined in Node.js REPL, it can be invoked thusly: foo)(. This however, has been fixed in the latest version of Node.js In repl.js⁵, the following can be found: ⁵https://github.com/joyent/node/blob/master/lib/repl.js On IIFE Behaviour 6 Node.js’ REPL source code snippet, accurate as at 7th January 2014 262 if (!skipCatchall) { 263 var evalCmd = self.bufferedCommand + cmd; 264 if (/^\s*\{/.test(evalCmd) && /\}\s*$/.test(evalCmd)) { 265 // It's confusing for `{ a : 1 }` to be interpreted as a block 266 // statement rather than an object literal.