• What will the code below output to the console and why?

    (function(){
      var a = b = 3;
    })();
    
    console.log("a defined? " + (typeof a !== 'undefined'));
    console.log("b defined? " + (typeof b !== 'undefined'));

    Since both a and b are defined within the enclosing scope of the function, and since the line they are on begins with the var keyword, most JavaScript developers would expect typeof a and typeof b to both be undefined in the above example.

    However, that is not the case. The issue here is that most developers incorrectly understand the statement var a = b = 3; to be shorthand for:

    var b = 3;
    var a = b;
    But in fact, var a = b = 3; is actually shorthand for:

    b = 3;
    var a = b;
    As a result (if you are not using strict mode), the output of the code snippet would be:

    a defined? false
    b defined? true
    But how can b be defined outside of the scope of the enclosing function? Well, since the statement var a = b = 3; is shorthand for the statements b = 3; and var a = b;, b ends up being a global variable (since it is not preceded by the var keyword) and is therefore still in scope even outside of the enclosing function.

    Note that, in strict mode (i.e., with use strict), the statement var a = b = 3; will generate a runtime error of ReferenceError: b is not defined, thereby avoiding any headfakes/bugs that might othewise result. (Yet another prime example of why you should use use strict as a matter of course in your code!)

  • What will the code below output to the console and why?

    var myObject = {
        foo: "bar",
        func: function() {
            var self = this;
            console.log("outer func:  this.foo = " + this.foo);
            console.log("outer func:  self.foo = " + self.foo);
            (function() {
                console.log("inner func:  this.foo = " + this.foo);
                console.log("inner func:  self.foo = " + self.foo);
            }());
        }
    };
    myObject.func();

    The above code will output the following to the console:

    outer func:  this.foo = bar
    outer func:  self.foo = bar
    inner func:  this.foo = undefined
    inner func:  self.foo = bar


    In the outer function, both this and self refer to myObject and therefore both can properly reference and access foo.

    In the inner function, though, this no longer refers to myObject. As a result, this.foo is undefined in the inner function, whereas the reference to the local variable self remains in scope and is accessible there. (Prior to ECMA 5, this in the inner function would refer to the global window object; whereas, as of ECMA 5, this in the inner function would be undefined.)

  • What is the significance of, and reason for, wrapping the entire content of a JavaScript source file in a function block?


    This is an increasingly common practice, employed by many popular JavaScript libraries (jQuery, Node.js, etc.). This technique creates a closure around the entire contents of the file which, perhaps most importantly, creates a private namespace and thereby helps avoid potential name clashes between different JavaScript modules and libraries.

    Another feature of this technique is to allow for an easily referenceable (presumably shorter) alias for a global variable. This is often used, for example, in jQuery plugins. jQuery allows you to disable the $ reference to the jQuery namespace, using jQuery.noConflict(). If this has been done, your code can still use $ employing this closure technique, as follows:

    (function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
  • What is the significance, and what are the benefits, of including 'use strict' at the beginning of a JavaScript source file?

    The short and most important answer here is that use strict is a way to voluntarily enforce stricter parsing and error handling on your JavaScript code at runtime. Code errors that would otherwise have been ignored or would have failed silently will now generate errors or throw exceptions. In general, it is a good practice.

    Some of the key benefits of strict mode include:
    1. Makes debugging easier. Code errors that would otherwise have been ignored or would have failed silently will now generate errors or throw exceptions, alerting you sooner to problems in your code and directing you more quickly to their source.
    2. Prevents accidental globals. Without strict mode, assigning a value to an undeclared variable automatically creates a global variable with that name. This is one of the most common errors in JavaScript. In strict mode, attempting to do so throws an error.
    3. Eliminates this coercion. Without strict mode, a reference to a this value of null or undefined is automatically coerced to the global. This can cause many headfakes and pull-out-your-hair kind of bugs. In strict mode, referencing a a this value of null or undefined throws an error.
    4. Disallows duplicate property names or parameter values. Strict mode throws an error when it detects a duplicate named property in an object (e.g., var object = {foo: "bar", foo: "baz"};) or a duplicate named argument for a function (e.g., function foo(val1, val2, val1){}), thereby catching what is almost certainly a bug in your code that you might otherwise have wasted lots of time tracking down.
    5. Makes eval() safer. There are some differences in the way eval() behaves in strict mode and in non-strict mode. Most significantly, in strict mode, variables and functions declared inside of an eval() statement are not created in the containing scope (they are created in the containing scope in non-strict mode, which can also be a common source of problems).
    6. Throws error on invalid usage of delete. The delete operator (used to remove properties from objects) cannot be used on non-configurable properties of the object. Non-strict code will fail silently when an attempt is made to delete a non-configurable property, whereas strict mode will throw an error in such a case.
  • Consider the two functions below. Will they both return the same thing? Why or why not?

    function foo1()
    {
      return {
          bar: "hello"
      };
    }
    
    function foo2()
    {
      return
      {
          bar: "hello"
      };
    }

    Surprisingly, these two functions will not return the same thing. Rather:

    console.log("foo1 returns:");
    console.log(foo1());
    console.log("foo2 returns:");
    console.log(foo2());


    will yield:

    foo1 returns:
    Object {bar: "hello"}
    foo2 returns:
    undefined 


    Not only is this surprising, but what makes this particularly gnarly is that foo2() returns undefined without any error being thrown.

    The reason for this has to do with the fact that semicolons are technically optional in JavaScript (although omitting them is generally really bad form). As a result, when the line containing the return statement (with nothing else on the line) is encountered in foo2(), a semicolon is automatically inserted immediately after the return statement.

    No error is thrown since the remainder of the code is perfectly valid, even though it doesn’t ever get invoked or do anything (it is simply an unused code block that defines a property bar which is equal to the string "hello").

    This behavior also argues for following the convention of placing an opening curly brace at the end of a line in JavaScript, rather than on the beginning of a new line. As shown here, this becomes more than just a stylistic preference in JavaScript.

  • What is NaN? What is its type? How can you reliably test if a value is equal to NaN?


    The NaN property represents a value that is “not a number”. This special value results from an operation that could not be performed either because one of the operands was non-numeric (e.g., "abc" / 4), or because the result of the operation is non-numeric (e.g., an attempt to divide by zero).

    While this seems straightforward enough, there are a couple of somewhat surprising characteristics of NaN that can result in hair-pulling bugs if one is not aware of them.

    For one thing, although NaN means “not a number”, its type is, believe it or not, Number:
    console.log(typeof NaN === "number");  // logs "true"

    Additionally, NaN compared to anything – even itself! – is false:
    console.log(NaN === NaN);  // logs "false"

    A semi-reliable way to test whether a number is equal to NaN is with the built-in function isNaN(), but even using isNaN() is an imperfect solution.

    A better solution would either be to use value !== value, which would only produce true if the value is equal to NaN. Also, ES6 offers a new Number.isNaN() function, which is a different and more reliable than the old global isNaN() function.

  • What will the code below output? Explain your answer.

    console.log(0.1 + 0.2);
    console.log(0.1 + 0.2 == 0.3);

    An educated answer to this question would simply be: “You can’t be sure. it might print out “0.3” and “true”, or it might not. Numbers in JavaScript are all treated with floating point precision, and as such, may not always yield the expected results.”

    The example provided above is classic case that demonstrates this issue. Surprisingly, it will print out:
    0.30000000000000004
    false

     

  • In what order will the numbers 1-4 be logged to the console when the code below is executed? Why?

    (function() {
        console.log(1);
        setTimeout(function(){console.log(2)}, 1000);
        setTimeout(function(){console.log(3)}, 0);
        console.log(4);
    })();

    The values will be logged in the following order:

    1
    4
    3
    2
    Let’s first explain the parts of this that are presumably more obvious:

    1 and 4 are displayed first since they are logged by simple calls to console.log() without any delay
    2 is displayed after 3 because 2 is being logged after a delay of 1000 msecs (i.e., 1 second) whereas 3 is being logged after a delay of 0 msecs.
    OK, fine. But if 3 is being logged after a delay of 0 msecs, doesn’t that mean that it is being logged right away? And, if so, shouldn’t it be logged before 4, since 4 is being logged by a later line of code?

    The answer has to do with properly understanding JavaScript events and timing.

    The browser has an event loop which checks the event queue and processes pending events. For example, if an event happens in the background (e.g., a script onload event) while the browser is busy (e.g., processing an onclick), the event gets appended to the queue. When the onclick handler is complete, the queue is checked and the event is then handled (e.g., the onload script is executed).

    Similarly, setTimeout() also puts execution of its referenced function into the event queue if the browser is busy.

    When a value of zero is passed as the second argument to setTimeout(), it attempts to execute the specified function “as soon as possible”. Specifically, execution of the function is placed on the event queue to occur on the next timer tick. Note, though, that this is not immediate; the function is not executed until the next tick. That’s why in the above example, the call to console.log(4) occurs before the call to console.log(3) (since the call to console.log(3) is invoked via setTimeout, so it is slightly delayed).

 

  •  Write a sum method which will work properly when invoked using either syntax below.

    console.log(sum(2,3));   // Outputs 5
    console.log(sum(2)(3));  // Outputs 5
    

    There are (at least) two ways to do this:

    METHOD 1

    function sum(x) {
      if (arguments.length == 2) {
        return arguments[0] + arguments[1];
      } else {
        return function(y) { return x + y; };
      }
    }

    In JavaScript, functions provide access to an arguments object which provides access to the actual arguments passed to a function. This enables us to use the length property to determine at runtime the number of arguments passed to the function.

    If two arguments are passed, we simply add them together and return.

    Otherwise, we assume it was called in the form sum(2)(3), so we return an anonymous function that adds together the argument passed to sum() (in this case 2) and the argument passed to the anonymous function (in this case 3).

    METHOD 2

    function sum(x, y) {
      if (y !== undefined) {
        return x + y;
      } else {
        return function(y) { return x + y; };
      }
    }

    When a function is invoked, JavaScript does not require the number of arguments to match the number of arguments in the function definition. If the number of arguments passed exceeds the number of arguments in the function definition, the excess arguments will simply be ignored. On the other hand, if the number of arguments passed is less than the number of arguments in the function definition, the missing arguments will have a value of undefined when referenced within the function. So, in the above example, by simply checking if the 2nd argument is undefined, we can determine which way the function was invoked and proceed accordingly.

 

  •  Consider the following code snippet:


    for (var i = 0; i < 5; i++) {
      var btn = document.createElement('button');
      btn.appendChild(document.createTextNode('Button ' + i));
      btn.addEventListener('click', function(){ console.log(i); });
      document.body.appendChild(btn);
    }

    (a) What gets logged to the console when the user clicks on “Button 4” and why?

    (b) Provide one or more alternate implementations that will work as expected.


    (a) No matter what button the user clicks the number 5 will always be logged to the console. This is because, at the point that the onclick method is invoked (for any of the buttons), the for loop has already completed and the variable i already has a value of 5. (Bonus points for the interviewee if they know enough to talk about how execution contexts, variable objects, activation objects, and the internal “scope” property contribute to the closure behavior.)

    (b) The key to making this work is to capture the value of i at each pass through the for loop by passing it into a newly created function object. Here are three possible ways to accomplish this:

    for (var i = 0; i < 5; i++) {
      var btn = document.createElement('button');
      btn.appendChild(document.createTextNode('Button ' + i));
      btn.addEventListener('click', (function(i) {
        return function() { console.log(i); };
      })(i));
      document.body.appendChild(btn);
    }
    Alternatively, you could wrap the entire call to btn.addEventListener in the new anonymous function:

    for (var i = 0; i < 5; i++) {
      var btn = document.createElement('button');
      btn.appendChild(document.createTextNode('Button ' + i));
      (function (i) {
        btn.addEventListener('click', function() { console.log(i); });
      })(i);
      document.body.appendChild(btn);
    }
    Or, we could replace the for loop with a call to the array object’s native forEach method:

    ['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
      var btn = document.createElement('button');
      btn.appendChild(document.createTextNode('Button ' + i));
      btn.addEventListener('click', function() { console.log(i); });
      document.body.appendChild(btn);
    });
  • What will the code below output to the console and why?


    var arr1 = "john".split('');
    var arr2 = arr1.reverse();
    var arr3 = "jones".split('');
    arr2.push(arr3);
    console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
    console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));


    The logged output will be:
    "array 1: length=5 last=j,o,n,e,s"
    "array 2: length=5 last=j,o,n,e,s"

    arr1 and arr2 are the same after the above code is executed for the following reasons:

    Calling an array object’s reverse() method doesn’t only return the array in reverse order, it also reverses the order of the array itself (i.e., in this case, arr1).
    The reverse() method returns a reference to the array itself (i.e., in this case, arr1). As a result, arr2 is simply a reference to (rather than a copy of) arr1. Therefore, when anything is done to arr2 (i.e., when we invoke arr2.push(arr3);), arr1 will be affected as well since arr1 and arr2 are simply references to the same object.
    And a couple of side points here that can sometimes trip someone up in answering this question:

    Passing an array to the push() method of another array pushes that entire array as a single element onto the end of the array. As a result, the statement arr2.push(arr3); adds arr3 in its entirety as a single element to the end of arr2 (i.e., it does not concatenate the two arrays, that’s what the concat() method is for).
    Like Python, JavaScript honors negative subscripts in calls to array methods like slice() as a way of referencing elements at the end of the array; e.g., a subscript of -1 indicates the last element in the array, and so on.
  • What will the code below output to the console and why ?


    console.log(1 +  "2" + "2");
    console.log(1 +  +"2" + "2");
    console.log(1 +  -"1" + "2");
    console.log(+"1" +  "1" + "2");
    console.log( "A" - "B" + "2");
    console.log( "A" - "B" + 2);


    The above code will output the following to the console:

    "122"
    "32"
    "02"
    "112"
    "NaN2"
    NaN

    Here’s why…

    The fundamental issue here is that JavaScript (ECMAScript) is a loosely typed language and it performs automatic type conversion on values to accommodate the operation being performed. Let’s see how this plays out with each of the above examples.

    Example 1: 1 + "2" + "2" Outputs: "122" Explanation: The first operation to be performed in 1 + "2". Since one of the operands ("2") is a string, JavaScript assumes it needs to perform string concatenation and therefore converts the type of 1 to "1", 1 + "2" yields "12". Then, "12" + "2" yields "122".
  • The following recursive code will cause a stack overflow if the array list is too large. How can you fix this and still retain the recursive pattern?


    var list = readHugeList();
    
    var nextListItem = function() {
        var item = list.pop();
    
        if (item) {
            // process the list item...
            nextListItem();
        }
    };


    The potential stack overflow can be avoided by modifying the nextListItem function as follows:
    var list = readHugeList();
    
    var nextListItem = function() {
        var item = list.pop();
    
        if (item) {
            // process the list item...
            setTimeout( nextListItem, 0);
        }
    };

    The stack overflow is eliminated because the event loop handles the recursion, not the call stack. When nextListItem runs, if item is not null, the timeout function (nextListItem) is pushed to the event queue and the function exits, thereby leaving the call stack clear. When the event queue runs its timed-out event, the next item is processed and a timer is set to again invoke nextListItem. Accordingly, the method is processed from start to finish without a direct recursive call, so the call stack remains clear, regardless of the number of iterations.

 

  • What is a “closure” in JavaScript? Provide an example.


    A closure is an inner function that has access to the variables in the outer (enclosing) function’s scope chain. The closure has access to variables in three scopes; specifically: (1) variable in its own scope, (2) variables in the enclosing function’s scope, and (3) global variables.

    Here is a simple example:

    var globalVar = "xyz";
    
    (function outerFunc(outerArg) {
      var outerVar = 'a';
     
      (function innerFunc(innerArg) {
        var innerVar = 'b';
        
        console.log(
          "outerArg = " + outerArg + "\n" +
          "innerArg = " + innerArg + "\n" +
          "outerVar = " + outerVar + "\n" +
          "innerVar = " + innerVar + "\n" +
          "globalVar = " + globalVar);
        
      })(456);
    })(123);

    In the above example, variables from innerFunc, outerFunc, and the global namespace are all in scope in the innerFunc. The above code will therefore produce the following output:
    outerArg = 123
    innerArg = 456
    outerVar = a
    innerVar = b
    globalVar = xyz
  • What would the following lines of code output to the console?

    console.log("0 || 1 = "+(0 || 1));
    console.log("1 || 2 = "+(1 || 2));
    console.log("0 && 1 = "+(0 && 1));
    console.log("1 && 2 = "+(1 && 2));

    Explain your answer.

    The code will output the following four lines:

    0 || 1 = 1
    1 || 2 = 1
    0 && 1 = 0
    1 && 2 = 2

    In JavaScript, both || and && are logical operators that return the first fully-determined “logical value” when evaluated from left to right.

    The or (||) operator. In an expression of the form X||Y, X is first evaluated and interpreted as a boolean value. If this boolean value is true, then true (1) is returned and Y is not evaluated, since the “or” condition has already been satisfied. If this boolean value is “false”, though, we still don’t know if X||Y is true or false until we evaluate Y, and interpret it as a boolean value as well.

    Accordingly, 0 || 1 evaluates to true (1), as does 1 || 2.

    The and (&&) operator. In an expression of the form X&&Y, X is first evaluated and interpreted as a boolean value. If this boolean value is false, then false (0) is returned and Y is not evaluated, since the “and” condition has already failed. If this boolean value is “true”, though, we still don’t know if X&&Y is true or false until we evaluate Y, and interpret it as a boolean value as well.

    However, the interesting thing with the && operator is that when an expression is evaluated as “true”, then the expression itself is returned. This is fine, since it counts as “true” in logical expressions, but also can be used to return that value when you care to do so. This explains why, somewhat surprisingly, 1 && 2 returns 2 (whereas you might it expect it to return true or 1).
  • What is the output out of the following code? Explain your answer.


    var a={},
        b={key:'b'},
        c={key:'c'};
    
    a[b]=123;
    a[c]=456;
    
    console.log(a[b]);

    The output of this code will be 456 (not 123).

    The reason for this is as follows: When setting an object property, JavaScript will implicitly stringify the parameter value. In this case, since b and c are both objects, they will both be converted to "[object Object]". As a result, a[b] and a[c] are both equivalent to a["[object Object]"] and can be used interchangeably. Therefore, setting or referencing a[c] is precisely the same as setting or referencing a[b].

 

  • Consider the code snippet below (source). What will the alert display? Explain your answer.


    var foo = new Object();
    var bar = new Object();
    var map = new Object();
    
    map[foo] = "foo";
    map[bar] = "bar";
    
    alert(map[foo]);
    what will this display?

    It is the rare candidate who will correctly answer that this alerts the string “bar”. Most will mistakenly answer that it alerts the string “foo”. So let’s understand why “bar” is indeed the correct, albeit surprising, answer…

    As mentioned in the answer to the prior question, a JavaScript object is essentially a hashtable of name-value pairs where the names (i.e., keys) are strings. And they are always strings. In fact, when an object other than a string is used as a key in JavaScript, no error occurs; rather, JavaScript silently converts it to a string and uses that value as the key instead. This can have surprising results, as the above code demonstrates.

    To understand the above code snippet, one must first recognize that the map object shown does not map the object foo to the string “foo”, nor does it map the object bar to the string “bar”. Since the objects foo and bar are not strings, when they are used as keys for map, JavaScript automatically converts the key values to strings using each object’s toString() method. And since neither foo nor bar defines its own custom toString() method, they both use the same default implementation. That implementation simply generates the literal string “[object Object]” when it is invoked. With this explanation in mind, let’s re-examine the code snippet above, but this time with explanatory comments along the way:

    var foo = new Object();
    var bar = new Object();
    var map = new Object();
    
    map[foo] = "foo";    // --> map["[Object object]"] = "foo";
    map[bar] = "bar";    // --> map["[Object object]"] = "bar";
                         // NOTE: second mapping REPLACES first mapping!
    
    alert(map[foo]);     // --> alert(map["[Object object]"]);
                         // and since map["[Object object]"] = "bar",
                         // this will alert "bar", not "foo"!!
  • What will be the output of the code below?


    var Employee = {
      company: 'xyz'
    }
    var emp1 = Object.create(Employee);
    delete emp1.company
    console.log(emp1.company);

    The output would be xyz. Here, emp1 object has company as its prototype property. The delete operator doesn't delete prototype property.

    emp1 object doesn't have company as its own property. You can test it console.log(emp1.hasOwnProperty('company')); //output : false. However, we can delete the company property directly from the Employee object using delete Employee.company. Or, we can also delete the emp1 object using the __proto__ property delete emp1.__proto__.company.
  • What will be the output of the code below?


    var bar = true;
    console.log(bar + 0);   
    console.log(bar + "xyz");  
    console.log(bar + true);  
    console.log(bar + false);   

    The code will output
    1
    "truexyz"
    2
    1

    Here's a general guideline for addition operators:

    Number + Number -> Addition
    Boolean + Number -> Addition
    Boolean + Number -> Addition
    Number + String -> Concatenation
    String + Boolean -> Concatenation
    String + String -> Concatenation 

 

  • What will be the output of the code below?

    var z = 1, y = z = typeof y;
    console.log(y);  

    The output would be undefined. According to the associativity rule, operators with the same precedence are processed based on the associativity property of the operator. Here, the associativity of the assignment operator is Right to Left, so typeof y will evaluate first , which is undefined. It will be assigned to z, and then y would be assigned the value of z and then z would be assigned the value 1.

 

  • What will be the output of the code below?

    // NFE (Named Function Expression
     var foo = function bar(){ return 12; };
     typeof bar();  

    The output would be Reference Error. To make the code above work, you can re-write it as follows:

    Sample 1

     var bar = function(){ return 12; };
     typeof bar(); 
     
    or

    Sample 2
     
    function bar(){ return 12; };
     typeof bar();  

    A function definition can have only one reference variable as its function name. In sample 1, bar's reference variable points to anonymous function. In sample 2, the function's definition is the name function.

     var foo = function bar(){
        // foo is visible here
        // bar is visible here
         console.log(typeof bar()); // Work here :)
     };
     // foo is visible here
     // bar is undefined here

 

  • Can you name two programming paradigms important for JavaScript app developers?

    JavaScript is a multi-paradigm language, supporting imperative/procedural programming along with OOP (Object-Oriented Programming) and functional programming. JavaScript supports OOP with prototypal inheritance.

    Good to hear:

    Prototypal inheritance (also: prototypes, OLOO).
    Functional programming (also: closures, first class functions, lambdas).

  •  What is functional programming?

    Functional programming produces programs by composing mathematical functions and avoids shared state & mutable data. Lisp (specified in 1958) was among the first languages to support functional programming, and was heavily inspired by lambda calculus. Lisp and many Lisp family languages are still in common use today.
    Functional programming is an essential concept in JavaScript (one of the two pillars of JavaScript). Several common functional utilities were added to JavaScript in ES5.

    Good to hear:

    • Pure functions / function purity.
    • Avoid side-effects.
    • Simple function composition.
    • Examples of functional languages: Lisp, ML, Haskell, Erlang, Clojure, Elm, F Sharp, OCaml, etc…
    • Mention of features that support FP: first-class functions, higher order functions, functions as arguments/values.
  • What is the difference between classical inheritance and prototypal inheritance?

    Class Inheritance: instances inherit from classes (like a blueprint — a description of the class), and create sub-class relationships: hierarchical class taxonomies. Instances are typically instantiated via constructor functions with the `new` keyword. Class inheritance may or may not use the `class` keyword from ES6.

    Prototypal Inheritance: instances inherit directly from other objects. Instances are typically instantiated via factory functions or `Object.create()`. Instances may be composed from many different objects, allowing for easy selective inheritance.
    In JavaScript, prototypal inheritance is simpler & more flexible than class inheritance.

    Good to hear:

    Classes: create tight coupling or hierarchies/taxonomies.
    Prototypes: mentions of concatenative inheritance, prototype delegation, functional inheritance, object composition.
  •  What does “favor object composition over class inheritance” mean?

    This is a quote from “Design Patterns: Elements of Reusable Object-Oriented Software”. It means that code reuse should be achieved by assembling smaller units of functionality into new objects instead of inheriting from classes and creating object taxonomies.
    In other words, use can-do, has-a, or uses-a relationships instead of is-a relationships.
  • What are two-way data binding and one-way data flow, and how are they different?

    Two way data binding means that UI fields are bound to model data dynamically such that when a UI field changes, the model data changes with it and vice-versa.
    One way data flow means that the model is the single source of truth. Changes in the UI trigger messages that signal user intent to the model (or “store” in React). Only the model has the access to change the app’s state. The effect is that data always flows in a single direction, which makes it easier to understand.
    One way data flows are deterministic, whereas two-way binding can cause side-effects which are harder to follow and understand.

    Good to hear:

    • React is the new canonical example of one-way data flow, so mentions of React are a good signal.
    • Cycle.js is another popular implementation of uni-directional data flow.
    • Angular is a popular framework which uses two-way binding.

 

  • What are the pros and cons of monolithic vs microservice architectures?

    A monolithic architecture means that your app is written as one cohesive unit of code whose components are designed to work together, sharing the same memory space and resources.

    A microservice architecture means that your app is made up of lots of smaller, independent applications capable of running in their own memory space and scaling independently from each other across potentially many separate machines.

    Monolithic Pros: The major advantage of the monolithic architecture is that most apps typically have a large number of cross-cutting concerns, such as logging, rate limiting, and security features such audit trails and DOS protection.

    When everything is running through the same app, it’s easy to hook up components to those cross-cutting concerns.
    There can also be performance advantages, since shared-memory access is faster than inter-process communication (IPC).

    Monolithic cons: Monolithic app services tend to get tightly coupled and entangled as the application evolves, making it difficult to isolate services for purposes such as independent scaling or code maintainability.

    Monolithic architectures are also much harder to understand, because there may be dependencies, side-effects, and magic which are not obvious when you’re looking at a particular service or controller.

    Microservice pros: Microservice architectures are typically better organized, since each microservice has a very specific job, and is not concerned with the jobs of other components. Decoupled services are also easier to recompose and reconfigure to serve the purposes of different apps (for example, serving both the web clients and public API).

    They can also have performance advantages depending on how they’re organized because it’s possible to isolate hot services and scale them independent of the rest of the app.

    Microservice cons: As you’re building a new microservice architecture, you’re likely to discover lots of cross-cutting concerns that you did not anticipate at design time. A monolithic app could establish shared magic helpers or middleware to handle such cross-cutting concerns without much effort.

    In a microservice architecture, you’ll either need to incur the overhead of separate modules for each cross-cutting concern, or encapsulate cross-cutting concerns in another service layer that all traffic gets routed through.

    Eventually, even monolthic architectures tend to route traffic through an outer service layer for cross-cutting concerns, but with a monolithic architecture, it’s possible to delay the cost of that work until the project is much more mature.
    Microservices are frequently deployed on their own virtual machines or containers, causing a proliferation of VM wrangling work. These tasks are frequently automated with container fleet management tools.

    Good to hear:

    • Positive attitudes toward microservices, despite the higher initial cost vs monolthic apps.
    • Aware that microservices tend to perform and scale better in the long run.
    • Practical about microservices vs monolithic apps. Structure the app so that services are independent from each other at the code level, but easy to bundle together as a monolithic app in the beginning. Microservice overhead costs can be delayed until it becomes more practical to pay the price.
  • Are classical inheritance and prototypal inheritance really the same thing, just a stylistic preference?

    No.

    Classical and prototypal inheritance are fundamentally and semantically distinct.

    There are some defining characteristics between classical inheritance and prototypal inheritance. For any of this article to make sense, you must keep these points in mind:

    In class inheritance, instances inherit from a blueprint (the class), and create sub-class relationships. In other words, you can’t use the class like you would use an instance. You can’t invoke instance methods on a class definition itself. You must first create an instance and then invoke methods on that instance.

    In prototypal inheritance, instances inherit from other instances. Using delegate prototypes (setting the prototype of one instance to refer to an examplar object), it’s literally Objects Linking to Other Objects, or OLOO, as Kyle Simpson calls it. Using concatenative inheritance, you just copy properties from an exemplar object to a new instance.

    It’s really important that you understand these differences. Class inheritance by virtue of its mechanisms create class hierarchies as a side-effect of sub-class creation. Those hierarchies lead to arthritic code (hard to change) and brittleness (easy to break due to rippling side-effects when you modify base classes).

    Prototypal inheritance does not necessarily create similar hierarchies. I recommend that you keep prototype chains as shallow as possible. It’s easy to flatten many prototypes together to form a single delegate prototype.

 

  •  What is memoization? Give an example of its usage in JavaScript.

    Memoization is a programming technique which attempts to increase a function’s performance by caching its previously computed results. Because JavaScript objects behave like associative arrays, they are ideal candidates to act as caches. Each time a memoized function is called, its parameters are used to index the cache. If the data is present, then it can be returned, without executing the entire function.  However, if the data is not cached, then the function is executed, and the result is added to the cache.In the following example, the original Fibonacci function is rewritten to include memoization.

    In the example, a self-executing anonymous function returns an inner function, f(), which is used as the Fibonacci function. When f() is returned, its closure allows it to continue to access the “memo” object, which stores all of its previous results. Each time f() is executed, it first checks to see if a result exists for the current value of “n”. If it does, then the cached value is returned. Otherwise, the original Fibonacci code is executed. Note that “memo” is defined outside of f() so that it can retain its value over multiple function calls. Learn more about memoization at the webpage link given at the bottom.

    var fibonacci = (function() {
      var memo = {};
    
      function f(n) {
        var value;
    
        if (n in memo) {
          value = memo[n];
        } else {
          if (n === 0 || n === 1)
            value = n;
          else
            value = f(n - 1) + f(n - 2);
    
          memo[n] = value;
        }
    
        return value;
      }
    
      return f;
    })();

     

  • What is the difference between throttling and debouncing?


    Throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."
    Say under normal circumstances you would call this function 1,000 times over 10 seconds. If you throttle it to only once per 100 milliseconds, it would only execute that function at most 100 times

    (10s * 1,000) = 10,000ms
    10,000ms / 100ms throttling = 100 maximum calls

    #Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called."
    Perhaps a function is called 1,000 times in a quick burst, dispersed over 3 seconds, then stops being called. If you have debounced it at 100 milliseconds,


    the function will only fire once, at 3.1 seconds, once the burst is over. Each time the function is called during the burst it resets the debouncing timer. 

    Examples:

    Throttle

    $("body").on('scroll', _.throttle(function() {
    // Do expensive things
    }, 100));


     Debounce: 

    $(window).on('resize', _.debounce(function() {
      // Do expensive things
    }, 100));

Source:

Toptal

JavaScript Vital Questions

10 Interview Questions Every Javascript Developer Should Know

Implementing Memoization in JavaScript