Learn JavaScript Next – New Array Methods

5.6 Summary

  • Array.from creates an array containing all the values from an array-like object
  • Array.of creates an array with supplied values is safer than new Array
  • Array.prototype.includes checks if array contains a value at any of it’s indices
  • Array.prototype.find searches an array based on a criteria function and returns a first value found
  • Array.prototype.fill fills an array with a specified value.

Chapter 6: Object.assign

In this session we will cover:

  • Setting Default Values with object.assign
  • Extending Objects with Object.assign
  • Preventing Mutations When Using Object.assign
  • How Object.assign Assigns Values

Object.assign is one such method. With it we can easily set default values or extend objects in place or as copies all without having to write a lot of boilerplate code to do so.

Priming Exercise: JavaScript objects can inherit properties from another object via the prototype chain. However any given object can only have one prototype. How would you go about devising a way to allow objects to inherit from multiple source objects?

6.1 Setting Default Values with Object.assign

function createMap(options) {
  options = Object.assign({
    width: 900,
    height: 500,
    coordinates: [33.762909, -84.422675]
  }, options)
  
  // ...
}

Using object.assign is much more terse than our initial approach. The way object.assign works is, it takes any number of objects as arguments and it assigns all the values from subsequent objects on the first. If any objects contain the same keys, the object in to the right will override the object to the left in the list. This is demonstrated below:

let a = { x: 1, y: 2, z: 3 }
let b = { x: 5, y: 6 }
let c = { x: 12 }

Object.assign(a, b, c)

console.log(a)

fig_6-1

When setting defaults like this with Object.assign we are basically taking objects that are incomplete and filling in the missing values. Another common task is taking objects that are ready complete, and extending them into more customized variations.

6.2 Extending Objects with Object.assign

Another common use case for Object.assign is to just add some additional properties to an existing object.

function createBaseSpaceShip() {
  return {
    if function() {
      // ... fly function implementation
    },
    shoot: function() {
      // ... shoot function implementation
    },
    destroy: function() {
      // ... function for when the ship is destroyed
    }
  }
}

To create a specialized spaceship you just create a copy of a base spaceship and extend it with the specialized features:

// Listing 6.2
function createBomberSpaceShip() {
 let spaceship = createBaseSapceShip()
 
 Object.assign(spaceship, {
   bomb: function() {
     // ... make the ship drop a bomb
   }
 })
 
 return spaceship
}

let bomber = createBomberSpaceShip()
bomber.shoot()
bomber.bomb()

First we create a base spaceship object. Then we use Object.assign to ad an additional bomb property. You could create several enhanced spaceships using this technique. This function does have a lot of boilerplate though, so if you were going to reuse this technique you could create a helper function for enhancing the base spaceship:

function enhancedSpaceShip(enhancements) {
  let spaceship = createBaseSpaceShip()
  
  Object.assign((spaceship, enhancements)
                
  return spaceship
}
                
function createBomberSpaceShip() {
  return enhancedSpaceShip({
    bomb: function() {
      // ... make the ship drop a bomb
    }
  })
}
  
function createStealthSpaceShip() {
  return enhancedSapceShip({
    stealth: function() {
      // ... make the ship invisible
    }
  })
}
  
let bomber = createBomberSpaceShip()
bomber.shoot()
bomber.bomb()

let stealthShip = createStealthSpaceShip()
stealthship.shoot()
stealthship.stealth()

Now we created an enhancedSpaceship function that takes an object of enhancements as an argument. These enhancements could be new features or override existing features. Notice how in every example so far we have created an object and then mutated it? Sometimes that is desired but often what we want is just a copy.

6.3 Preventing Mutations When Using object.assign

For example, what if in our spaceship example we didn’t have a function for creating a new copy of the base spaceship object. What if we just had a single base spaceship object. If that were the case, every time we call Object.assign to enhance it, we would be enhancing the base which we don’t want. Instead we would want to make a copy and leave the base unchanged. You may think that const can help you here but it can’t. Remember, const only prevent overriding a variable with a new object, but it does not prevent modifying/mutating the existing object.

Since Object.assign mutates the first object in the parameter list, and leaves the rest of the objects unchanged, a common practice is to make the first object an empty object literal.

let newObject = {}
Object.assign(newObject, {foo: 1}, {bar: 2})
console.log(newObject)

When doing this, it is inconvenient to first create the empty object, then pass it to Object.assign. Conveniently, Object.assign also returns the mutated object, so you can shorten it like so:

let newObject = Object.assign({}, {foo: 1}, {bar: 2})

console.log(newObject)

Let’s rewrite out spaceship example to use a single base object and our enhance function to make an enhanced copy:

// Listing 6.4
const baseSpaceShip = {
  fly: function() {
    // ... fly function implementation
  },
  shoot: function() {
    // ... shoot function implementation
  },
  destroy: function() {
    // ... function for when the ship is destroyed
  }
}

function enhancedSpaceShip(enhancements) {
  return Object.assign({}, baseSpaceShip, enhancements) // By passing {} as the first argument, Object.assign makes a copy
}

function createBomberSpaceShip() {
  return enhancedSpaceShip({
    bomb: function() {
      // ... make the ship drop a bomb
    }
  })
}

function createStealthSpaceShip() {
  return enhancedSpaceShip({
    stealth: function() {
      // ... make the ship invisible
    }
  })
}

Notice how because we abstracted away our enhancement technique into it’s own function, when we modified how it works, we didn’t have to make any changes to the createBomberSpaceShip or createStealthSpaceShip.

At this point you may thinking that Object.assign basically just merge objects from right to left. Well, not exactly, it only assigns values from the left to the right. This minor distinction is what we will discuss next.

How Object.assign Assigns Values

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.