Categories
JavaScript

Using AJAX and JSON in JavaScript

AJAX makes our page dynamic and lets us refresh data in our web pages without refreshing the page. It makes user pages interactive and creates a slicker user experience for the user. AJAX stands for Asynchronous JavaScript and XML. It’s used to describe the way that we use the XMLHttpRequest object to refresh part of the page by getting data from the server via HTTP requests and manipulate the DOM to refresh the data.

The HTML DOM changes the dynamically. AJAX allows us to use the XMLHttpRequest object to get data from the server and manipulate the DOM without blocking the execution of other parts of the JavaScript program. Despite that AJAX stands for Asynchronous JavaScript and XML, JSON is frequently used for sending and retrieving data from the server. JSON stands for JavaScript Object Notation. JSON is a data format that very close to a JavaScript object, except that it can’t contain any functions or dynamic code. Only string, numbers, arrays and objects without functions are allowed.

Old style web pages refresh the whole page to refresh the data. This is getting less and less common today as it creates a less pleasant experience than just refreshing parts of the page that’s needed to refresh. Refreshing the whole page would make the whole page flash and everything has to be loaded again.

AJAX is also great for updating live data on a page since it only refreshes parts of a page that’s need to refresh. It’s good for tables, charts, emails, and things that refresh periodically.

To view AJAX in action, we can look at the browser’s development console. For example, in Chrome, we can go to a website like weather.com and then we can press F12 or right click the browser window and click Inspect to open the development console. Then we can go to the Network tab. In there, we have the XHR button, XHR stands for XmlHttpRequest, which is what’s originally used for AJAX. On the left side of the XHR section, click on one of the entries. Next click on Preview, and then we can see something like the following:

This is the JSON data that’s parsed by the browser and can be inserted to the DOM of a web page. All AJAX is sending data via HTTP requests and then response will be obtained from the the server, then the data is populated on the page via manipulating the DOM. Most modern browsers work the same way and support AJAX.

Same-Origin Policy

In most cases, requests are made to a server that has a different domain from the server that has a different domain from the originating server. If we do that without setting some options, we will get an error since most browsers by default stop cross origin requests from being made without setting specific options on the server and the client. The purpose of this is to prevent browsers from downloading code that’s unknown to the browser. This policy applies everywhere, even on our local computer, since it’s can be used to compromise the security of our computers no matter where the data is downloaded from.

To allow requests to request between computers with different domains from a web browser. The server has to enable Cross-Origin Resource Sharing, or CORS for short. This has to be enabled on the server side. If we look at a cross origin HTTP request, we should see an OPTIONS request before the actual request is made and in we should see the following in the response headers:

Access-Control-Allow-Origin: \*

Alternatively, we can put the client and server side code in the same domain. This wouldn’t violate the same origin policy in browsers since the request from the browser is made from the same domain from the server.

Using the XMLHttpRequest Object

We can use the XMLHttpRequest object to make a request to a server and retrieve its response. This can be done with a simple example. In this example, we will load content from a text file and then put the content inside a page. Create a file called index.html and add:

<html>  
  <head>  
    <title>XMLHttpRequest</title>  
  </head>  
  <body>  
    <h1>XML HTTP Request</h1>  
    <button id="load">Load Data</button>  
    <div id="content"></div>  
    <script src="script.js"></script>  
  </body>  
</html>

The create a file called script.js and add:

const reqListener = response => {  
  const content = document.getElementById("content");  
  content.innerHTML = response.currentTarget.response;  
};

const loadData = () => {  
  const req = new XMLHttpRequest();  
  req.onload = reqListener;  
  req.open("get", "file.txt", true);  
  req.send();  
};

window.onload = () => {  
  const loadButton = document.getElementById("load");  
  loadButton.onclick = loadData;  
};

Then in file.txt we put:

Hello world.

In the above example, we created an instance of the XMLHttpRequest object to make a GET request to our server, which is a local web server that servers the file.txt file. We call the open function to start the request, The first argument is the HTTP request method, which can be get , post , put , patch or delete . The second argument is the URL or relative path to your server side resource. The third argument is for setting whether the HTTP request happens asynchronously. Usually, it should be true since we do not want to hold up other parts of our JavaScript code from loading. Also, we set the req.onload listener function to get the response from the server and then populate the result. In reqListener function, we get the response from the server when it’s available and then populate the content of the element with ID content with it. This happens after we call end send function on the req object.

Once we have these files we should get ‘Hello world’ when we click ‘Load Data’, we should get ‘Hello world.’ in our page.

This is the simplest case for making HTTP requests and then getting the result with AJAX. If we want to make requests that are more complex, then this is a very cumbersome way to do it. Fortunately, we have the Fetch API. to make a HTTP requests in our client side applications.

Promises

In JavaScript, a lot of code are asynchronous since it’s single threaded. To prevent holding up a program by code that finishes running in a indeterminate amount of time, we have to make a lot code asynchronous to prevent holding up the rest of the program from executing.

A promise is an object that represents an asynchronous process that finishes in an indeterminate amount of time. Before it’s executed then its status is pending. It can end in 2 states. If it’s successful, then the promise is resolved. A resolved promised is in the fulfilled state. Otherwise, it ends by by rejecting the promise. A promise is rejected if an error occurred. If an error occurred then the value that’s returned in the promise is ignored. In either case, when the promise finishes executing, a promise is in the settled status. That means settled status includes both fulfilled and error status.

The benefit of using promises for writing asynchronous code is that we can chain multiple promises together as if they are synchronous code. However, promises can only return other promises and they cannot return anything else. They are chained with the then function, which takes a callback function which has the resolved value when the promise is fulfilled. When an errors occurred they can be handled with the catch function at the end. To execute code no matter what the result of a promise is, we can chain the finally function to the end of a promise.

To run an array of promises, we can use the Promise.all function, which takes an array of promises.

An example of a promise would be something like:

const getTextPromise = new Promise((resolve, reject) => {  
  const xhr = new XMLHttpRequest();  
  xhr.open('GET', 'file.txt', true);  
  xhr.onload = (response) => resolve(response.currentTarget.responseText);  
  xhr.onerror = () => reject(xhr.statusText);  
  xhr.send();  
});

We wrapped the example we have above inside a promise and we resolve the response text instead of populating it in the DOM directly. To populate it in the DOM, we do:

getTextPromise  
.then(responseText =>{  
  const content = document.getElementById("content");  
  content.innerHTML = responseText;  
})  
.catch(error =>{  
  alert(error.toString());  
})

Make HTTP Requests with the Fetch API

The Fetch API makes use of promises to make asynchronous HTTP requests from the browser to a server. It provides us with an easy to use API for making requests. It lets us create HTTP requests with a Request object, then when the server makes a response, then we get a Response object. We make a request with the fetch method. The fetch method takes one mandatory, which includes all the data for making requests. Once the Response object is returned then we can massage it to get it to the form we want and then populate it in our web pages.

Also, it’s aware of concepts like CORS, so we can make cross origin requests if the server allows by setting some options within the argument of the fetch method. To use the fetch method for making HTTP request, we can do it with an example. If we want to make a form for letting users subscribe to your email list, we can write the following code. Create a file calledindex.html and add:

<html>  
  <head>  
    <title>Email List Subscribe Form</title>  
  </head>  
  <body>  
    <h1>Email List Subscribe Form</h1>  
    <form action="" name="nameForm" id="nameForm" method="post">  
      <label for="firstName">First Name: </label>  
      <input type="text" name="firstName" id="firstName" /><br />  
      <label for="lastName">Last Name: </label>  
      <input type="text" name="lastName" id="lastName" /><br />  
      <label for="email">Email: </label>  
      <input type="text" name="email" id="email" /><br />  
      <input type="submit" value="Subscribe" />  
    </form>  
    <script src="script.js"></script>  
  </body>  
</html>

to add the form. Then create a script.js file in the same folder and add:

const APIURL = "http://localhost:3000";
const subscribe = data => {  
  return fetch(`${APIURL}/subscribers\`, {  
    method: "POST",  
    mode: "cors",  
    cache: "no-cache",  
    headers: {  
      "Content-Type": "application/json"  
    },  
    body: JSON.stringify(data)  
  })
  .then(response => response.json());  
};

window.onload = () => {  
  const nameForm = document.forms.nameForm;  
  nameForm.method = "post";  
  nameForm.target = "\_blank";  
  nameForm.action = "";  
  nameForm.addEventListener("submit", e => {  
    e.preventDefault();  
    const firstName = document.getElementById("firstName").value;  
    const lastName = document.getElementById("lastName").value;  
    const email = document.getElementById("email").value;  
    let errors = [];  
    if (!firstName) {  
      errors.push("First name is required.");  
    } 

    if (!lastName) {  
      errors.push("Last name is required.");  
    } 

    if (!email) {  
      errors.push("Email is required.");  
    } 

    if (!/\[^@\]+@\[^\.\]+\..+/.test(email)) {  
      errors.push("Email is invalid.");  
    } if (errors.length > 0) {  
      alert(errors.join(" "));  
      return;  
    }  
    subscribe({  
      firstName,  
      lastName,  
      email  
    }).then(response => {  
      alert(\`${response.firstName} ${response.lastName} has subscribed\`);  
    });  
  });  
};

The subscribe function is where we used the fetch method. In there we pass in the URL of the server we are making the request to as the first argument. In the second argument, we set the method, which the HTTP request method for the request we want to make, which is POST. mode should be CORS since we’re making a cross domain request. We don’t want any request caching so we set cache to no-cache . In headers , we set Content-Type to application/json to make the requests data type send JSON. The body is the request body of the request, which we create a JSON object since we are sending JSON to the server. Finally, we return response.json() , which is a promise with the response if the request is successful sent back from the server and converted to JSON format.

To send something to the URL we point to we have to set up a web server. We first install the json-server package by running npm i json-server. Then, go to our project folder and run:

json-server --watch db.json

In db.json, change the text to:

{  
  "subscribers": []  
}

So we have the subscribers endpoints defined in the requests.js available.

Shorten Promise Code with Async and Await

The code above makes use of several promises. We have one for making the request, and then another one for converting it to JSON. Then finally, we make the alert at the end. With the then function, we make to put callback functions as an argument of all of our then functions. This makes the code long is we have lots of promises. Instead, we can use the async and await syntax to replace the then and its associated callbacks as follows. In script.js , we put:

const APIURL = "http://localhost:3000";
const subscribe = async data => {  
  const response = await fetch(`${APIURL}/subscribers`, {  
    method: "POST",  
    mode: "cors",  
    cache: "no-cache",  
    headers: {  
      "Content-Type": "application/json"  
    },  
    body: JSON.stringify(data)  
  });  
  return response.json();  
};

window.onload = () => {  
  nameForm.method = "post";  
  nameForm.target = "_blank";  
  nameForm.action = "";  
  nameForm.addEventListener("submit", async e => {  
    e.preventDefault();  
    const firstName = document.getElementById("firstName").value;  
    const lastName = document.getElementById("lastName").value;  
    const email = document.getElementById("email").value;  
    let errors = \[\];  
    if (!firstName) {  
      errors.push("First name is required.");  
    } 

    if (!lastName) {  
      errors.push("Last name is required.");  
    } 

    if (!email) {  
      errors.push("Email is required.");  
    } 
    
    if (!/\[^@\]+@\[^\.\]+\..+/.test(email)) {  
      errors.push("Email is invalid.");  
    } 

    if (errors.length > 0) {  
      alert(errors.join(" "));  
      return;  
    }  
    try {  
      const response = await subscribe({  
        firstName,  
        lastName,  
        email  
      });  
      alert(`${response.firstName} ${response.lastName} has subscribed\`);  
    } catch (error) {  
      alert(error.toString());  
    }  
  });  
};

We replaced the then and callbacks with await . Then we can assign the resolved values of each promise as variables. Note that if we use await for our promise code then we have to put async in the function signature like we did in the above example. To catch errors, instead of chaining the catch function at the end, we use the catch clause to do it instead. Also, instead of chaining the finally function at the bottom to run code when a promise ends, we use the finally clause after the catch clause to do that instead.

async functions always return promises, and cannot return anything else like any other function that uses promises.

With the Fetch API and async and await syntax, we can make HTTP requests to servers much more easily than using the old XMLHttpRequest object. This is why now more and more web applications are using AJAX instead of refreshing the whole page on server side to get data.

Categories
JavaScript

A Guide to JSON and How It’s Handled in JavaScript

JSON stands for JavaScript Object Notation. It is a format for serializing data, which means that it can be used to transmit and receive data between different sources. In JavaScript, there’s a JSON utility object that provides methods to convert JavaScript objects to JSON strings and vice versa. The JSON utility object can’t be constructed or called — there are only 2 static methods which are stringify and parse to convert between JavaScript objects and JSON strings.

Properties of JSON

JSON is a syntax for serializing objects, arrays, numbers, booleans, and null. It is based on the JavaScript object syntax, but they are not the same thing. Not all JavaScript object properties can be converted to valid JSON, and JSON strings must be correctly formatted to be converted into a JavaScript object.

For objects and arrays, JSON property names must be in double-quoted strings, and trailing commas for objects are prohibited. Numbers cannot have leading zeroes, and a decimal point must be followed by at least one digit. NaN and Infinity aren’t supported, and JSON strings can’t have undefined or comments. In addition, JSON can not contain functions.

Any JSON text must contain valid JavaScript expressions. In some browser engines, the U+2028 line separator and U+2029 paragraph separator are allowed in string literals and property keys in JSON, but when using them in JavaScript code will result in SyntaxError. Those 2 characters can be parsed with JSON.parse into valid JavaScript strings, but fails when passed into eval.

Insignificant whitespace may be included anywhere except within JSONNumber or JSONString. Numbers can’t have whitespace inside and strings would be interpreted as whitespace in the string or cause an error. The tab character (U+0009), carriage return (U+000D), line feed (U+000A), and space (U+0020) characters are the only valid whitespace characters in JSON.

Basic Usage of the JSON Object

There are 2 methods on the JSON utility object. There is the stringify method for converting a JavaScript object to a JSON string and the parse method for converting a JSON string to a JavaScript object.

The parse method parses a string as JSON with a function as a second argument to optionally transform JSON entities to the JavaScript entity that you specified and return the resulting JavaScript object. If the string has entities that aren’t allowed in the JSON syntax, then a SyntaxError would be raised. Also, tailing commas aren’t allowed in the JSON string that is passed into JSON.parse. For example, we can use it as in the following code:

JSON.parse('{}'); // {}       
JSON.parse('false'); // false        
JSON.parse('"abc"'); // 'abc'         
JSON.parse('[1, 5, "abc"]');  // [1, 5, 'abc']  
JSON.parse('null'); // null

The first line would return an empty object. The second would return false. The third line would return 'abc'. The fourth line would return [1, 5, "abc"]. The fifth line would return null. It returns what we expect since every line we pass in is valid JSON.

Customize the Behavior of Stringify and Parse

Optionally, we can pass in a function as the second argument to convert values to whatever we want. The function we pass in will take the key as the first parameter and the value as the second and returns the value after manipulation is done. For example, we can write:

JSON.parse('{"a:": 1}', (key, value) =>  
  typeof value === 'number'  
    ? value * 10  
    : value       
);

Then we get {a: 10} returned. The function returns the original value multiplied by 10 if the value’s type is a number.

The JSON.stringify method can take a function as the second parameter that maps entities in the JavaScript object to something else in JSON. By default, all instances of undefined and unsupported native data like functions are removed. For example, if we write the following code:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc'  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

Then we see that fn1 is removed from the JSON string after running JSON.stringify since functions aren’t supported in JSON syntax. For undefined, we can see from the following code that undefined properties will be removed.

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

undefinedProp is not in the JSON string logged because it has been removed by JSON.strinfiy.

Also, NaN and Infinity all become null after converting to a JSON string:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}  
const jsonString = JSON.stringify(obj);  
console.log(jsonString);

We see that:

'{“foo”:1,”bar”:2,”abc”:”abc”,”nullProp”:null,”notNum”:null,”infinity”:null}'

NaN and Infinity have both become null instead of the original values.

For unsupported values, we can map them to supported values with the replacer function in the second argument which we can optionally pass in. The replace function takes the key of a property as the first parameter and the value as the second parameter. For example, one way to keep NaN, Infinity, or functions is to map them to a string like in the following code:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}

const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}

const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

After running console.log on jsonString in the last line, we see that we have:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity"  
}

What the replace function did was add additional parsing using the key and the value from the object being converted with JSON.stringify. It checks that if the value is a function, then we convert it to a string and return it. Likewise, with NaN, Infinity, and undefined, we did the same thing. Otherwise, we return the value as-is.

The third parameter of the JSON.stringfy function takes in a number to set the number of whitespaces to be inserted into the output of the JSON to make the output more readable. The third parameter can also take any string that will be inserted instead of whitespaces. Note that if we put a string as the third parameter that contains something other than white space(s), we may create a “JSON” a string that is not valid JSON.

For example, if we write:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity  
}
const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}
const jsonString = JSON.stringify(obj, replacer, 'abc');  
console.log(jsonString);

Then console.log will be:

{  
abc"fn1": "fn1() {}",  
abc"foo": 1,  
abc"bar": 2,  
abc"abc": "abc",  
abc"nullProp": null,  
abc"undefinedProp": "undefined",  
abc"notNum": null,  
abc"infinity": "Infinity"  
}

Which is obviously not valid JSON. JSON.stringify will throw a “cyclic object value” TypeError. Also, if an object has BigInt values, then the conversion will fail with a “BigInt value can’t be serialized in JSON” TypeError.

Also, note that Symbols are automatically discarded with JSON.stringify if they are used as a key in an object. So if we have:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity,  
  [Symbol('foo')]: 'foo'  
}

const replacer = (key, value) => {
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}

const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

We get back:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity"  
}

Date objects are converted to strings by using the same string as what date.toISOString() will return. For example, if we put:

const obj = {  
  fn1() {},  
  foo: 1,  
  bar: 2,  
  abc: 'abc',  
  nullProp: null,  
  undefinedProp: undefined,  
  notNum: NaN,  
  infinity: Infinity,  
  [Symbol('foo')]: 'foo',  
  date: new Date(2019, 1, 1)  
}  
const replacer = (key, value) => {  
  if (value instanceof Function) {  
    return value.toString();  
  } 
  else if (value === NaN) {  
    return 'NaN';  
  } 
  else if (value === Infinity) {  
    return 'Infinity';  
  } 
  else if (typeof value === 'undefined') {  
    return 'undefined';  
  } 
  else {  
    return value; // no change  
  }  
}  
const jsonString = JSON.stringify(obj, replacer, 2);  
console.log(jsonString);

We get:

{  
  "fn1": "fn1() {}",  
  "foo": 1,  
  "bar": 2,  
  "abc": "abc",  
  "nullProp": null,  
  "undefinedProp": "undefined",  
  "notNum": null,  
  "infinity": "Infinity",  
  "date": "2019-02-01T08:00:00.000Z"  
}

As we can see, the value of the date property is now a string after converting to JSON.

Deep Copy Objects

We can also use JSON.stringify with JSON.parse to make a deep copy of JavaScript objects. For example, to do a deep copy of a object without a library, you can JSON.stringify then JSON.parse:

const a = { foo: {bar: 1, {baz: 2}}  
const b = JSON.parse(JSON.stringfy(a)) // get a clone of a which you can change with out modifying a itself

This does a deep copy of an object, which means all levels of an object are cloned instead of referencing the original object. This works because JSON.stringfy converted the object to a string which are immutable, and a copy of it is returned when JSON.parse parses the string which returns a new object that doesn’t reference the original object.

Categories
JavaScript Mistakes

Common JavaScript Mistakes — Part 1

JavaScript is a language that’s friendlier than many other programming languages in the world. However, it’s still very easy to make mistakes when writing JavaScript code through misunderstanding or overlooking stuff that we already know. By avoiding some of the mistakes below, we can make our lives easier by preventing bugs and typos in our code that bog us down with unexpected results.


Mismatching Brackets

Statements and functions nested in each other means that there’re multiple levels of brackets in each file. Usually, apps are pretty complex, so the levels can add up. This means that mismatching brackets is easy if you use a text editor that doesn’t support syntax highlight or don’t check for mismatched brackets. This can easily be avoided with modern text editors such as Visual Studio Code, Atom, and Sublime. If we want to use simpler text editors, then use linters and code formatting tools like ESLint and Prettier to detect these issues. They also let us format code automatically and detect common style issues that may occur, like quote style inconsistencies, number of characters in a line, functions that can be shortened, etc.


Mismatching Quotes and Parentheses

JavaScript lets us use single quotes, double quotes, and backticks for strings. They are equivalent. However, we should open and close with the same character. So if we open a string with a single quote, then use a single quote to close a string. If we start with double quotes or backticks, then use those respectively to close the string. Also, special characters like quotation marks have to escape to be included in a string in some cases. If you open a string with single quotes and you also use single quotes in a string, then you have to escape that to include them in the string. This also applies to double quotes and backticks. If you use double quotes in a double-quoted string, then you have to escape it. And if you use a backtick in a template string, then you have to escape the backtick character.

In if statements, parentheses always have to wrap around the whole condition. For example, something like

if (x > y) && (y < 10) {...}

won’t work. The correct way to write that statement is to write is

if ((x > y) && (y < 10)) {...}

if we want to check that both conditions are true.

We can easily avoid this by using a JavaScript code-aware text editor, like Visual Studio code, which will highlight these syntax errors for us so that we can fix these mistakes and make the code run.


Confusing the =, ==, and === Operators

The single equal (=) operator is for assigning the data on the right side to the variable on the left side. It should not be confused with the double equals (==) and the triple equals operator (===), which are operators for comparing values of the left and the right of the operator. In JavaScript we can use all three operators in an if statement. However, in most cases, we don’t mean to use the single equal operator inside the condition of the if statement. What we actually want is to use the double or triple equals operator to compare the operands on the left and right of it.

For example, we shouldn’t be writing

if (x = 1){ ... }

since we don’t want to assign 1 to x inside the if statement. Instead, we should be using the double or triple equals operator like the code below

if (x == 1){ ... }

or

if (x === 1){ ... }

The first example, if (x = 1){ … }, is always true, since x is truthy because it’s assigned the value of 1, which is truthy. What we actually want is to make a comparison which makes use of the conditional if statement.


Using Variables Outside of Block Level

If we use the var keyword to declare a variable, it can be referenced in any place below the var expression. For example, suppose we have:

for (var j = 0; j < 10; j++) {  
  j = j + 1;  
}  
console.log(j);

Then we can see that j is 10 when we run the console.log statement on the last line of the code. This is the reason that we have the let and const keywords to declare variables and constants. They’re blocked scoped so that they can’t be referenced outside the block. This means that we avoid bugs that can occur when we use var since the variable can’t be accessed outside the block that it’s declared in. As a result, we can’t assign it to anything else accidentally, causing issues with our code. Instead of using var like we did in the example above, we can use let like in the following example:

for (let j = 0; j < 10; j++) {  
  j = j + 1;  
}  
console.log(j);

We should get ReferenceError: j is not defined when we run the code above, which is a good sign since we don’t want j to be referenced outside the for loop. If we remove the console.log statement, then it will run.


Treating Array-Like Iterable Objects Like Arrays

In JavaScript, we can have properties with keys that are numbers. They’re automatically converted to strings before being accessed since they’re actually strings with all numerical content. Objects like the arguments and NodeList objects aren’t arrays, but they have properties stored with integer keys like they’re arrays. It’s easy to mistake them for arrays. The difference between arrays and array-like objects is that array-like objects aren’t arrays, but they both have a function with the Symbol Symbol.Iterator as the identifier. In these cases, we can convert them to arrays. For example, if we want to get the arguments passed into a regular function with the arguments object, we write something like the following with the spread operator:

function f() {  
  const args = [...arguments];  
  console.log(args);  
}  
f(1, 2, 3);

If we run the code above, we get [1,2,3].


Confusing Non-Array Objects With Arrays

Since we can access object properties and array entries with the bracket notation, which looks the same when used for arrays and regular objects, it’s easy to confuse objects and arrays.

For example, suppose we have the following code:

const obj = {  
  0: 1,  
  1: 2,  
  2: 3  
};  
console.log(obj[0]);

We see that the console.log statement would return 1. The code above has a regular object, but in the console.log statement, we pass in 0 as the key with the bracket notation to get the value obj[0], which is 1.

If we have an array and we try to access an entry by its index like in the code below:

const arr = [1, 2, 3];  
console.log(arr[0]);

we also get 1 with the console.log statement. They both use the bracket notation to access their values, but they aren’t the same. Arrays are objects, but you can iterate through them, unlike regular objects. If you try to loop through an array with the for...of loop or the forEach function, or try to use the spread operator with it, the example with the obj object will result in an error since it’s not an iterable object. We can make it iterable by adding a generator function with the Symbol Symbol.iterator to it like in the following code:

const obj = {  
  0: 1,  
  1: 2,  
  2: 3,  
  [Symbol.iterator]: function*() {  
    for (let prop in this) {  
      yield this[prop];  
    }  
  }  
};

Then when we iterate the obj object with the for...of loop like the code below:

for (let num of obj) {  
  console.log(num);  
}

we get back the entries of the new obj object that we made iterable.

The spread operator would also work. If we have the following code:

console.log([...obj]);

we get [1, 2, 3] from the console.log output.


Most of these tricks to avoid mistakes include features from ES6. It’s a major reason why ES6 was released. Generators and the let and const keywords are all part of the specification. The use of ES6, along with better text editors like Visual Studio Code, help us avoid mistakes that we would otherwise would make with older technologies. It’s a great reason to keep JavaScript code and tooling up to date. ES6 was released in 2015, so there’s no reason to stay back. For older browsers like Internet Explorer, we can use something like Babel to convert it to ES5 on the fly. Or we can use a module bundler like Webpack to convert ES6 or later code to ES5 in the build artifact so that we can write code with newer versions of JavaScript.

Categories
JavaScript

Using Arrow Functions in JavaScript

Arrow functions are a new type of functions introduced with ES2015 that provides an alternative compact syntax to functions declares with the function keyword. They don’t have their own bindings to this, argument, super or new.target keywords. This means that they aren’t suited for methods since they don’t bind to this, argument, or super. This also means that they can’t be used as constructors.

Declare Arrow Functions

To declare arrow functions, we can write the following if the function is one line long:

() => 2

The function above returns the number 2. Notice that for functions that are only one line long, we don’t need the return keyword to return anything. Whatever is at the end is returned. Also notice that if it’s has no parameters than we need to put empty parentheses for the function signature. If there’s one parameter, then we can write something like:

x => x*2

The function above takes in a parameter x and multiplies x by 2 and then return it. Notice that for arrow functions that only has one parameter, we don’t need to wrap it inside parentheses. If we have more than 1 parameter, then we need parentheses around the parameters, like in the following code:

(x,y) => x*y

In the function above, we take x and y as parameters and return x*y . If we want to return an object literal in a one-line arrow function, we have to put parentheses around the object literal that we want to return. For example, we write something like:

() => ({ a: 1, b: 2 });

to return the object { a: 1, b: 2 } .

If we need to define a function that spans multiple lines, then we need to wrap curly brackets around the function’s code as we have below:

(x, y) => {  
  return x+y;  
}

So the general syntax for arrow functions are one of the following:

(p1, p2, …, pN) => { statements } (p1, p2, …, pN) => expression  
  
(p) => { statements }p => { statements }  
  
() => { statements }

Default Parameters

Arrow functions’ parameters can have default values set for them so that we can kip them when are calling functions and still get a value set to them. Default parameters is easy with JavaScript. To set them in our code, we write the following:

const sum = (a,b=1) => a+b

In the code above, we set the default parameter value of b to 1, so if the second argument isn’t passed in when we call the sum function, then b is automatically set to 1. So if we call sum as follows:

sum(1)

we get 2 returned.

As we can see, now we don’t have to worry about having optional arguments being undefined , which would be the alternative result if no default value if set for parameters. This eliminates many sources of errors that occurs when making parameters optional with JavaScript.

The alternative way without using default parameters would be to check if each parameter is undefined . We write the following to do this:

const sum = (a,b,c)=>{  
  if (typeof b === 'undefined'){  
    b = 1;  
  }if (typeof c === 'undefined'){  
    c = 1;  
  }  
  return a+b+c;  
}

If we call the following sum function without some parameters, then we get:

const sum = (a,b = 1,c = 1)=> a+b+c;  
sum(1) // returns 3  
sum(1, 2) // returns 4

As we can see, we handled missing parameters gracefully with default parameters.

For default parameters, passing in undefined is the same as skipping the parameters. For example, if we call the following function and pass in undefined , then we get:

const sum = (a,b = 1,c = 1)=> a+b+c;  
sum(1,undefined) // returns 3  
sum(1,undefined,undefined) // returns 3  
sum(1, 2,undefined) // returns 4

Note that undefined is the same as skipping parameters. This isn’t true for other falsy values that are passed into a function. So if we pass in 0, null , false , or empty string, then they get passed in, and the default parameter values will be overwritten. For example, if we have the code below:

const test = (num=1) => typeof num;  
test(undefined);  
test(null);  
test('');  
test(0);  
test(false);

We get number for test(undefined) , object for test(null) , string for test(string) , number for test(0) , and boolean for test(false) . This means that anything other than undefined is passed in, but note that if we pass in falsy values and then run arithmetic operations on them, then falsy values get converted to 0.

const sum = (a,b = 1,c = 1)=> a+b+c;  
sum(1,null)  
sum(1,null,false)  
sum(1, 2,'')

So sum(1, null) returns 2 since b is converted to 0 and c has the default value of 1. sum(1) returns 1 since b and c are converted to 0. sum(1, 2,’’) is 3 since a is 1, b is passed in so that it becomes 2 instead of getting the default value of 1, and c is an empty string which is converted to 0.

Default arguments are evaluated at call time so that they’re set each time they’re called if no argument is passed into the function parameter with default values. For example, if we have:

const append = (val, arr = [])=>{  
  arr.push(val);  
  return arr;  
}
append(1);  
append(2);

append(1) returns [1] and append(2) returns [2] since we didn’t pass in an array to each function call, so arr is set to an empty array each time it’s run.

It’s also important to know that we can pass in values returned by functions in the default parameter, so if we a function that returns something, we can call it in the default parameter and assign the returned value to the parameter. For example, we can write:

const fn = () => 2  
const sum(a, b = fn()) => a+b;

Then if we call sum(1) , we get 3 since fn function returns 2. This is very handy if we want to manipulate and combine values beforehand before assigning it as a parameter.

Another great feature of the default parameters is that the parameters left of the given parameter is available for the parameter for assignment as default values, so we can have a function like the function below:

const saySomething = (name, somethingToSay, message = \`Hi ${name}. ${somethingToSay}`) => ({  
  name,  
  somethingToSay,  
  message  
});

Notice that we assigned an expression to the message parameter in the saySomething function. This is great for manipulating data and then assigning as we did before by assigning the function. We can also see that we can have default parameters that depend on parameters that are to the left of it. This means that default parameters to not have to be static.

So we call it with the first 2 parameters filled in, like saySomething(‘Jane’, ‘How are you doing?’). We get:

{name: "Jane", somethingToSay: "How are you doing?", message: "Hi Jane. How are you doing?"}

The message is returned with the template string that we defined evaluated.

We cannot call functions nested inside a function to get the returned value as the default parameter value. For example, if we write:

const fn = (a = innerFn()) => {  
  const innerFn = () => { return 'abc'; }  
}

This will result in a ReferenceError being thrown because the inner function isn’t defined yet when the default parameter is defined.

We can also have default parameter values that are set to the left of the required parameters. Arguments are still passed into parameters from the left to the right, so is we have:

const sum = (a=1,b) => a+b

If we have sum(1) we have NaN return 1 is added to undefined since we didn’t pas in anything for the second argument, b is undefined . However, if we write sum(1,2) then 3 is returned since we have a set to 1 and b set to 2.

Finally, we can use destructuring assignment to set default parameter values. For example, we can write:

const sum = ([a,b] = [1,2], {c:c} = {c:3}) => a+b+c;

Then we call sum without arguments we get 6 since a is set to 1, b is set to 2, and c is set to 3 by the destructuring assignment feature that JavaScript provides.

This Object

If this is referenced in a regular function declared with the function keyword, the this object isn’t set inside an arrow function to the function that has the this inside. If an arrow function is inside a constructor, then this is set to the new object. If it’s not inside any object, then this inside the arrow function is undefined in strict mode. this will be set to the base object if the arrow function is inside an object. However, we always get the window object if we reference this in an arrow function. For example, if we log this inside an arrow function that’s not inside a constructor or class like in the following code:

const fn = () => thisconsole.log(fn);

We get the window object logged when console.log is run. Likewise, if we ran:

let obj = {}  
obj.f = () => {  
  return this;  
};  
console.log(obj.f());

We get the same thing as we got before. This is in contrast to the tradition functions declared with the function keyword. If we replace the functions above with traditional functions in the code above, like in the following code:

const fn = function() {  
  return this  
};  
console.log(fn);

We get the function fn that we declared logged for fn.

However, if the arrow function is inside a constructor, we get the object that it’s the property of as the value of this. For example, if we have:

const obj = function() {  
  this.fn = () => this;  
}
console.log(new obj().fn())

Then this will be the current object the this.fn function is in, which is obj, so obj will be logged by console.log(new obj().fn()) . Also, if we have the code below:

let obj = {}  
obj.f = function() {  
  return this;  
};  
console.log(obj.f());

We get the obj object logged when it’s run.

When arrow functions are called with call and apply , the argument for the this object will be ignored. This means that they can’t be used to change the this object. For example, if we have:

function add(a) {  
  return this.base + a;  
}  
console.log(add.call({  
  base: 1  
}, 2));

We get 3 logged. This means that we can pass in an object that we want for this and get its value with traditional functions. This wouldn’t work with arrow functions. If we have:

const add = (a) => {  
  return this.base + a;  
}  
console.log(add.call({  
  base: 1  
}, 2));

We get NaN because this is window, which doesn’t have the base property. The first argument for call is ignored. Likewise, if we try to set the value of this inside the function when we use apply, we can do that with traditional functions:

function add(a) {   
  return this.base + a;  
}  
console.log(add.apply({  
  base: 1  
}, [1]));

We get 2 logged since the first argument for apply is the this object for the function inside. This means that we can change this.base to 1 by passing in our own object for the first argument of apply. When we try the same thing with arrow functions like in the following code:

const add = (a) => {   
  return this.base + a;  
}  
console.log(add.apply({  
  base: 1  
}, [1]));

We get NaN because this.base is undefined . Again this is set to the window object since we have an arrow function, so this cannot be changed with the apply function.

The arguments object isn’t bound to an arrow function, so we can’t use the arguments object to get the arguments passed into a function call of an arrow function. If we have a traditional, we can get the arguments passed in with the arguments object like the following code:

function add(){  
  return [...arguments].reduce((a,b) => +a + +b, [])  
}  
console.log(add(1,2,3));

We get 6 logged in the last line. This is because the arguments object had all the arguments of the object in string form. So arguments would have '1' , '2' , and '3' listed. However, if we try to do that we an arrow function, we can’t get the sum of the arguments with the arguments object. For example, if we have:

const add = () => [...arguments].reduce((a,b) => +a + +b, [])  
  
console.log(add(1,2,3));

We get NaN because we didn’t get the arguments of the function call in the arguments object.

Arrow functions can’t be used as constructors, therefore we can’t instantiate it with thenew keyword. For example, if we write the following and run it:

const Obj = () => {};  
let obj = new Obj();

We would get a TypeError .

Also, arrow functions don’t have a prototype property, so when we log something like:

const Obj = () => {};  
console.log(Obj.prototype);

We get undefined . This means we can‘t inherit from an arrow function. The yield keyword can’t be used in an arrow’s functions’s body. Therefore, we can’t use arrow functions as generator functions.

Arrow functions are useful for times when we need to write a function more concisely than writing traditional functions and we don’t need to change the content of the this object inside the function. Also, we can’t use them for generator functions or constructor since they don’t have their own this object. They also don’t have the prototype property so they can’t be inherited from. Also, the arguments of an arrow function call cannot be retrieved from the arguments object, so we just get them explicitly from the parameters.

Categories
JavaScript

Defining and Manipulating Classes and Objects in JavaScript

In JavaScript, objects are entities that allow us to encapsulate data, functions, and other objects into one entity that we can access and manipulate. This is another fundamental building block of JavaScript programs.

Objects are used for modeling real-world data. An example would be a bird. We know that it has wings, a head, two legs, and two wings. Also, we know that it can chirp, eat, and fly.

We can model them in JavaScript by using an object. The body parts are the properties of the object, and the actions that it does are the methods.

Methods are functions that are part of the object. Properties are anything denoted with a key-value pair in JavaScript. Methods are special properties. When a property has a function as the value, it’s called a method.

For example, if we model a bird as an object, we can write:

const bird = {  
  numWings: 2,  
  numLegs: 2,  
  numHeads: 1,  
  fly(){},  
  chirp(){},  
  eat(){}  
}

fly(){}, chirp(){}, and eat(){} are methods and the rest are properties with data as values.


Creating Objects

In JavaScript, we can define objects in three ways. We can either define them as object literals, by using the object constructor method, or by using a class.

Object literal

Defining objects with object literals is straightforward. We just have to specify the properties and methods of an object directly. For example, we write:

const bird = {  
  name: 'Joe',  
  numWings: 2,  
  numLegs: 2,  
  numHeads: 1,  
  fly(){},  
  chirp(){},  
  eat(){}  
}

To define an object as an object literal.

If we want to add more properties, we write:

bird.sleep = function(){};

We can assign anything as values of object properties, so strings, booleans, functions, arrays, and other objects all can be set as properties.

Object constructor

Equivalently, we can use the object constructor method to define an object. We can write:

let bird = new Object();  
bird.name = 'Joe';  
bird.numWings = 2;  
bird.numLegs = 2;  
bird.numHeads = 1;

This is a long way to define an object. It’s slower and harder to read than object literals.

Define object instances with classes

We can also define JavaScript by first defining a class, then using the new keyword to create an object, which is an instance of the class.

To do this, we write:

class Bird {  
  constructor(name, numWings) {  
    this.name = name;  
    this.numWings = numWings;  
  } 

  logProperties() {  
    console.log(this)  
  }  
}

const bird = new Bird('Joe', 2);  
bird.logProperties();

We have the Bird class, which is a template for creating an object with the name and numWings properties and the getProperties method.

The Bird class has a constructor function that lets us pass in values for the properties and set it as properties of the object being created. It also has the logProperties function.

this is set as the object created as we aren’t using arrow functions as methods.

The object is defined by using the new keyword and passing in the data into the constructor. Once we’ve defined the object, we can call the methods defined inside it.

In this example, we called bird.logProperties to log the value of this, which should be {name: “Joe”, numWings: 2}.


Prototypes

In JavaScript, prototypes are templates that let you create other objects. They can also be used for inheritance in JavaScript. So, if we have an Animal object defined as:

function Animal(){  
  this.name = 'Joe'  
}

We can extend it by creating a new object, like so:

let bird = animal.prototype;  
bird.fly = function() {};  
bird.chirp = function() {};

Then we get the fly and chirp methods, as well as the name property in the bird object.


Defining Functions in an Object

We can define a function in an object in a few ways.

We can use the function keyword or arrow function as usual, but we can also write it with a shorthand for the function keyword. For example, if we have a bird object and we want to define the chirp function, we can write:

const bird = {  
 chirp: function(){  
   console.log('chirp', this)  
  }  
}

Or use the following shorthand:

const bird = {  
 chirp(){  
   console.log('chirp', this)  
  }  
}

The two are the same as the chirp function will have the bird object as the value of this.

On the other hand, if you use the arrow function:

const bird = {  
 chirp: () => {  
   console.log('chirp', this)  
  }  
}

this will be logged as the global window object, as arrow functions do not change the value of this to the object that the function is in.


Getting and Setting Object Properties

After an object is defined, it’s very useful to be able to get and set the properties of an object. There are two ways to get a property of an object. One is to use the dot notation, and another is the square bracket notation.

The dot notation allows us to get properties that have property names that follow the variable naming conventions. That means they start with a letter, underscore, or dollar sign and have no spaces or other special characters.

So, if we have:

const bird = {  
  name: 'Joe',  
  numWings: 2,  
  numLegs: 2,  
  numHeads: 1,  
  fly(){},  
  chirp(){},  
  eat(){}  
}

Then, we can access the name property by writing bird.name.

The alternative syntax, which is the square bracket notation, can do the same thing as the dot notation, as well as access properties dynamically.

It also lets us access properties that are defined with names that don’t follow the variable name creation rules. To use the square bracket notation to get object properties, we write: bird['name']. We can also write:

const prop = 'name';  
bird[prop];

This allows us to get properties of an object by passing in variables and string, which is handy for dynamically modifying objects and using objects as dictionaries, as we can traverse the keys and modify them as well as the values.

To set object properties with both notations, we use the assignment operator, like so:

bird.name = 'Jane';

Or, with square bracket notation, we write:

bird['name']= 'Jane';

Get All the Top-Level Properties of an Object

All JavaScript has the following functions to get all the top-level properties of an object.

Object.keys

Object.keys gets the top level list of keys of an object and returns an array of them. For example:

const a = {foo: 1, bar: 2};  
const length = Object.keys(a).length // 2

You can call find on the keys array to look for the key, like the following:

Object.keys(a).find(k => k == 'foo') // 'foo'

Object.getPropertyNames

Object.getPropertyNames also gets a list of all top levels of keys of an object and returns them as an array. For example:

const a = {foo: 1, bar: 2};  
const length = Object.getOwnPropertyNames(a).length // 2

You can call find on the keys array to look for the key, like the following:

Object.getOwnPropertyNames(a).find(k => k == 'foo') // 'foo'

for…in Loop

There is also a special loop for looping through the keys of an object. You can do the following:

const a = {foo: 1, bar: 2};  
let keysCount = 0;  
for (let key in a) {  
    keysCount++;  
}  
console.log(keysCount) // 2

Checking If an Object Property Exists

hasOwnProperty

You can check if an object has a property by calling hasOwnProperty of an object. For example:

const a = {foo: 1, bar: 2};  
a.hasOwnProperty(key); // true

Deleting Properties of an Object

JavaScript has the delete operator to remove a property of an object. If we have:

const bird = {  
  name: 'Joe',  
  numWings: 2,  
  numLegs: 2,  
  numHeads: 1,  
  fly(){},  
  chirp(){},  
  eat(){}  
}

Then we run delete bird.name, then the bird.name property will be removed. When you log it, it will log as undefined.


JavaScript Inheritance

In JavaScript, we can create classes where the properties can be included in the properties of a child class.

So, we can have a high-level class that contains the properties that are common to all the child classes, and the child class can have its own special properties that are not in any other classes.

For example, if we have an Animal class with the common properties and methods, like name and the eat method, then the Bird class can just inherit the common properties in the Animal class. They don’t have to be defined in the Bird class again.

We can write the following to do inheritance in JavaScript:

class Animal {  
  constructor(name) {  
    this.name = name;  
  }  
  eat() {  
    console.log('eat');  
  }  
}

class Bird extends Animal {  
  constructor(name, numWings) {  
    super(name);  
    this.numWings = numWings;  
  }  
}

const bird = new Bird('Joe', 2);  
console.log(bird.name)  
bird.eat();

In the example above, we have the parent class, Animal, that has the eat method, which all classes that extends from Animal will have, so they don’t have to define eat again.

We have the Bird class which extends the Animal class. Note that in the constructor of the Bird class, we have the super() function call to call the parent’s class constructor to populate the properties of the parent class in addition to the properties of the child class.


this Keyword

The this keyword allows us to access the current object’s properties inside an object unless you’re using arrow functions.

As we can see from the above example, we can get the properties of the instance of the child and the parent class in the object.


Copying Objects

Copying objects means making a new object reference to an object that has the same contents as the original.

It is used a lot to prevent modifying the original data while you assign a variable to another variable. Because if you assign a variable to a new one, the new one has the same reference as the original object.

There are a few ways to clone objects with JavaScript. Some functions do shallow copying, which means that not all levels of the object are copied, so they may still hold a reference to the original object.

A deep copy copies everything so that nothing references the original object, eliminating any confusion which arises from a shallow copy.

If you assign an object to another variable, it just assigns the reference to the original object, so both variables will point to the original object.

When one of the variables is manipulated, both will be updated. This is not always the desired behavior. To avoid this, you need to copy an object from one variable to another.

Shallow copy

In JavaScript, this is easy to do. To shallow copy an object, we can use Objec.assign(), which is built into the latest versions of JavaScript.

This function does a shallow copy, which means it only copies the top level of an object, while the deeper levels remain linked to the original object reference. This may not be desired if there are nested ones in your original object.

Here is an example of how to use Object.assign:

const a = { foo: {bar: 1 }}  
const b = Object.assign({}, a) // get a clone of a which you can change with out modifying a itself

You can also clone an array like this:

const a = [1,2,3]  
const b = Object.assign([], a) // get a clone of a which you can change with out modifying a itself

Deep copy

To do a deep copy of an object without a library, you can JSON.stringify then JSON.parse:

const a = { foo: {bar: 1, {baz: 2}}  
const b = JSON.parse(JSON.strinfy(a)) // get a clone of a which you can change without modifying a itself

This does a deep copy of an object, which means all levels of an object are cloned instead of referencing the original object.

JSON.parse and JSON.stringify only work with plain objects, which means they cannot have functions and other code that runs.

We can also use object destructuring to shallow clone objects, like so:

const a = { foo: {bar: 1}}  
const b = {...a} // get a clone of a which you can change with out modifying a itself

Now that we know how to create objects, we can easily store and manipulate data. We can create programs that do non-trivial things with the use of objects and classes.