Categories
JavaScript Mistakes

JavaScript Mistakes — Async, Type Checking, and Values

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at how to avoid race conditions when running multiple promises and more.

Assignments that can Lead to Race Conditions Because of yield and await

When using promises, it’s easy to create race conditions when we aren’t combining multiple promises properly.

For instance, if we have:

let total = 0;

const getNum = async (num) => {
  return num;
}

const add = async (num) => {
  total += await getNum(num);
}

Promise.all([add(1), add(2)]).then(() => {
  console.log(total);
});

Then we’re calling the add async function, which gets the result from the getNum promise and add it to the total .

Then we call Promise.all , which runs the code in parallel. In that time, the first value assigned to total will be overwritten because total is still set to the initial value in each promise before they were resolved.

Therefore, we’ll see 2 instead of 3 as we expect.

We can fix this mistake by assigning the resolved value in add to a variable before adding them to the total as follows:

const add = async (num) => {
  const res = await getNum(num);
  total += res;
}

Or we can just wait for both to resolve and then add the results together:

const getNum = async (num) => {
  return num;
}

const add = async (num) => {
  return await getNum(num);
}

Promise.all([add(1), add(2)]).then((nums) => {
  const total = nums.reduce((a, b) => a + b, 0);
  console.log(total);
});

Not Using isNaN to Check for NaN

NaN is a special value in JavaScript. It doesn’t equal itself when compared with == or === . For instance, the following:

NaN === NaN
NaN == NaN

returns false and:

NaN !== NaN
NaN != NaN

returns true .

NaN check also doesn’t work with indexOf and lastIndexOf :

const arr = [NaN];
console.log(arr.indexOf(NaN));
console.log(arr.`lastIndexOf`(NaN));

We’ll get -1 returned from the console log in the code above.

Therefore, we should check for NaN with the isNaN function as follows:

const foo = NaN;
if (isNaN(foo)){
  console.log('is NaN');
}

We can also use the Object.is method to check for NaN as follows:

const foo = NaN;
if (Object.is(foo, NaN)) {
  console.log('is NaN');
}

To check if NaN is in an array, we can write our own function to do it:

const arr = [NaN];
console.log(arr.some(a => isNaN(a)));

Compare typeof Expressions Against Invalid Strings

It’s easy to make typos when writing strings. Therefore, we should check if the types that we check with typeof are correct.

For instance, the following code has typos in the string that’ll cause typeof to not work as we expect them to:

typeof foo === "strnig"
typeof foo == "undefimed"
typeof bar === "nunber"
typeof bar === "fucntion"

We should make sure that the strings that we compare against are spelled correctly as follows:

typeof foo === "string"
typeof foo == "undefined"
typeof bar === "number"
typeof bar === "function"

Getters and Setters should be Together in Object and Classes

A setter isn’t very useful without its corresponding getter. For instance, if we have:

const obj = {
  set foo(val) {
    this._foo = val
  }
}

Then there’s no way to get the value of foo , and we’ll get undefined when we reference obj.foo even after we set the value.

Therefore, we should add a corresponding getter to the object to return a value. For instance, we should instead write:

const obj = {
  get foo() {
    return this._foo;
  },
  set foo(val) {
    this._foo = val
  }
}

To define a new property with defineProperty with getters and setters, we can write the following:

const obj = {};
Object.defineProperty(obj, 'foo', {
  get() {
    return this._foo;
  },
  set(val) {
    this._foo = val
  }
});

Then if we assign a value to foo , we can get the value of the foo property. Likewise, we can do the same for classes as follows:

class Obj {
  get foo() {
    return this._foo;
  }

  set foo(val) {
    this._foo = val;
  }
}

const obj = new Obj();
obj.foo = 1;

This works the same as the object example, except that we’ve to instantiate it with new first.

Conclusion

We shouldn’t do assignment with operations like += with await or yield as the right operand as the value will be overwritten instead of combined with the existing value.

Also, we should use isNaN to check for NaN instead of checking with the === or == operator.

When using typeof , we should check for typos in the strings that we check for the types against.

Setters without getters also isn’t very useful since we can’t get the value that we set.

Categories
JavaScript Mistakes

More JavaScript Mistakes — Objects, Async Code and Event Listeners

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. In this article, we’ll look mistaking object keys as array indexes, not understanding asynchronous code, misusing event listeners.

Confusing Object Keys and Array Indexes

In JavaScript, the brackets can be used to accept an object which can take a string containing a property name and then get the value from it. For example, if we have the following object:

const obj = {
  foo: 1,
  bar: 2
}

Then we can access the value of the foo property by writing either:

obj.foo

or we can use the bracket notation and write:

obj['foo']

The bracket notation is also used to access an array entry by its index. For example, if we have:

const arr = [1, 2, 3];

Then we write:

arr[0]

to get the first entry of the arr array.

We have to be careful in that we don’t confuse normal objects and arrays since the values of them can be accessed with the bracket notation, and the JavaScript interpreter doesn’t distinguish between the 2. This means that code like:

arr['foo']

are still allowed in JavaScript. It doesn’t stop us from commit the mistake of accessing an array-like we do with a regular object. If we log the value of arr[‘foo’] we just get undefined .

If we don’t know whether a variable is a regular object, an array or something, else, then we can use the Array.isArray method which checks if a value that we pass in if an array. It takes one argument which is any objects that we want to check to see whether if it’s an array.

By using the Array.isArray method, we’ll be sure that we’re accessing an array if Array.isArray returns true. For example, we can use it as the following code:

const arr = [1, 2, 3]

if (Array.isArray(arr)) {
  console.log(arr[0]);
}

This way, we’re sure that arr is an array before trying to access an array entry.

Not Understanding Asynchronous Code

Because of the single thread nature of JavaScript, lots of code is going to be asynchronous since running code line by line all the time is going to hang the computer if it’s waiting for something that takes some time such as a response from an HTTP request.

With asynchronous code, we often have run code that’s in a callback function, which runs in an indeterminate amount of time. They can’t be treated like synchronous code that runs line by line.

For example, if we want to get some data from a GET request, we often have to use an HTTP client that makes the request asynchronously and the response is obtained in an indeterminate amount of time.

If we use the Fetch API built into most modern browsers, we’ll get the response data asynchronously. For example, we’ll have code that looks something like the following:

fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))
  .then((response) => {
    return response.json()
  })
  .then((responseBody) => {
    console.log(responseBody);
  })

As we can see we have multiple callback functions that do various things. If we write something like:

const response = fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))

const responseBody = response.json()
console.log(responseBody);

It’s not going to work since the correct code doesn’t run code line by line. In the correct code, the fetch function is called, which returns a promise, then inside the callback function in the first then method, we return the result of theresponse.json() object, which returns another promise which resolves to the JSON body of the response. Then in the callback function of the second then method, we log the responseBody which is obtained by resolving the promise returned by response.json() call.

Promises may be run sequentially by chaining them, but they don’t run instantaneously and so they don’t run like synchronous code.

A shorthand that looks more like synchonous code would be using the async and await keyword introduced with ES2017. We can write:

fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))
  .then((response) => {
    return response.json()
  })
  .then((responseBody) => {
    console.log(responseBody);
  })

to:

(async () => {
   const response = await fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27));
   const responseBody = await response.json();
   console.log(responseBody);
 })();

An async function looks like it’s running line by line, but it’s just a shorthand for the then method calls with callbacks passed inside. We can’t return anything other than a promise inside an async function.

Adding Too Many Event Listeners

If we have the following HTML code:

<input type="checkbox" />

<button>Click</button>

and the corresponding JavaScript code:

const checkbox = document.querySelector('input[type=checkbox]');
const button = document.querySelector('button');

checkbox.addEventListener('change', () => {
  if (checkbox.checked) {
    button.addEventListener('click', () => {
      alert('Alert');
    });
  }
});

Then we’ll see that if we toggle the checkbox on and off a few times and then click the button, we’ll see the ‘Alert’ pop up multiple times. This is because we used the addEventListener method on the button object, which represents the button element that we have in the HTML. The addEventListener method attaches a new click event listener each time the checkbox is checked. This means that we have multiple event listeners listening to the click event of the button. When the click event is finally fired by clicking the button, all the click event listener functions that we attached to the button all run, causing the ‘Alert’ pop up to appear multiple times.

This means we attached too many event listeners to the button.

The right way to do this is to set the onclick property of the button with the same event listener function instead. Likewise, we can also do the same thing for the checkbox object even though we don’t have the same problem for consistency. We can instead write:

const checkbox = document.querySelector('input[type=checkbox]');
const button = document.querySelector('button');

checkbox.onchange = () => {
  if (checkbox.checked) {
    button.onclick = () => {
      alert('Alert');
    };
  }
};

This way, we keep overwriting the onclick event listener assigned to the button.onclick property instead of keep attach new click event listeners to it.

In JavaScript, the brackets can be used to accept an object which can take a string containing a property name and then get the value from it. With arrays, we can use the bracket notation to access an array element by its index. There’s no way to distinguish whether an object with an array just by looking at the code. Therefore, we should check whether an object is actually an array before trying to access items by its index.

Lots of code in JavaScript are asynchronous since JavaScript is single-threaded, so having all the code run line by line will stall the browser. This means that we have to use asynchronous code to avoid stalling the computer by letting code that doesn’t return a result immediately wait in the background. We have to be aware that code are in callback functions of asynchronous functions are there because they can’t live outside the callback function.

Finally, we shouldn’t use addEventListener too often since it keeps adding new event listeners to a DOM object without discarding the old ones. Instead, we can use the set the event listener function to the property of the DOM object which listens to the event we want to listen to. For example, if we want to listen to the click event, then we can set the onclick property of a DOM object with the event handler function that we defined.

Categories
JavaScript Mistakes

JavaScript Mistakes — null vs undefined, Constructors, and Scopes

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. In this article, we’ll look at the confusion between null and undefined, scope issues with asynchronous code, and scopes and using object literals vs. constructors for built-in objects.

Confusing Null and Undefined

In JavaScript, both null and undefined mean no value for a variable. They’re pretty similar but there are some important differences. One if that null is of type object, so when we use the typeof operator on null as we do below:

typeof null

We get 'object'.

On the other hand, if we use the typeof operator on undefined, as we do below:

typeof undefined

We get 'undefined'.

If we compare them with the === operator, as we do below:

undefined === null

We get false since they’re of 2 different types. However, when we compare them with the == operator:

undefined == null

Then we get true since they’re both falsy. Since they’re 2 different types, they shouldn’t be confused with each other.

One thing to note is that for function default parameters, they will only be assigned if the value is undefined and not null.

const hello = (name = 'World') => console.log(`Hello, ${name}!`)
hello(null) // Hello, null!
hello(undefined) // Hello, World!

Asynchronous Code and Scope

One confusing aspect of JavaScript is that the visibility of a variable remains in the parent scope. This means that when a loop runs with a loop variable that we want to access in the callback of the setTimeout function, the index variable would have already been incremented to meet the end condition before the callback in setTimeout is called. This means that we’ll only get the final value of the loop variable after the loop is finished, which gets passed into the callback of the setTimeout function.

For example, if we have the following code:

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}

We get 10 logged 10 times. This is because the code in the setTimeout ‘s callback function runs after the loop is finished. i would have been updated to 10 by then. If we want to keep the value of the loop variable in each setTimeout callback function call. Then we have to pass the value of i into the callback function by wrapping the setTimeout inside a function.

If we want to see 0 to 9 logged instead, then we’ve to write the following:

for (var i = 0; i < 10; i++) {
  ((i) => {
    setTimeout(() => {
      console.log(i);
    }, 100);
  })(i)
}

The code above wraps the setTimeout call inside the anonymous function, which we call every time the loop runs, and we pass in the value of i in each iteration into the function. Then the value of i will be passed into the console.log rather than the value of i after the loop is done.

Photo by Francesco De Tommaso on Unsplash

Using Constructors over Literals

In JavaScript, constructors aren’t always useful for creating new instances of objects. This is especially true for built-in objects like arrays, numbers, and strings. We construct new instances of objects with the new operator like other languages such as C# or Java. However, it doesn’t always do what we expect.

For example, we can create a new Array object with the new operator. For example, we can write:

let arr = new Array('a', 'b', 'c', 'd');

Then we get [“a”, “b”, “c”, “d”] like we expect. However, when we only pass in one argument to the Array constructor, as we do in the code below:

let arr1 = new Array(23);

Then we get an empty array with length property set to 23.

It turns out that there’re 2 Array constructors. One takes one argument, which is the length of the array, and the other takes an infinite list of objects separated by commas, which are the list of elements for the new array.

However, if the one and only argument isn’t a number, then it does create an array with the argument as the only entry of the array. For example, if we write:

let arr = new Array('a');

We get [“a”] .

If the only argument is a negative number, it also fails to create a new array. We get the error message Uncaught RangeError: Invalid array length .

Using the Array constructor is confusing because there’re 2 constructors that take different kinds of arguments.

For numbers and strings, we have different problems when we use constructors to create these objects. When we use the new operator to create a number or string object, we get problems when we compare them with the === operator. For example, if we try to compare 2 strings with the same content where one is a string literal and the other is created with the constructor, as we have in the following code:

'abc' === new String('abc');

We get false even though their content are exactly the same. This is because the string literal 'abc' is of type string, but the object created with new String(‘abc’) is of type object. Again, this is confusing.

We have to call the valueOf method on the string object to make it a string type object.

Likewise, if we have:

1 === new Number(1);

We also get false for the same reason. 1 is of type number while, new Number(1) is of type object. Again, we have to call the valueOf method to convert new Number(1) back to a number-type object.

The solution to all these problems is solved by using the literal notation whenever it’s available. JavaScript doesn’t need to know the length of the array beforehand since it just assigns undefined to whatever position that doesn’t have a value assigned to it.

Conclusion

In JavaScript, both null and undefined means no value for a variable that’s been assigned these values. They’re pretty similar but there’re some important. One if that null is of type object , and undefined is of type undefined . This means that null === undefined will be evaluated to false .

Whenever we have asynchronous code and we want to pass in some variable’s value into a callback that’s called by an asynchronous function like setTimeout , we have to pass in the outside variable into the callback function wrapping the asynchronous code with a function and then pass in the variable from the outside to the callback.

Finally, we should avoid using constructors for creating built-in objects like arrays, numbers, and strings. For numbers and strings, the type will be different from the literal type which will have the actual primitive types of number or string. For arrays, it’s because there’re 2 constructors which take different arguments and we’ll get unexpected results depending on the numbers of arguments we pass in.

Categories
JavaScript Mistakes

Common JavaScript Mistakes — Part 2

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.


Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined . The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

Photo by Mikhail Vasilyev on Unsplash


Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;
let y = 2;
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;
let y = '2';
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;
let y = '2';
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;
let y = '2';
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;
let y = '2';
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert a string or object into an integer and a parseFloat function to convert a string or object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional 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.

We can use them as in the following examples:

let x = 1;
let y = '2';
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;
let y = '2';
let z = Number.`parseFloat`(x) + Number.`parseFloat`(y)

We will get 3 in both of the examples above.


Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

const add = (a, b) => {
  return
  a + b;
}

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

const add = (a, b) => {
  return a + b;
}

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

const add = (a, b) => {
  return (
    a + b
  );
}

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {
  const
    power = 10;
  return a ** 10;
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Categories
JavaScript Mistakes

Common JavaScript Mistakes — Part 3

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.


Trying to Overload Functions

Functional overloading is a feature of some programming languages where you can declare functions with the same name but different signatures. In JavaScript, we can’t overload functions. Whenever a function is declared more than once, the one that is declared later overwrites the one that’s declared earlier. This is because functions are objects and declaring a function is like assigning an object to a variable. When you assign an object to a variable more than once, then the value that’s assigned later will overwrite the value that’s assigned earlier. That means that we can’t have two functions with the same name in the same module in JavaScript. For example, if we have the following,

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

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

console.log(add(1, 2, 3));

then we get three because the add function that’s declared later has overwritten the one that’s declared earlier. To fix this, we have to rename one of them. We can also put them inside two different objects. Then they can have the same name since they aren’t in the same level. Also, we can write an Immediately Invoked Function Expression, or IIFE for short. IIFEs are run as soon as they’re defined. To wrap them in an object, we can write the following:

const a = {
  add(a, b, c) {
    return a + b + c;
  }
}

const b = {
  add(a, b) {
    return a + b;
  }
}

console.log(a.add(1, 2, 3));
console.log(b.add(1, 2, 3));

As we can see, if we run the code, then the console.log of the first one will be six and the second one will be three since a.add has three parameters and b.add has two.

We can also use an IIFE as in the following example:

const sum1 = (function add(a, b, c) {
  return a + b + c;
})(1, 2, 3);

const sum2 = (function add(a, b) {
  return a + b;
})(1, 2, 3);

console.log(sum1);
console.log(sum2);

In the code above, we wrapped the function inside the parentheses and then called it immediately after it was defined. Then we assigned the returned result to a variable. After that, we get six and three as we wanted. Because we called each function immediately and returned the result, we get the right result since they didn’t overlap. It also means that they can’t be called again.


Missing Parameters

When we add a new parameter to a function, then we have to remember to pass in the extra argument in the function calls. Otherwise, there may be undefined errors. To avoid undefined parameters creating errors, we can either check for it in our function, or we can set a default value of the parameter. For example, if we have the following function

function addressFn(address, city, region) { ... }

and we want to add a countryparameter and we have other parts of our program calling the function above, then we can add a default parameter. We can do this by writing the following:

function addressFn(address, city, region, country = 'US') { ... }

This way, if the country argument didn’t get passed in, country will be set to 'US'.

Photo by Kendall Lane on Unsplash


Forgetting About the this Keyword

When we try to access some property from another property inside an object, we should use the this keyword to get the property’s value that we want. For example, if we have the following,

let obj = {
  prop: "some text",
  method() {
    console.log(prop);
  }
};

obj.method();

we will get an Uncaught ReferenceError: prop is not defined error when we run the code above. This is because we forgot to put the this keyword before the prop variable. Instead, we need to write the following:

let obj = {
  prop: "some text",
  method() {
    console.log(this.prop);
  }
};

obj.method();

When we run the code above, then we get 'some text', which is what we wanted.


Iterate Through the Object Keys

The for...in loop will loop through the keys of the current object as well as all the prototypes’ keys. This isn’t ideal for all situations. It’s also slower than the other ways of iterating through the keys of an object. With the for...in loop, we need to use the Object.hasOwnProperty function to check that the property is originally defined in the object. This makes the loop even slower. This is a problem if we have a large object with lots of properties. For example, if we have,

const parent = {
  pa: 1,
  pb: 2
}
let obj = Object.create(parent);

obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;

then the for...in loop will loop through all the properties of the parent and the properties added to obj. If we only want to loop through the properties in obj, then we have to loop using the hasOwnProperty function as in the following code:

for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(obj[key]);
  }
}

However, this is slower than the newer alternatives, which are Object.keys to get the keys of an object and Object.entries to get the key-value pairs of an object. Then we loop through them with the for...of loop since both return arrays. They only loop through the object’s properties and nothing up the prototype chain. The fastest ways to loop through the entries are these two functions. We can use them as follows:

const parent = {
  pa: 1,
  pb: 2
}
let obj = Object.create(parent);

obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;

for (const key of Object.keys(obj)) {
  console.log(obj[key]);
}

for (const [key, value] of Object.entries(obj)) {
  console.log(value);
}

In each example, we get the following logged,

a 1
b 2
c 3
d 4
e 5

which means that we’re getting what we want from the Object.keys and Object.entries functions.


Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. Remember that in JavaScript, we can’t overload functions, so we can’t define two functions with the same name in the same level. If there’s potential for a function parameter to not be set, then we can set a default parameter so that it will never be undefined. Also, we can’t forget about the this object when we’re accessing one property from another property of the same object. Finally, we shouldn’t use the for...in loop anymore to loop through the keys of an object because it’s slow and clunky if we just want to loop through the keys of the current object without its prototype’s keys. We want to use the Object.keys or Object.entries functions instead so we get the keys or the key-value pairs, respectively, as arrays, and we can loop through them like any other array.