paint-brush
10 Essential Javascript Questionsby@Kiran
1,203 reads
1,203 reads

10 Essential Javascript Questions

by KiranAugust 22nd, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

JavaScript is a client-side programming language used in over 90% of websites. It is one of the most used programming languages in the world. Today we will be talking about the 10 most asked questions about JavaScript. The answers to these questions include how to remove a specific item from an array using splice() and how to redirect the user from one page to another using jQuery or pure JavaScript. JavaScript maintains a reference to its outer lexical environment (lexical environment) and a map between identifiers (ie a local variable) and values.

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - 10 Essential Javascript Questions
Kiran HackerNoon profile picture

JavaScript is a client-side programming language. It is used in over 90% of the websites in the world. It is one of the most used programming languages in the world. So, today we will be talking about the 10 most
asked questions about JavaScript.

10 Most Asked Questions About JavaScript

1. How to remove a specific item from an array?

Answer:

First, find the

index
of the array element you want to remove using
indexOf
, and then remove that index with
splice
.

The splice() method changes the contents of an array by removing existing elements and/or adding new elements.

const array = [2, 5, 9];

console.log(array);

const index = array.indexOf(5);
if (index > -1) {
  array.splice(index, 1);
}

// array = [2, 9]
console.log(array); 

The second parameter of

splice
is the number of elements to remove. Note that
splice
modifies the array in place and returns a new array containing the elements that have been removed.

For the reason of completeness, here are functions. The first function removes only a single occurrence (i.e. removing the first match of

5
from
[2,5,9,1,5,8,5]
), while the second function removes all occurrences:

function removeItemOnce(arr, value) { 
    var index = arr.indexOf(value);
    if (index > -1) {
        arr.splice(index, 1);
    }
    return arr;
}

function removeItemAll(arr, value) {
    var i = 0;
    while (i < arr.length) {
        if(arr[i] === value) {
            arr.splice(i, 1);
        } else {
            ++i;
        }
    }
    return arr;
}

Alternative Answer:

To remove an element of an array at an index

i
:

array.splice(i, 1);

If you want to remove every element with value

number
from the array:

for(var i = array.length - 1; i >= 0; i--) {
    if(array[i] === number) {
        array.splice(i, 1);
    }
}

If you just want to make the element at index

i
no longer exist, but you don’t want the indexes of the other elements to change:

delete array[i];

2. How to redirect the user from one page to another using jQuery or pure JavaScript?

Answer:

One does not simply redirect using jQuery.

jQuery is not necessary, and

window.location.replace(...)
will best simulate an HTTP redirect.

window.location.replace(...)
is better than using
window.location.href
, because
replace()
does not keep the originating page in the session history, meaning the user won’t get stuck in a never-ending back-button fiasco.

If you want to simulate someone clicking on a link, use

location.href

If you want to simulate an HTTP redirect, use

location.replace

For example:

// similar behavior as an HTTP redirect
window.location.replace("http://stackoverflow.com");

// similar behavior as clicking on a link
window.location.href = "http://stackoverflow.com";

Alternative Answer:

You can also do it as shown below.

$(location).attr('href', 'http://stackoverflow.com')

3. How do JavaScript closures work?

Answer:

A closure is a pairing of:

  • A function, and
  • A reference to that function’s outer scope (lexical environment)

A lexical environment is part of every execution context (stack frame), and is a map between identifiers (ie. local variable names) and values.

Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to “see” variables declared outside the function, regardless of when and where the function is called.

If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.

In the following code,

inner
forms a closure with the lexical environment of the execution context created when
foo
is invoked, closing over variable
secret
:

function foo() {
  const secret = Math.trunc(Math.random()*100)
  return function inner() {
    console.log(`The secret number is ${secret}.`)
  }
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`

In other words, in JavaScript, functions carry a reference to a private “box of state”, to which only they (and any other functions declared within the same lexical environment) have access. This box of state is invisible to the caller of the function, delivering an excellent mechanism for data-hiding and encapsulation.

And remember functions in JavaScript can be passed around like variables (first-class functions), meaning these pairings of functionality and state can be passed around your program: similar to how you might pass an instance of a class around in C++.

If JavaScript did not have closures, then more state would have to be passed between functions explicitly, making parameter lists longer and code noisier.

So, if you want a function to always have access to a private piece of state, you can use a closure, and frequently we do want to associate the state with a function. For example, in Java or C++, when you add a private instance variable and a method to a class, you are associating state with functionality.

In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed. In JavaScript, if you declare a function within another function, then the local variables of the outer function can remain accessible after returning from it. In this way, in the code above,

secret
remains available to the function object
inner
, after it has been returned from
foo
.

Uses of Closures

Closures are useful whenever you need a private state associated with a function. This is a very common scenario and you need to remember that JavaScript did not have a class syntax until 2015, and it still does not have a private field syntax. Closures meet this need.

Private Instance Variables

In the following code, function

toString
closes over the details of the car.

function Car(manufacturer, model, year, color) {
  return {
    toString() {
      return `${manufacturer} ${model} (${year}, ${color})`
    }
  }
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())

Functional Programming

In the following code, function

inner
closes over both
fn
and
args
.

function curry(fn) {
  const args = []
  return function inner(arg) {
    if(args.length === fn.length) return fn(...args)
    args.push(arg)
    return inner
  }
}

function add(a, b) {
  return a + b
}

const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5

Event-Oriented Programming

In the following code, function

onClick
closes over variable
BACKGROUND_COLOR
.

const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'

function onClick() {
  $('body').style.background = BACKGROUND_COLOR
}

$('button').addEventListener('click', onClick)
<button>Set background color</button>

Modularization

In the following example, all the implementation details are hidden inside an immediately executed function expression. The functions

tick
and
toString 
close over the private state and functions, they need to complete their work. Closures have enabled us to modularise and encapsulate our code.

let namespace = {};

(function foo(n) {
  let numbers = []
  function format(n) {
    return Math.trunc(n)
  }
  function tick() {
    numbers.push(Math.random() * 100)
  }
  function toString() {
    return numbers.map(format)
  }
  n.counter = {
    tick,
    toString
  }
}(namespace))

const counter = namespace.counter
counter.tick()
counter.tick()
console.log(counter.toString())

Examples

Example 1

This example shows that the local variables are not copied in the closure. The closure maintains a reference to the original variables themselves. It is as though the stack-frame stays alive in memory even after the outer function exits.

function foo() {
  let x = 42
  let inner  = function() { console.log(x) }
  x = x+1
  return inner
}
var f = foo()
f() // logs 43

Example 2

In the following code, three methods

log
,
increment
and
update
all close over the same lexical environment.

And every time

createObject
is called, a new execution context (stack frame) is created and a completely new variable
x
, and a new set of functions (
log
etc.) are created, that close over this new variable.

function createObject() {
  let x = 42;
  return {
    log() { console.log(x) },
    increment() { x++ },
    update(value) { x = value }
  }
}

const o = createObject()
o.increment()
o.log() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42

Example 3

If you are using variables declared using

var
, be careful you understand which variable you are closing over. Variables declared using
var
are hoisted. This is much less of a problem in modern JavaScript due to the introduction of
let
and
const
.

In the following code, each time around the loop, a new function

inner
is created, which closes over
i
. But because
var i
is hoisted outside the loop, all of these inner functions close over the same variable, meaning that the final value of
i
(3) is printed, three times.

function foo() {
  var result = []
  for (var i = 0; i < 3; i++) {
    result.push(function inner() { console.log(i) } )
  }
  return result
}

const result = foo()
// The following will print `3`, three times...
for (var i = 0; i < 3; i++) {
  result[i]() 
}

Final points:

  • Whenever a function is declared in JavaScript, a closure is created.
  • Returning a
    function
    from inside another function is the classic example of closure, because the state inside the outer function is implicitly available to the returned inner function, even after the outer function has completed execution.
  • Whenever you use
    eval()
    inside a function, a closure is used. The text you eval can reference local variables of the function, and in the non-strict mode, you can even create new local variables by using
    eval('var foo = …')
    .
  • When you use
    new Function(…)
    (the Function constructor) inside a function, it does not close over its lexical environment, it closes over the global context instead. The new function cannot reference the local variables of the outer function.
  • A closure in JavaScript is like keeping a reference (not a copy) to the scope at the point of function declaration, which in turn keeps a reference to its outer scope, and so on, all the way to the global object at the top of the scope chain.
  • A closure is created when a function is declared. This closure is used to configure the execution context when the function is invoked.
  • A new set of local variables is created every time a function is called.

Alternative Answer:

Every function in JavaScript maintains a link to its outer lexical environment. A lexical environment is a map of all the names (eg.
variables, parameters) within scope, with their values.

So, whenever you see the

function
keyword, code inside that function has access to variables declared outside the function.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

This will log

16
because function
bar
closes over the parameter
x
and the variable
tmp
, both of which exist in the lexical environment of the outer function
foo
.

Function

bar
, together with its link with the lexical environment of function
foo 
is a closure.

A function doesn’t have to return in order to create a closure. Simply by virtue of its declaration, every function closes over its enclosing lexical environment, forming a closure.

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2);
bar(10); // 16
bar(10); // 17

The above function will also log 16 because the code inside

bar
can still refer to the argument
x
and variable
tmp
, even though they are no longer directly in scope.

However, since

tmp
is still hanging around inside
bar
‘s closure, it is available to be incremented. It will be incremented each time you call
bar
.

The simplest example of a closure is this:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

When a JavaScript function is invoked, a new execution context

ec 
is created. Together with the function arguments and the target object, this execution context also receives a link to the lexical environment of the calling execution context, meaning the variables declared in the outer lexical environment (in the above example, both
a
and
b
) are available from
ec
.

Every function creates a closure because every function has a link to its outer lexical environment.

Note that variables themselves are visible from within a closure, not copies.

4. What does “use strict” do in JavaScript, and what is the reasoning behind it?

Answer:

To quote some interesting parts:

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a “strict” operating context. This strict context prevents certain actions from being taken and throws more exceptions.

And Strict mode helps out in a couple of ways:

  • It catches some common coding bloopers, throwing exceptions.
  • It prevents or throws errors when relatively “unsafe” actions are taken (such as gaining access to the global object).
  • It disables features that are confusing or poorly thought out.

Also, note that you can apply “strict mode” to the whole file or you can use it only for a specific function.

// Non-strict code...

(function(){
  "use strict";

  // Define your library strictly...
})();

// Non-strict code... 

Which might be helpful if you have to mix old and new code.

So, it’s a bit like the

"use strict"
you can use in Perl. It helps you make fewer errors, by detecting more things that could lead to breakages.

Strict mode is now supported by all major browsers.

Inside native ECMAScript modules (with

import
and
export
statements) and ES6 classes, strict mode is always enabled and cannot be disabled.

Alternative Answer:

It’s a new feature of ECMAScript 5.

It’s just a string you put in your JavaScript files (either at the top of your file or inside of a function) that looks like this:

"use strict";

Putting it in your code now shouldn’t cause any problems with current browsers as it’s just a string. It may cause problems with your code in the future if your code violates the pragma. For instance, if you currently have

foo = "bar"
without defining
foo
first, your code will start failing which is a good thing in our opinion.

5. How to check whether a string contains a substring in JavaScript?

Answer:

ECMAScript 6 introduced

String.prototype.includes
:

const string = "foo";
const substring = "oo";

console.log(string.includes(substring));

includes
doesn’t have Internet Explorer support, though. In CMAScript 5 or older environments, use
String.prototype.indexOf
, which returns -1 when a substring cannot be found:

var string = "foo";
var substring = "oo";

console.log(string.indexOf(substring) !== -1);

Alternative Answer:

There is a String.prototype.includes in ES6:

"potato".includes("to");
> true

Note that this does not work in Internet Explorer or some other old browsers with no or incomplete ES6 support. To make it work in old browsers, you may wish to use a transpiler like Babel, a shim library like es6-shim, or this polyfill from MDN:

if (!String.prototype.includes) {
  String.prototype.includes = function(search, start) {
    'use strict';
    if (typeof start !== 'number') {
      start = 0;
    }

    if (start + search.length > this.length) {
      return false;
    } else {
      return this.indexOf(search, start) !== -1;
    }
  };
}

6. var functionName = function() {} vs function functionName() {}

Answer:

The difference is that

functionOne
is a function expression and so only defined when that line is reached, whereas
functionTwo
is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

For example, a function expression:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

And, a function declaration:

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

Alternative Answer:

function abc(){}
is scoped too — the name
abc
is defined in the scope where this definition is encountered. Example:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

Secondly, it is possible to combine both styles:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

xyz
is going to be defined, as usual, abc is undefined in all browsers but Internet Explorer does not rely on it being defined. But it will be defined inside its body:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

If you want to alias functions on all browsers, use this kind of declaration:

function abc(){};
var xyz = abc;

In this case, both

xyz
and
abc
are aliases of the same object:

console.log(xyz === abc); // prints "true"

One compelling reason to use the combined style is the “name” attribute of function objects (not supported by Internet Explorer). Basically when you define a function like

console.log(xyz === abc); // prints "true"

its name is automatically assigned. But when you define it like

var abc = function(){};
console.log(abc.name); // prints ""

its name is empty — we created an anonymous function and assigned it to some variable.

Another good reason to use the combined style is to use a short internal name to refer to itself while providing a long non-conflicting name for external users:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

In the example above we can do the same with an external name, but it’ll be too unwieldy (and slower).

(Another way to refer to itself is to use

arguments.callee
, which is still relatively long, and not supported in the strict mode.)

Deep down, JavaScript treats both statements differently. This is a function declaration:

function abc(){}

abc
here is defined everywhere in the current scope:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Also, it hoisted through a

return
statement:

// We can call it here
abc(); // Works
return;
function abc(){}

This is a function expression:

var xyz = function(){};

xyz
here is defined from the point of assignment:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Function declaration vs. function expression is the real reason why there is a difference.

Fun fact:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

Personally, we prefer the “function expression” declaration because this way we can control the visibility. When we define the function like

var abc = function(){};

We know that we defined the function locally. When we define the function like

var abc = function(){};

We know that we defined it globally providing that we didn’t define

abc
anywhere in the chain of scopes. This style of the definition is resilient even when used inside
eval()
. While the definition

function abc(){};

depends on the context and may leave you guessing where it is actually defined, especially in the case of

eval()
— the answer is it depends on the browser.

7. How to remove a property from a JavaScript object?

Answer:

You can do as shown below:

delete myObject.regex;
// or,
delete myObject['regex'];
// or,
var prop = "regex";
delete myObject[prop];

Demo

var myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI",
    "regex": "^http://.*"
};
delete myObject.regex;

console.log(myObject);

Alternative Answer:

Objects in JavaScript can be thought of as maps between keys and values. The

delete
operator is used to remove these keys, more commonly known as object properties, one at a time.

var obj = {
  myProperty: 1    
}
console.log(obj.hasOwnProperty('myProperty')) // true
delete obj.myProperty
console.log(obj.hasOwnProperty('myProperty')) // false

The

delete
operator does not directly free memory, and it differs from simply assigning the value of
null
or
undefined
to a property, in that, the property itself is removed from the object. Note that if the value of a deleted property was a reference type (an object), and another part of your program still holds a reference to that object, then that object will, of course, not be garbage collected until all references to it have disappeared.

delete
will only work on properties whose descriptor marks them as configurable.

8. Which equals operator (== vs ===) should be used in JavaScript comparisons?

Answer:

The strict equality operator (

===
) behaves identically to the abstract equality operator (
==
) except no type conversion is done, and the types must be the same to be considered equal.

Reference: Javascript Tutorial: Comparison Operators

The

==
operator will compare for equality after doing any necessary type conversions. The
===
operator will not do the conversion, so if two values are not the same type
===
will simply return
false
. Both are equally quick.

JavaScript has two sets of equality operators:

===
and
!==
, and their evil twins
==
and
!=
. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then
===
produces
true
and
!==
produces false.

The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. The rules by which they do that are complicated and unmemorable.

These are some of the interesting cases:

'' == '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 lack of transitivity is alarming. Our advice is to never use the evil twins. Instead, always use

===
and
!==
.

All of the comparisons just shown produce false with the

===
operator.

For reference types

==
and
===
act consistently with one another (except in a special case).

var a = [1,2,3];
var b = [1,2,3];

var c = { x: 1, y: 2 };
var d = { x: 1, y: 2 };

var e = "text";
var f = "te" + "xt";

a == b            // false
a === b           // false

c == d            // false
c === d           // false

e == f            // true
e === f           // true

The special case is when you compare a literal with an object that valuates to the same literal, due to its

toString
or
valueOf
method.

For example, consider the comparison of a string literal with a string object created by the

String
constructor.

"abc" == new String("abc")    // true
"abc" === new String("abc")   // false

Here the

==
operator is checking the values of the two objects and returning
true
, but the
===
is seeing that they’re not the same type and returning
false
. Which one is correct? That really depends on what you’re trying to compare.

Our advice is to bypass the question entirely and just don’t use the

String
constructor to create string objects.

Alternative Answer:

Using the

==
operator (Equality)

true == 1; //true, because 'true' is converted to 1 and then compared
"2" == 2;  //true, because "2" is converted to 2 and then compared

Using the

===
operator (Identity)

true === 1; //false
"2" === 2;  //false

This is because the equality operator

==
does type coercion, meaning that the interpreter implicitly tries to convert the values before comparing.

On the other hand, the identity operator

===
does not do type coercion, and thus does not convert the values when comparing, and is, therefore, faster (as according to This JS benchmark test) as it skips one step.

9. What is the most efficient way to deep clone an object in JavaScript?

Answer:

Native deep cloning

It’s called “structured cloning”, works experimentally in Node 11 and later, and hopefully will land in browsers.

Fast cloning with data loss – JSON.parse/stringify

If you do not use

Date
s, functions,
undefined
,
Infinity
, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one-liner to deep clone an object is:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Reliable cloning using a library

Since cloning objects is not trivial (complex types, circular references, function, etc.), most major libraries provide a function to clone objects. Don’t reinvent the wheel – if you’re already using a library, check if it has an object cloning function. For example,

ES6

For completeness, note that ES6 offers two shallow copy mechanisms:

Object.assign()
and the spread syntax. which copies values of all enumerable own properties from one object to another. For example:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

Alternative Answer:

Check out this benchmark: http://jsben.ch/#/bWfk9

In our previous tests where speed was a main concern we found

JSON.parse(JSON.stringify(obj))

to be the slowest way to deep clone an object (it is slower than jQuery.extend with

deep
flag set true by 10-20%).

jQuery.extend is pretty fast when the

deep
flag is set to
false 
(shallow clone). It is a good option, because it includes some extra logic for type validation and doesn’t copy over undefined properties, etc., but this will also slow you down a little.

If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple

for (var i in obj)
loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.

Lastly if you are attempting to clone a known object structure in a hot loop you can get much much more performance by simply in-lining the cloning procedure and manually constructing the object.

JavaScript trace engines suck at optimizing

for..in
loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Beware using the

JSON.parse(JSON.stringify(obj))
method on
Date
objects –
JSON.stringify(new Date())
returns a string representation of the date in ISO format, which
JSON.parse()
doesn’t convert back to a
Date
object. See this answer for more details.

Additionally, please note that in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.

Update for ES6

If you are using Javascript ES6 try this native method for cloning or shallow copy.

Object.assign({}, obj);

10. How to include a JavaScript file in another JavaScript file?

Answer:

The old versions of JavaScript had no import, include, or require, so many different approaches to this problem have been developed.

But since 2015 (ES6), JavaScript has had the ES6 modules standard to import modules in Node.js, which is also supported by most modern browsers.

For compatibility with older browsers, build tools like Webpack and Rollup and/or transpilation tools like Babel can be used.

ES6 Modules

ECMAScript (ES6) modules have been supported in Node.js since v8.5, with the

--experimental-modules 
flag, and since at least Node.js v13.8.0 without the flag. To enable “ESM” (vs. Node.js’s previous CommonJS-style module system [“CJS”]) you either use
"type": "module"
in
package.json
or give the files the extension
.mjs
. (Similarly, modules written with Node.js’s previous CJS module can be named
.cjs
if your default is ESM.)

Using

package.json
:

{
    "type": "module"
}

Then

module.js
:

export function hello() {
  return "Hello";
}

Then

main.js
:

import { hello } from './module.js';
let val = hello();  // val is "Hello";

Using

.mjs
, you’d have
module.mjs
:

export function hello() {
  return "Hello";
}

Then

main.mjs
:

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";

ECMAScript modules in browsers

Browsers have had support for loading ECMAScript modules directly (no tools like Webpack required) since Safari 10.1, Chrome 61, Firefox 60, and Edge 16. Check the current support at caniuse. There is no need to use Node.js’

.mjs
extension; browsers completely ignore file extensions on modules/scripts.

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');
</script>
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

Read more at https://jakearchibald.com/2017/es-modules-in-browsers/

Dynamic imports in browsers

Dynamic imports let the script load other scripts as needed:

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

Read more at https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js require

The older CJS module style, still widely used in Node.js, is the

module.exports/require
system.

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

There are other ways for JavaScript to include external JavaScript contents in browsers that do not require preprocessing.

AJAX Loading

You could load an additional script with an AJAX call and then use

eval 
to run it. This is the most straightforward way, but it is limited to your domain because of the JavaScript sandbox security model. Using
eval 
also opens the door to bugs, hacks, and security issues.

Fetch Loading

Like Dynamic Imports, you can load one or many scripts with a

fetch
call using promises to control order of execution for script dependencies using the Fetch Inject library:

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

jQuery Loading

The jQuery library provides loading functionality in one line:

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

Dynamic Script Loading

You could add a script tag with the script URL into the HTML. To avoid the overhead of jQuery, this is an ideal solution.

The script can even reside on a different server. Furthermore, the browser evaluates the code. The

<script>
tag can be injected into either the web page
<head>
, or inserted just before the closing
</body>
tag.

Here is an example of how this could work:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script");  // create a script DOM node
    script.src = url;  // set its src to the provided URL

    document.head.appendChild(script);  // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}

This function will add a new

<script>
tag to the end of the head section of the page, where the
src
attribute is set to the URL which is given to the function as the first parameter.

Both of these solutions are discussed and illustrated in JavaScript Madness: Dynamic Script Loading.

Detecting when the script has been executed

Now, there is a big issue you must know about. Doing that implies that you remotely load the code. Modern web browsers will load the file and keep executing your current script because they load everything asynchronously to improve performance. (This applies to both the jQuery method and the manual dynamic script loading method.)

It means that if you use these tricks directly, you won’t be able to use your newly loaded code the next line after you asked it to be loaded, because it will be still loading.

For example:

my_lovely_script.js
contains
MySuperObject
:

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Then you reload the page hitting F5. And it works! Confusing…

So what to do about it?

You can put all the code using the remote library in the callback function. For example:

function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.head;
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    // Then bind the event to the callback function.
    // There are several events for cross browser compatibility.
    script.onreadystatechange = callback;
    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}

Then you write the code you want to use after the script is loaded in a lambda function:

var myPrettyCode = function() {
   // Here, do whatever you want
};

Then you run all that:

loadScript("my_lovely_script.js", myPrettyCode);

Note that the script may execute after the DOM has loaded, or before, depending on the browser and whether you included the line

script.async = false;
. There’s a great article on Javascript loading in general which discusses this.

Source Code Merge/Preprocessing

As mentioned at the top of this answer, many developers use build/transpilation tool(s) like Parcel, Webpack, or Babel in their projects, allowing them to use upcoming JavaScript syntax, provide backward compatibility for older browsers, combine files, minify, perform code-splitting etc.

Alternative Answer:

If you are looking for something more advanced, try out RequireJS. You’ll get added benefits such as dependency management, better concurrency, and avoid duplication (that is, retrieving a script more than once).

You can write your JavaScript files in “modules” and then reference them as dependencies in other scripts. Or you can use RequireJS as a simple “go get this script” solution.

Example:

Define dependencies as modules:

some-dependency.js

define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {

     //Your actual script goes here.   
     //The dependent scripts will be fetched if necessary.

     return libraryObject;  //For example, jQuery object
});

implementation.js is your “main” JavaScript file that depends on some-dependency.js

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

Excerpt from the GitHub README:RequireJS loads plain JavaScript files as well as more defined modules. It is optimized for in-browser use, including in a Web Worker, but it can be used in other JavaScript environments, like Rhino and Node. It implements the Asynchronous Module API.

RequireJS uses plain script tags to load modules/files, so it should allow for easy debugging. It can be used simply to load existing JavaScript files, so you can add it to your existing project without having to re-write your JavaScript files.

In Conclusion

These are the 10 most commonly asked questions about JavaScript. If you have any suggestions or any confusion, please comment below. If you need any help, we will be glad to help you.

This post was originally posted on DevPost by Truemark.

Previously published at https://thedevpost.com/blog/10-most-asked-questions-about-javascript/