Categories
JavaScript JavaScript Basics

Adding Inputs and Outputs to JavaScript Apps

For many web apps, inputs and outputs are important parts of the app. Many parts of most applications consist of forms. JavaScript makes it easy to add forms and handle inputs and then get the outputs. An HTML form consists of many parts. It can have text inputs, text areas, checkboxes, radio buttons, dropdowns, buttons, etc. The look of them can be adjusted with CSS, and JavaScript lets you add dynamic functionality to the form.

The form element is the HTML element that contains the controls for forms. It holds all the text inputs, radio buttons, checkboxes, text areas, etc., along with the label text for each element. It can also contains HTML elements that divide the form into sections like div , article , and section elements.

To make a simple form, we can write the following:

<html>  
  <head>  
    <title>Form</title>  
  </head>  
  <body>  
    <form action="save.php" name="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 />  
      <input type="submit" value="Submit" />  
    </form>  
  </body>  
</html>

In the example above, we have the action attribute in the form tag to tell the browser what to do with the form when the submit action is triggered. It can be sent to the server-side directly, or it can be manipulated on the client-side first before sending it to the server via Ajax. The name attribute is the name that the programmer assigns to the form. It’s a handy reference for accessing it in our JavaScript code. The method attribute is used when we send the form data to the server directly and it’s used to specify the HTTP method used to send the data to the server.

Other attributes that form elements can have include:

  • accept-charset: specifies the character set to use when sending the data to the server. It’s useful if your form inputs have multilingual content.
  • autocomplete: specifies whether the form should use browser’s autocomplete
  • enctype: indicates the type of content that’s sent to the server. If the data sent to the server is all text, then it should be text/html. If the form has file inputs, then it should be multipart/form-data. The default value is application/x-www-form-urlencoded .
  • novalidate: a boolean value that specifies whether we want to use browser’s form validation for form inputs
  • target: specifies whether we want the server’s response should be displayed after the form is submitted. The default setting is to open the response in the same browser window (value is _self ). If you want the response data to be shown in a new window, then it should be set to _blank.

Label Element

The label element is used to specify the text associated with a form field. It makes it clear to the user which field is used for entering what kind of data. It has a for attribute that takes the id of the input element as the value to associate the label with the form field. You can also nest the form field in the label element to associate the form field with the input element, so you don’t have to have an id attribute in your input field.

Input Element

Almost all forms have an input element. It’s used for entering text. Input elements have the type attribute which is usually set to text as the value. Other possible values include number , email , password , etc. The full list of types are listed below. Inputs also can have a value attribute which lets us set the value entered into the form field. It’s handy if you want to preset the value of the form field. The name attribute is also an important one since it lets us get the input by name and get the value from it.

Below is the full list of possible inputs that we can set in the type attribute:

  • button — a clickable button
  • checkbox — checkbox
  • color — color picker
  • date — date control
  • datetime — control to let us enter date and time (year, month, day, hour, minute, second, and a fraction of a second in UTC)
  • datetime-local — control to let us enter date and time (year, month, day, hour, minute, second, and a fraction of a second in local time)
  • email — email address
  • file — file upload
  • hidden — hidden input
  • image — submit button that has an image instead of text
  • month — month and year picker
  • number — input that only takes numbers
  • password — password field
  • radio — radio button
  • range — input that takes a range of numbers
  • reset — reset button
  • search — input that takes a search string
  • submit — submit button
  • tel — phone number input
  • text — a single line of text input. This is the default choice.
  • time — time input without taking into account the time zone
  • url — URL input
  • week — week and year input without taking into account the time zone

Select Element

The select element is a drop-down that lets users select from one or more choices in the drop-down list. We can also have default choices with the selected attribute and group the drop-down options with optgroup.

A simple example would be:

<select name='color'>  
  <option color='white'>White</option>  
  <option color='yellow'>Yellow</option>  
  <option color='black'>Black</option>  
</select>

If we want to group items together we can write:

<select name='car'>  
  <optgroup label="American Cars">  
    <option value="ford">Ford</option>  
    <option value="gm">GM</option>  
  </optgroup>  
  <optgroup label="Japanese Cars">  
    <option value="toyota">Toyota</option>  
    <option value="mazda">Mazda</option>  
  </optgroup>  
</select>

An option can have a selected attribute to make a choice a preset choice:

<select name='color'>  
  <option color='white' selected>White</option>  
  <option color='yellow'>Yellow</option>  
  <option color='black'>Black</option>  
</select>

Textarea

A textarea element is a multline text input field:

<textarea name='recipe' rows='10' cols='50'></textarea>

Button

We can use the button element to create our submit button instead of an input with type attribute set to submit . We can write:

<button type='submit'>Submit</button>

which is the same as:

<input type='submit value='Submit' >

The Form Object

The form object is an HTML DOM object that represents the form element that you defined in HTML. We can manipulate the form object to get the input and set different options to our liking, to change the behavior of the form. With the form object, we get full access to all parts of the form.

A form object has the following properties:

  • acceptCharset — get or set a list of character sets supported by the server
  • action — get or set the action attribute of the form
  • autocomplete — get or set whether to turn on or off autocomplete attribute of the form
  • encoding — set the enctype attribute of the form, or the way data is encoding when sending to the server
  • enctype — set the enctype attribute of the form, or the way data is encoding when sending to the server
  • length — get the number of controls in a form
  • method — get or set the HTTP method used to submit the form data
  • name — get or set the name attribute of the form
  • noValidate — get or set the novalidate attribute of the form, or whether validation can be skipped by the browser
  • target — indicate the place to show the response data after submitting a form

Autocomplete Attribute

The autocomplete attribute in a form tells the browser whether to use its autocomplete feature to let users enter data by getting existing input data that was saved from previous data entries. We can set it to form the whole form or just single inputs.

Getting Form and Input Attributes

We can get a form’s attribute like getting the attributes of any other element. So we can use getElementById , getElementsByTagName , querySelector , querySelectorAll , or getElementsByClassName to get what we want.

For example, we can use document.querySelector('form') to get the first element with the form tag, which is a convenient way of getting forms. If we have more than one form element in one page, then we can get it by using document.querySelector('form')[0] to get the first one, document.querySelector('form')[1] to get the second one, etc.

Also, there is the document.forms property, which we can use to access a form if the form has a name attribute. If it has a name attribute, then we can pass it in as the key. For example, if there’s a form with name attribute set to nameForm , then we can write document.forms.nameForm or document.forms['nameForm'] to get the form.

A form object has a methods, reset() and submit() . The reset method clears the input values and reset all validation previously done to the form. It’s the same as having a input with type reset in the form, which also clears the form’s inputs, like the following:

<input type='reset' value='Clear form'>

The submit method submits the form data to the place you want depending on the action, method and target attributes. It’s the same as what an input element with type submit does, like the following:

<input type='submit' value='Submit'>

We have the example below for making a form that displays an alert of the inputted values after the Submit button is clicked. In index.html, we add our form by adding the following:

<html>  
  <head>  
    <title>Form</title>  
  </head>  
  <body>  
    <form action="" name="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 />  
      <input type="submit" value="Submit" />  
    </form>  
    <script src="script.js"></script>  
  </body>  
</html>

Then in script.js , we add:

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");  
    const lastName = document.getElementById("lastName");  
    alert(`Your name is ${firstName.value} ${lastName.value}`);  
  });  
};

We use the document.forms property to get the form with name attribute set to nameForm in the script. Then we set the method , target , and action properties. We do not want to submit to a server immediately, so in the handler function that we pass into the addEventListener function, we add e.preventDefault() to prevent the submit action. Then we get the values of the firstName and lastName field and display an alert from it.

Other ways to get input fields in a form element in this example is to use nameForm.firstName instead of document.getElementById(“firstName”), or set an ID for the form and then get the form element by ID then access the form field with the given name as a property of the form object. So if we have:

<html>  
  <head>  
    <title>Form</title>  
  </head>  
  <body>  
    <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 />  
      <input type="submit" value="Submit" />  
    </form>  
    <script src="script.js"></script>  
  </body>  
</html>

in index.html , then in script.js , we can write:

document.getElementById("nameForm").firstName

to get the first name field instead of what we have above.

Setting Form Values

We can set form values with the value property of an input element. We can access the form elements like we did before, and set the value attribute of a form element. For example, if we have index.html with the following code:

<html>  
  <head>  
    <title>Form</title>  
  </head>  
  <body>  
    <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 />  
      <input type="submit" value="Submit" />  
    </form>  
    <script src="script.js"></script>  
  </body>  
</html>

and script.js with:

window.onload = () => {  
  const nameForm = document.forms.nameForm;  
  nameForm.method = "post";  
  nameForm.target = "_blank";  
  nameForm.action = "";  
  const firstName = document.getElementById("firstName");  
  const lastName = document.getElementById("lastName");  
  firstName.value = "John";  
  lastName.value = "Smith"; nameForm.addEventListener("submit", e => {  
    e.preventDefault();  
    alert(`Your name is ${firstName.value} ${lastName.value}`);  
  });  
};

Then we set the default value of firstName and lastName fields by setting the value property of both field objects when the page is loaded.

Form Validation

Most forms need to be validated for valid data before submitting the form data to ensure that we don’t get bad data. With JavaScript, we can use regular expressions to validate the content of each input to ensure that they’re entered in the correct format. We can extend the examples above to include form validation. HTML5 also provides attributes to validate form data in HTML instead of JavaScript. However, if you want to customize your validation, then JavaScript makes this possible.

To validate forms with JavaScript, we can do the following. In index.html, we put:

<html>  
  <head>  
    <title>Form</title>  
  </head>  
  <body>  
    <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="Submit" />  
    </form>  
    <script src="script.js"></script>  
  </body>  
</html>

Then in script.js , we add:

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");  
    const lastName = document.getElementById("lastName");  
    const email = document.getElementById("email");  
    let errors = [];  
    if (!firstName.value) {  
      errors.push("First name is required.");  
    } 

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

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

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

    if (errors.length > 0) {  
      alert(errors.join(" "));  
      return;  
    }  
    alert(  
      `Your name is ${firstName.value} ${lastName.value}. Your email is ${email.value}`  
    );  
  });  
};

In the example above, we check each field’s value to see if they’re filled in. Then for the email field, we check if the field is filled in and that it matches the email format with a regular expression check. To learn more about regular expressions, you can go to https://regexone.com/. The regular expression we have basically checks if we have characters before the at sign, then the at sign, then check if we have anything separated by a dot after the at sign.

If there are errors, we display an alert box with all the errors. Otherwise, we show what’s entered.

If we want to use HTML for validation, we can do the following instead. In index.html, we put:

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

Then in script.js , we put:

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");  
    const lastName = document.getElementById("lastName");  
    const email = document.getElementById("email");  
    alert(  
      `Your name is ${firstName.value} ${lastName.value}. Your email is ${email.value}`  
    );  
  });  
};

We’ve put validation attributes in the input elements like required and pattern in the HTML form instead of using JavaScript to validate the data. They serve the same purpose since the browser prevents you from submitting the form if there’re invalid values in any input, so the submit event is only triggered if everything’s valid. The only difference is that the validation messages are controlled by the browser so there’s less choice in controlling how it’s displayed.

Now that we know how to add forms to a web page with form validation, we can make many features of a web app. For many web apps, inputs and outputs are important parts of the app. Many parts of most applications consist of forms.

Categories
JavaScript JavaScript Basics

Introducing the JavaScript Destructuring Assignment Syntax

The destructuring assignment syntax is a JavaScript syntax feature that was introduced in the 2015 version of JavaScript and lets us unpack a list of values of an array or key-value pairs of an object into individual variables.

It’s very handy for retrieving entries from arrays or objects and setting them as values of individual variables. This is very handy because the alternative was to get an entry from an array from an index and then setting them as values of variables for arrays.

For objects, we have the value from the key and set them as values of variables.


Array Destructuring

We can use the destructuring assignment syntax easily in our code. For arrays, we can write:

const [a,b] = [1,2];

Then, we get 1 as the value of a and 2 as the value of b because the destructing syntax unpacked the entries of an array into individual variables.

Note that the number of items in the array does not have to equal the number of variables. For example, we can write:

const [a,b] = [1,2,3]

Then a is still 1 and b is still 2 because the syntax only sets the variables that are listed in the same order as the numbers appeared in the array. So, 1 is set to a, 2 is set to b, and 3 is ignored.

We can also use the rest operator to get the remaining variables that weren’t set to variables. For example, we can have:

const [a,b,...rest] = [1,2,3,4,5,6]

Then, rest would be [3,4,5,6] while we have a set to 1 and b set to 2. This lets us get the remaining array entries into a variable without setting them all to their own variables.

We can use the destructuring assignment syntax for objects as well. For example, we can write:

const {a,b} = {a:1, b:2};

In the code above, a is set to 1 and b is set to 2 as the key is matched to the name of the variable when assigning the values to variables.

Because we have a as the key and 1 as the corresponding value, the variable a is set to 1 as the key name matches the variable name. It is the same with b. We have a key named b with a value of 2, because we have the variable named b, we can set b to 2.

We can declare variables before assigning them with values with the destructuring assignment syntax. For example, we can write:

let a, b;  
([a, b] = [1, 2]);

Then, we have a set to 1 and b set to 2 because a and b that were declared are the same ones that are assigned.

As long as the variable names are the same, the JavaScript interpreter is smart enough to do the assignment regardless of whether they’re declared beforehand or not.

We need the parentheses on the line so that the assignment will be interpreted as one line and not individual blocks with an equal sign in between, because two blocks on the same line aren’t valid syntax.

This is only required when the variable declarations happen before the destructuring assignment is made.

We can also set default values for destructuring assignments. For instance:

let a,b;  
([a=1,b=2] = [0])

This is valid syntax. In the code above, we get that a is 0 because we assigned 0 to it. b is 2 because we didn’t assign anything to it.

The destructuring assignment syntax can also be used for swapping variables, so we can write:

let a = 1;  
let b = 2;  
([a,b] = [b,a])

b would become 1 and a would become 2 after the last line of the code above. We no longer have to assign things to temporary variables to swap them, and we also don’t have to add or subtract things to assign variables.

The destructuring assignment syntax also works for assigning returned values of a function to variables.

So, if a function returns an array or object, we can assign them to variables with the destructuring assignment syntax. For example, if we have:

const fn = () =>[1,2]

We can write:

const [a,b] = fn();

To get 1 as a and 2 as b with the destructuring syntax because the returned array is assigned to variables with the syntax.

Similarly, for objects, we can write:

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

We can ignore variables in the middle by skipping the variable name in the middle of the destructuring assignment. For example, we can write:

const fn = () => [1,2,3];  
let [a,,b] = fn();

We get a with the value of 1 and b with the value of 3, skipping the middle value.

It’s important to know that if we use the rest operator with a destructuring assignment syntax, we cannot have a trailing comma on the left side, so this:

let [a, ...b,] = [1, 2, 3];

Will result in a SyntaxError.


Object Destructuring

We can use the destructuring assignment syntax for objects as well. For example, we can write:

const {a,b} = {a:1, b:2};

In the code above, a is set to 1 and b is set to 2 because the key is matched to the name of the variable when assigning the values to variables.

As we have a as the key and 1 as the corresponding value, the variable a is set to 1 because the key name matches the variable name. It is the same with b. We have a key named b with a value of 2, because we have the variable named b, we can set b to 2.

We can also assign it to different variable names, so we don’t have to set the key-value entries to different variable names. We just have to add the name of the variable we want on the value part of the object on the left side, which is the one we want to assign it to, like the following:

const {a: foo, b: bar} = {a:1, b:2};

In the code above, we assigned the value of the key a to foo and the value of the key b to the variable bar. We still need a and b as the keys on the left side so they can be matched to the same key names on the right side for the destructuring assignment.

However, a and b aren’t actually defined as variables. It’s just used to match the key-value pairs on the right side so that they can be set to variables foo and bar.

Destructuring assignments with objects can also have default values. For example, we can write:

let {a = 1, b = 2} = {a: 3};

Then, we have a set to 3 and b set to 2 which is the default value as we didn’t have a key-value pair with a key named b on the right side.

Default values can also be provided if we use the destructuring syntax to assign values to variables that are named differently from the keys of the originating object. So, we can write:

const {a: foo=3, b: bar=4} = {a:1};

In this case, foo would be 1 and bar would be 4 because we assigned the left bar with the default value, but assigned foo to 1 with the destructuring assignment.

The destructuring assignment also works with nested objects. For example, if we have the following object:

let user = {  
  id: 42,  
  userName: 'dsmith',  
  name: {  
    firstName: 'Dave',  
    lastName: 'Smith'  
  }  
};

We can write:

let {userName, name: { firstName }} = user;

To set displayName to 'dsmith' , and firstName to 'Dave'. The lookup is done for the whole object and, so, if the structure of the left object is the same as the right object and the keys exist, the destructuring assignment syntax will work.

We can also use the syntax for unpacking values into individual variables while passing objects in as arguments.

To do this, we put what we want to assign the values, which is the stuff that’s on the left side of the destructuring assignment expression, as the parameter of the function.

So, if we want to destructure user into its parts as variables, we can write a function like the following:

const who = ({userName, name: { firstName }}) => `${userName}'s first name is ${firstName};who(user)

So, we get userName and firstName, which will be set as 'dsmith' and 'Dave' respectively as we applied the destructuring assignment syntax to the argument of the who function, which is the user object we defined before.

Likewise, we can set default parameters as with destructuring in parameters like we did with regular assignment expressions. So, we can write:

const who = ({userName = 'djones', name: { firstName }}) => `${userName}'s first name is ${firstName}`

If we have user set to:

let user = {  
  id: 42,  
  name: {  
    firstName: 'Dave',  
    lastName: 'Smith'  
  }  
};

Then we when call who(user), we get 'djones's first name is Dave' as we set 'djones' as the default value for userName.

We can use the destructuring assignment syntax when we are iterating through iterable objects. For example, we can write:

const people = [{  
    firstName: 'Dave',  
    lastName: 'Smith'  
  },  
  {  
    firstName: 'Jane',  
    lastName: 'Smith'  
  },  
  {  
    firstName: 'Don',  
    lastName: 'Smith'  
  },  
]

for (let {  firstName, lastName } of people) {  
  console.log(firstName, lastName);  
}

We get:

Dave Smith  
Jane Smith  
Don Smith

Logged, as the destructuring syntax works in for...of loops because the variable after the let is the entry of the array.

Computed object properties can also be on the left side of the destructuring assignment expressions. So, we can have something like:

let key = 'a';  
let {[key]: bar} = {a: 1};

This will set bar to 1 because [key] is set to a and then the JavaScript interpreter can match the keys on both sides and do the destructuring assignment to the variable bar.

This also means that the key on the left side does not have to be a valid property or variable name. However, the variable name after the colon on the left side has to be a valid property or variable name.

For instance, we can write:

const obj = { 'abc 123': 1};  
const { 'abc 123': abc123 } = obj;  
  
console.log(abc123); // 1

As long as the key name is the same on both sides, we can have any key in a string to do a destructuring assignment to variables.

Another thing to note is that the destructuring assignment is smart enough to look for the keys on the same level of the prototype chain, so if we have:

var obj = {a: 1};  
obj.__proto__.b = 2;  
const {a, b} = obj;

We still get a set to 1 and b set to 2 as the JavaScript interpreter looks for b in the prototype inheritance chain and sets the values given by the key b.

As we can see, the destructuring assignment is a very powerful syntax. It saves lots of time writing code to assign array entries to variables or object values into their own variables.

It also lets us swap variables without temporary variables and makes the code much simpler and less confusing. It also works through inheritance so the property does not have to be in the object itself, but works even if the property is in its prototypes.

const fn = () => {a:1, b:2}  
const {a,b} = fn();
Categories
JavaScript JavaScript Basics

Introducing the JavaScript Spread Operator

The spread syntax allows us to break up a collection of objects, like arrays, into individual arguments or insert them into a different iterable object, like an array.

With the 2018 version of JavaScript, we can also spread properties of an object into another object, with keys and values spread into another object. The spread syntax is denoted by three periods before your object.

For example, we can write:

...arr

The spread syntax works by copying the values of the original array and then inserting them into another array, or putting them in the order they appeared in the array as the list of arguments in a function in the same order.

When the spread operator is used with objects, the key-value pairs appear in the same order they appeared in the original object.

We can use the spread syntax to spread an array of values as arguments of a function. For example, we can write:

const arr = [1,2,3];  
const add = (a,b,c) => a+b+c;  
add(...arr) // returns 6

In the example above, the spread operator spreads the variables into the argument in the same order they appeared in the array. So 1 is passed into a, 2 is passed into b, and 3 is passed into c.


Spread Arrays

For arrays, we can also use the spread syntax to insert one array’s values into another array. For example, we can write:

const arr = [1,2,3];  
const arr2 = ['a','b','c',...arr,'d']; // arr2 is ['a','b','c',1,2,3,'d']

As we can see, the spread operator inserts the values exactly where we spread the array, in the same order they appeared in the array.

So, 1 is inserted between a and d, then 2 is inserted between 1 and d, and 3 is inserted between 2 and d. The result is that we copied an array’s values into another array with the spread operator in the same order they appeared in, and exactly where you put the array spread expression.

Without the spread operator, we have to write loops to insert them into the position we want. We slice the array into two and then call concat on the three parts, then assign the result to the array you inserted the stuff into. It sounds painful just thinking about it.

Note that with the spread operator, only the first level of the array is spread. If we have nested or multi-dimensional arrays, it’s going to copy the references as-is. It will not do anything to nested items.

With ES2018, we can do the same thing with objects, like the following:

const obj = {a: 1, b: 2};  
let objClone = { ...obj }; // objClone is {a: 1, b: 2}

This creates a shallow copy of the object. It means that only the first level of the object is copied.

For nested objects, it’s going to copy the references as-is. It will not do anything to nested items. The top-level keys and values of the object will be copied to objClone.

So, if we have nested objects, we get:

const obj = {  
  a: 1,  
  b: {  
    c: 2  
  }  
};  
let objClone = {  
  ...obj  
};  
console.log(objClone) 

In objClone, we get:

{  
  a: 1,  
  b: {  
    c: 2  
  }  
}

So, nested objects will reference the same ones as the original.

The spread operator can be used as an alternative to other functions that existed before.

For example, we can use it to replace the apply function for passing in arguments to a function. The apply function takes an array of arguments for the function it’s called on as the second argument.

With the apply function, we call it as follows:

const arr = [1,2,3]  
const sum = (a,b,c)=> a+b+c;  
sum.apply(null, arr); // 6

With the spread syntax, we can write the following instead:

const arr = [1,2,3]  
const sum = (a,b,c)=> a+b+c;  
sum(...arr)

The spread operator also work with strings. We apply the spread operator to strings, we get an array with the individual characters of the string.

For example, if we write:

const str = 'abcd';  
const chars = [...str];

We get [“a”, “b”, “c”, “d”] as the value of chars.


Using Spread Operator Multiple Times

We can use the spread syntax multiple times in one place. For example, we can have the following:

const arr = [1,2,3];  
const arr2 = [4,5];  
const sum = (a,b,c,d,e,f)=> a+b+c+d+e+f;  
sum(...arr, ...arr2, 6)

As usual, the spread syntax will spread the array of numbers into arguments of the array in the order as they appeared in.

So, sum(…arr, …arr2, 6) is the same as sum(1,2,3,4,5,6).

1, 2, and 3 are the first three arguments, which are the entries of arr in the same order, and 4 and 5 are the fourth and fifth arguments, which are spread after 1, 2, and 3.

Then, in the end, we have 6 as the last argument. We can also see the spread syntax work with the normal function call syntax.


Use It in Constructors

We can use the spread operator as arguments for object constructors. For example, if we want to create a new Date object, we can write:

let dateFields = [2001, 0, 1];  
let date = new Date(...dateFields);

The items in the dateFields array are passed into the constructors as arguments in the order they appeared in. The alternative way to write that would be much longer, something like:

let dateFields = [2001, 0, 1];  
const year = dateFields[0];  
const month = dateFields[1];  
const day = dateFields[2];  
let date = new Date(year, month, day);

Copying Items

The spread syntax can also be used to make a shallow copy of an array or an object as it works by creating copies of the top-level elements of an array or key-value pairs of an object and then inserting them into the place you used the spread operator with.

For copying arrays, we can write:

const arr = [1, 2, 3];  
const arr2 = [...arr, 4, 5];

The above example, arr2, is [1,2,3,4,5], while arr1 is still [1,2,3].

arr1 is not referenced by arr2 because the spread operator actually makes a copy of the array and then inserts the values. Note that this doesn’t work with multi-dimensional arrays as it only makes copies of the top-level elements.

We can apply the spread syntax multiple times in one array or object. An example for array would be:

let arr = [1, 2, 3];  
let arr2 = [4, 5];  
let arr3 = [...arr2, ...arr];

In the above example, we get [4,5,1,2,3]. arr1 and arr2 are unaffected as a copy of the values from arr1 and arr2 are inserted into arr3.


Spread Operator and Objects

With ES2018, the spread operator works with object literals. Then, key-value pairs of an object can be inserted into another object with the spread operator.

If there are two objects with the same key that the spread operator is applied to in the same object, the one that’s inserted later will overwrite the one that’s inserted earlier.

For example, if we have the following:

let obj1 = {foo: 'bar', a: 1};  
let obj2 = {foo: 'baz', b: 1};  
let obj3 = {...obj1, ...obj2 }

Then we get {foo: “baz”, a: 1, b: 1} as the value of obj3 because obj1 is spread before obj2.

They both have foo as a key in the object. First, foo: 'bar' is inserted by the spread operator to obj3. Then, foo: 'baz' overwrites the value of foo after obj2 is merged in, as it has the same key foo but inserted later.

This is great for merging objects as we don’t have to loop through the keys and put in the values, which is much more than one line of code.

One thing to note is that we can’t mix the spread operator between regular objects and iterable objects. For example, we will get TypeError if we write the following:

let obj = {foo: 'bar'};  
let array = [...obj];

Conclusion

As we can see, the spread syntax is a great convenience feature of JavaScript. It lets us combine different arrays into one.

Also, it lets us pass arrays into a function as arguments with just one line of code. With ES2018, we can also use the same operator to spread key-value pairs into other objects to populate one object’s key-value pairs into another object.

The spread operator works by copying the top-level items and populating them in the place you use the spread operator, so we can also use it to make shallow copies of arrays and objects.

Categories
JavaScript JavaScript Basics

Introducing JavaScript Template Strings

Template strings are a powerful feature of modern JavaScript released in ES6. It lets us insert/interpolate variables and expressions into strings without needing to concatenate like in older versions of JavaScript. It allows us to create strings that are complex and contain dynamic elements. Another great thing that comes with template strings is tags. Tags are functions that take a string and the decomposed parts of the string as parameters and are great for converting strings to different entities.

The syntax for creating template strings is by using backticks to delimit them. For example, we can write:

`This is a string`

This is a very simple example of a template string. All the content is constant and there are no variables or expressions in it. To add variables and or expressions to a string, we can do the following.

Interpolating Variables and Expressions

// New JS with templates and interpolation  
const oldEnoughToDrink = age >= 21;  
const oldEnough = `You are ${oldEnoughToDrink ? 'old enough' : 'too young'} to drink.`

We insert a variable or expression by adding a dollar sign $ and curly braces {} into the string `${variable}`. This is much better than the alternative in old JavaScript where we had to concatenate a string like the following:

// Old JS using concatenation  
const oldEnoughToDrink = age >= 21;  
const oldEnough = 'You are ' + (oldEnoughToDrink ? 'old enough' : 'too young') + ' to drink.'

As we can see it’s easy to make syntax errors with the old concatenation syntax if we have complex variables and expressions. Therefore template strings are a great improvement from what we had before.

If we want to use the backtick character in the content of string just put a \ before the backtick character in the string. => `will have a literal ` backtick`

Multiline Strings

Another great feature of template strings is that we can have strings with multiple lines for better code readability. This cannot be done in an easy way with the old style of strings. With the old style of strings, we had to concatenate each line of the string to put long strings in multiple lines like the following examples:

const multilineStr = 'This is a very long string' +   
  'that spans multiple lines.'

const multllineStr2 = 'At vero eos et accusamus et iusto odio' + 'dignissimos ducimus qui blanditiis praesentium voluptatum ' + 'deleniti atque corrupti quos dolores et quas molestias ' + 'excepturi sint occaecati cupiditate non provident, ' +   
'similique sunt in culpa qui ' +   
'officia deserunt mollitia animi.'

As we can see, this will become really painful is we have many more lines. It’s very frustrating to have all those plus signs and divide the string into multiple lines.

With template strings, we don’t have this problem:

const longString = `At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi.`

This is much better than concatenating strings together. It takes a lot less time to write the code and a lower probability of syntax errors. It’s also much more readable.

Note that this method does add an actual new like to the string \n and if you don’t want the string to have multiple lines in its final format, just put a \ at the end of the line.

Nesting Templates

Template strings can be nested in each other. This is a great feature because many people want to create dynamic strings. Template strings allow us to nest regular strings or template strings within template strings. For example, we can write the following:

const oldEnoughToDrink = age >= 21;  
const oldEnough = `You are ${oldEnoughToDrink ? 'old enough' : 'too young'} to drink.`

We can write the same thing with the expression interpolated in the string with template strings instead:

const TOO_YOUNG = 'too young';  
const OLD_ENOUGH = 'old enough';
const oldEnoughToDrink = age >= 21;
const oldEnough = `You are ${oldEnoughToDrink ? OLD_ENOUGH : TOO_YOUNG} to drink.`

This case is simple, but with complex string combinations, it is very powerful to be able to add logic to how we build our strings.

Tagged Templates

With template strings, we can add something called tags in front of a template string. They are functions that take a string and the decomposed parts of the string as parameters. A tagged function decomposes the template string into an array as the first parameter, with each item of the array as the constant parts of the string. The additional parameters of the function are all the variables and expressions in the order in which they appear in the string.

const personOldTag = (strings, nameExp, ageExp, genderExp)=>{  
  let str0 = strings[0]; // " is a "  
  let str1 = strings[1]; // " year old "  
  
  let oldStr;  
  if (ageExp > 75){  
    oldStr = 'senior';  
  } else {  
    oldStr = 'young';  
  }  
  
  // We can even return a string built using a template literal  
  return `${nameExp}${str0}${oldStr}${genderExp}.`;  
}

const name = 'Bob'  
const age = 80;  
const gender = 'male;
const result = personOldTag`${ name } is a ${ age } year old ${ gender }`;// result should be `Bob is a senior man.`

Tagged templates are great for converting strings to anything you want since it’s just a regular function. However, it is a special function because the first parameter is an array containing the constant parts of the string. The rest of the parameters contain the returned values that each expression returns. This is great for manipulating the string and transforming the returned values to what we want.

The return value of tags can be anything you want. So we can return strings, arrays, objects, or anything else.

As we see in the function above, in the string that we put beside the personOldTag, we first interpolated the name, then the age , then the gender. So in the parameters, they also appear in this order — nameExp, ageEx, and genderExp. They are the evaluated versions of the name, age, and gender in the template string.

Since tags are functions, we can return anything we want:

const isOldTag = (strings, nameExp, ageExp, genderExp)=>{  
  let str0 = strings[0]; // " is a "  
  let str1 = strings[1]; // " year old "  
  return ageExp > 75  
}

const name = 'Bob'  
const age = 80;  
const gender = 'male;
const result = isOldTag`${ name } is a ${ age } year old ${ gender }`;
// result is true

As you can see, we can manipulate the data to return a boolean instead of a string. Template tags are a feature that’s only available for template strings. To do the same thing with the old style of strings, we have to decompose the strings with our own string manipulating code and the built-in string manipulation functions, which is nowhere near as flexible as using tags. It decomposes the parts of a string into arguments for you that get passed in the template tags.

Raw Strings

Template strings have a special raw property that you can get from tags. The raw property gets us the string without escaping the special characters, hence the property is called raw. To access the raw property of a string, we can follow the example:

const logTag = (strings) => {  
  console.log(strings.raw[0]);  
}  
  
logTag`line 1 \r\n line 2\`;  
// logs "line 1 \r\n line 2" including characters '\', 'r' and 'n'

This is handy for visualizing the string as-is with the special characters intact.

There’s also the String.raw tag where we can take a template string and return a string with the escape characters without actually escaping them to see the string in its full form. For example, with the String.raw tag, we can write:

let hello = String.raw`Hello\n${'world'}!`;  
// "Hi\nworld!"  
  
hello.length;  
// 13  
  
hello.split('').join(',');  
// "H,e,l,l,o,\,n,w,o,r,l,d,!"

As we can see, we have all the characters of the string with the expressions evaluated, but we keep the escape characters as is.

Support for Template Strings

Template strings are supported by almost all browsers that are regularly maintained today. The only major one that doesn’t support it right out of the box is Internet Explorer. However, browsers that do not support it can add it with Babel. Node.js also supports it in version 4 or later.

Template strings are the new standard for JavaScript strings. It is much better than strings before ES6 in that it supports interpolating expressions inside strings. We also have tagged templates which are just functions, with the decomposed parts of the template string as parameters. The constant string in the first parameter is the string in a decomposed array form, and the evaluated versions of the expressions in the order they appeared in the string are the remaining arguments. Tagged template functions can return any variable type.

The raw property of a string, which is available in tags, can get the string with the escape characters unescaped.

Categories
JavaScript JavaScript Basics

Using the JavaScript Number Object

The Number JavaScript object is a wrapper object that lets us work with numbers by providing us with various constants and methods. A primitive number can be created by the Number() function. A JavaScript number is a double-precision 64-bit binary format IEEE 754 value.


Creating a Number Object

We create a Number object using the Number function by writing the following code:

new Number('123');   
const a = new Number('123');  
const b = Number('123');

The main purpose of the Number function is to create a number from a string. The Number constructor used with the new operator is for creating a Number object, which is of type “object” instead of “number.” A number primitive isn’t an instance of Number. The data type for a primitive number value is a number. The type with the typeof operator and the constructor that it’s constructed from with the instanceof operator, are as in the following code:

console.log(typeof new Number('123'));  
console.log(new Number('123') instanceof Number);

We get “object” and true respectively when we run the two lines of code above. On the other hand, if we write this code instead:

console.log(typeof Number('123'));  
console.log(Number('123') instanceof Number);

We get number and false respectively.

The primary uses of the Number object are for checking if an object can be converted to a number. If a non-number is passed into the Number function, we get NaN returned. If the Number is used without the new operator in a non-constructor context, it’s handy to use for type conversion.

The Number object has the following properties:

  • Number.EPSILON: the smallest interval between two representable numbers.
  • Number.MAX_SAFE_INTEGER: the maximum safe integer in JavaScript, 2⁵³ minus 1.
  • Number.MAX_VALUE: the largest positive representable number (1.7976931348623157e+308).
  • Number.MIN_SAFE_INTEGER: the minimum safe integer in JavaScript, -2⁵³ minus 1.
  • Number.MIN_VALUE: the smallest positive representable number; the positive number closest to zero without actually being zero.
  • Number.NaN: the "not a number" value.
  • Number.NEGATIVE_INFINITY: value representing negative infinity.
  • Number.POSITIVE_INFINITY: value representing infinity.

Static Methods

The Number object has a few static methods.

Number.isNaN()

Determines whether the passed value is NaN. Returns true if a variable’s value is NaN. For example, we can use it to determine if an object is a number with the following code:

console.log(Number.isNaN(+'abc')); // true  
console.log(Number.isNaN(+'123')); // false

The first line logs true because when 'abc' is converted to a number with the unary + operator, we get NaN. On the other hand, the second line logs false because when '123' is converted to a number with a + operator, we get 123, which is a number, so the second line logs false.

Number.isFinite()

Determines whether the passed value is a finite number. Returns true if a number is finite and false otherwise. For example, we can use it to determine if an object is a number with the following code:

console.log(Number.isFinite(Infinity)); // false  
console.log(Number.isFinite(123)); // true

We get false for the first line because Infinity isn’t a finite number, but the second line logs true because 123 is a finite number.

Number.isInteger()

Determines whether the passed value is an integer. Returns true if the number is an integer and false otherwise. For example, we can use this method as in the following code:

console.log(Number.isInteger(123)); // true  
console.log(Number.isInteger(123.45)); // false

The first line logs true because 123 is an integer, but the second line logs false because it’s not an integer. If the argument passed is not of the number type then it will return false. For example, Number.isInteger('10'); will return false.

Number.isSafeInteger()

Determines whether an integer passed in the argument is within the range of a safe integer, i.e. if the number is between -2⁵³ minus 1 and 2⁵³ minus 1. For example, if we have:

console.log(Number.isSafeInteger(Math.pow(2, 53) - 1)); // true  
console.log(Number.isSafeInteger(Math.pow(2, 53))); // false

The first line logs true because Math.pow(2, 53) — 1 is within the safe range, but the second line logs false because Math.pow(2, 53) is not in the safe range.

Number.parseFloat()

The parseFloat method converts a string that’s passed into the argument and returns a number that’s parsed from the string as a floating-point number. If it can’t be parsed, then NaN is returned. For example, if we have:

console.log(Number.parseFloat('123.45'));  
console.log(Number.parseFloat('123'));  
console.log(Number.parseFloat('abc'));

We get 123.45 from the first line, 123 from the second line, and NaN from the third line.

Number.parseInt()

The parseInt method converts a string that’s passed into the argument and returns a number that’s parsed from the string as a whole number. If the first character of the string can’t be parsed, and the radix is smaller than 11, then NaN is returned. For example, if we have:

console.log(Number.parseFloat('123.45'));  
console.log(Number.parseFloat('123'));  
console.log(Number.parseFloat('abc'));

Then we get 123 from the first line and second line, and NaN from the third line.

It also takes a radix as the second argument, which is the base of the mathematical numeral systems. If the string starts with 0x then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

To convert a hexadecimal string into a number, we can write something like the following:

console.log(Number.parseInt('0x1'));

We get 1 when the last line is run. To convert a binary string to decimal, we can write something like:

console.log(Number.parseInt('0111', 2));

The line above will log 7, which is the decimal equivalent of the binary number 0111.


<img class="do t u hm ak" src="https://miro.medium.com/max/8000/0*azxvdPlJ5DchQYGA" width="4000" height="2423" role="presentation"/>

Photo by Andrew Buchanan on Unsplash

Instance Methods

All Number instances inherit from Number.prototype, which provides the object with a few instance methods. The instance methods for Number follow.

Number.toExponential()

The toExponential method returns a string representing the number in exponential notation. It takes an optional that specifies the number of fractional digits to include. For example, we can write:

(123).toExponential(1);

Then we get 1.2e+2.

Number.toFixed()

The toFixed method returns a string representing the number in fixed-point notation. It takes an optional that specifies the number of fractional digits to include after the decimal point. For example, we can write:

console.log((123).toFixed(1));

And we get 123.0.

Number.toLocaleString()

This method returns a string with a language-sensitive representation of the number. It overrides the Object.prototype.toLocaleString() method. The first argument is the locales argument, which takes one locale string or an array of locale strings. This is an optional argument. It takes a BCP 47 language tag with the optional Unicode extension key nu to specify the numbering system for formatting the number. Possible values for nu include: "arab", "arabext", "bali", "beng", "deva", "fullwide", "gujr", "guru", "hanidec", "khmr", "knda", "laoo", "latn", "limb", "mlym", "mong", "mymr", "orya", "tamldec", "telu", "thai", "tibt". The instance of the object created by the constructor has the format method return a string with the formatted number.

The second argument accepts an object with a few properties: localeMatcher, style, unitDisplay, currency, useGrouping, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits, and maximumSignificantDigits.

The localeMatcher option specifies the locale-matching algorithm to use. The possible values are lookup and best fit. The lookup algorithm searches for the locale until it finds the one that fits the character set of the strings that are being compared. best fit finds the locale that is at least as, but possibly more-suited than the lookup algorithm.

Style

The style option specifies the formatting style to use. Possible values for the style option include decimal, currency, percent, and unit. decimal is the default option and it’s used for plain number formatting, currency is for currency formatting, percent is for percent formatting, and unit is for unit formatting.

Possible values for thecurrency property are ISO 4217 currency codes, such as USD for the U.S. dollar and EUR for Euro. There’s no default value. If the style property is set to currency then the currency property must be provided. The currencyDisplay property sets how the currency is displayed in currency formatting. Possible values are symbol for adding localized currency symbols and is the default value, code is for adding the ISO currency code, name to use a localized currency name such as “dollar.” useGrouping option is for setting the grouping separator to use for numbers. It’s a boolean value.

minimumIntegerDigits, minimumFractionDigits, and maximumFractionDigits are considered one group of options. minimumIntegerDigits specifies the minimum number of integer digits to use, ranging from 1 to 21, with 1 being the default option. minimumFractionDigits is the minimum number of fraction digits to use, ranging from 0 to 20. The default is 0 for plain number and percent formatting. The default for currency formatting is specified by the ISO 4217 currency code list, and 2 if it’s not specified in the list. maximumFractionDigits is the maximum number of fraction digits to use, with possible values ranging from 0 to 20. The default for plain numbers is the maximum between minimumFractionDigits and 3. The default for currency formatting is the maximum between minimumFractionDigits and the number of fractional unit digits provided by the ISO 4217 currency code list, or 2 if the list doesn’t provide that information. The default for percent formatting is the maximum between minimumFractionDigits and 0.

minimumSignificantDigits and maximumSignificantDigits are considered as another group of options. If at least one of the options in this group is defined, then the first group is ignored. minimumSignificantDigits is the minimum number of significant digits to use, with possible values ranging from 1 to 21 with the default being 1. maximumSignificantDigits is the maximum number of significant digits to use, with possible values ranging from 1 to 21, with the default being 21.

For example, we can use this method in the following examples:

const number = 123.45;console.log(number.toLocaleString('fr', {  
  style: 'currency',  
  currency: 'EUR'  
}));

console.log(number.toLocaleString('zh-Hant-CN-u-nu-hanidec', {  
  style: 'currency',  
  currency: 'JPY'  
}))

console.log(number.toLocaleString('en-IN', {  
  maximumSignificantDigits: 3  
}));

The first console.log statement will log 123,45 €, the second console.log will log ¥一二三, and the third console.log will get 123.

Number.toPrecision()

The toPrecision method returns a string representing the number to a specified precision in fixed-point or exponential notation. It takes an argument that optionally lets us specify the base between 1 and 100. If it’s not specified then all digits after the decimal point will be returned. For example, we can use it as in the following code:

const numObj = 5.12345;console.log(numObj.toPrecision());  
console.log(numObj.toPrecision(100));  
console.log(numObj.toPrecision(5));  
console.log(numObj.toPrecision(2));  
console.log(numObj.toPrecision(1));

We get the following logged for each line:

5.123455.1234500000000000596855898038484156131744384765625000000000000000000000000000000000000000000000000005.12355.15

Number.toString()

The toString method returns a string representing the specified object in the specified radix, or base of the number. It overrides the Object.prototype.toString() method. The radix is an optional argument that lets us specify the base between 2 and 36. The default radix is 10. If the number is negative the sign is preserved. If it’s not a whole number, then a dot sign will be used to separate the decimal places. For example, we can write:

const count = 10;  
  
console.log(count.toString());    // '10'  
console.log((15).toString());     // '15'  
console.log((15.2).toString());   // '15.2'

It also works for non-decimal numbers. For example, we can write:

console.log((-0b0111).toString(10)); // '-7'

It’s handy for converting numbers of different bases to other numbers.

Number.valueOf()

The valueOf method returns the primitive value of the specified object. It overrides the Object.prototype.valueOf() method. For example, if we have a Number object constructed with the new operator, we can write:

const numObj = new Number('10');  
const numPrim = numObj.valueOf();  
console.log(numPrim);  
console.log(typeof numPrim);

We get the following logged when the code above is run:

10  
number

Converting Other Objects to Numbers

With the Number function, we can convert Date objects and null to numbers. For example, for Date objects, we can write:

const d = new Date(2019, 0, 1, 0, 0, 0);  
console.log(Number(d));

We get 1546329600000, which is the UNIX timestamp for January 1, 2019. The null object will be converted to 0, so if we write:

console.log(Number(null));

We get 0.


Conclusion

The Number JavaScript object is a wrapper object that lets us work with numbers by providing us with various constants and methods. A primitive number can be created by the Number() function. It has a number of useful static and instance methods for checking numbers and converting between strings and numbers and vice versa. The toString method is also handy for converting numbers to different bases.