Skip to content

Objects

sergio edited this page Nov 8, 2020 · 5 revisions

Do not to think of Object as being like "Object-Oriented Programming," but instead think of it as a key/value based data structure.

const address = {
  street1: '11 Broadway',
  street2: '2nd Floor',
  city: 'New York',
  state: 'NY',
  zipCode: 10004
};

address.street1;
// => "11 Broadway"
address.street2;
// => "2nd Floor"
address.city;
// => "New York"
address.state;
// => "NY"
address.zipCode;
// => 10004

address.country;
// => undefined	

address['street1'];
// => "11 Broadway"
address['street2'];
// => "2nd Floor"
address['city'];
// => "New York"
address['state'];
// => "NY"
address['zipCode'];
// => 10004
if you need to use non-standart keys:
const wildKeys = {
  'Cash rules everything around me.': 'Wu',
  'C.R.E.A.M.': 'Tang',
  'Get the money.': 'For',
  "$ $ bill, y'all!": 'Ever'
};

You cannot access those properties with dot notation:

wildKeys.'Cash rules everything around me.';
// ERROR: Uncaught SyntaxError: Unexpected string

But bracket notation works just fine:

wildKeys["$ $ bill, y'all!"];
// => "Ever"

In order to access a property via dot notation, the key must follow the same naming rules applied to variables and functions. Under the hood all keys are strings.


Dynamic Access to Properties

const meals = {
  breakfast: 'Oatmeal',
  lunch: 'Caesar salad',
  dinner: 'Chimichangas'
};

let mealName = 'lunch';

meals[mealName];
// => "Caesar salad"
mealName = 'dinner';
// => "dinner"

meals.mealName;
// => undefined

Can also be used on creation:

const morningMeal = 'breakfast';
const middayMeal = 'lunch';
const eveningMeal = 'dinner';

const meals = {
  [morningMeal]: 'French toast',
  [middayMeal]: 'Personal pizza',
  [eveningMeal]: 'Fish and chips'
};

meals;
// => { breakfast: "French toast", lunch: "Personal pizza", dinner: "Fish and chips" }

Dynamic Access to Properties

const circle = {};

circle.radius = 5;

circle['diameter'] = 10;

circle;
// => { radius: 5, diameter: 10}

keys must be unique. If the same key is used for multiple properties, only the final value will be retained.

const meals = {
  breakfast: 'Avocado toast',
  breakfast: 'Oatmeal',
  breakfast: 'Scrambled eggs'
};

meals;
// => { breakfast: "Scrambled eggs" }

Destructive Vs. Non-Destructive

Destructive!

function destructivelyUpdateObject (obj, key, value) {
  obj[key] = value;
  return obj;
}

const mondayMenu = {
  fries: 'Sweet potato',
  salad: 'Cobb'
};

const tuesdayMenu = destructivelyUpdateObject(mondayMenu, 'salad', 'Caesar');
// => { cheesePlate: { soft: "Chèvre", semiSoft: "Gruyère", hard: "Manchego" }, fries: "Sweet potato", salad: "Caesar" }

tuesdayMenu.salad;
// => "Caesar"

mondayMenu.salad;
// => "Caesar"

Non-Destructive

with the ES2015 spread operator, we can copy all of the old menu Object's properties into a new Object:

function nondestructivelyUpdateObject(obj, key, value) {
  const newObj = { ...obj };

  newObj[key] = value;

  return newObj;
}

const sundayMenu = nondestructivelyUpdateObject(tuesdayMenu, 'fries', 'Shoestring');

tuesdayMenu.fries;
// => "Sweet potato"

sundayMenu.fries;
// => "Shoestring"

performance gains from non-destructive updates

Object.assign(initialObject, additionalObject, additionalObject, ...);
Object.assign({ eggs: 3 }, { flour: '1 cup' });
// => { eggs: 3, flour: "1 cup" }

Object.assign({ eggs: 3 }, { chocolateChips: '1 cup', flour: '2 cups' }, { flour: '1/2 cup' });
// { eggs: 3, chocolateChips: "1 cup", flour: "1/2 cup" }

In same key, the last key to be defined wins out.

This pattern allows us to rewrite the above destructivelyUpdateObject() function in a nondestructive way:

function nondestructivelyUpdateObject(obj, key, value) {
  return Object.assign({}, obj, { [key]: value });
}

const recipe = { eggs: 3 };

const newRecipe = nondestructivelyUpdateObject(recipe, 'chocolate', '1 cup');
// => { eggs: 3, chocolate: "1 cup" }

newRecipe;
// => { eggs: 3, chocolate: "1 cup" }

recipe;
// => { eggs: 3 }

It's important that we merge everything into a new, empty Object. Otherwise, we would be modifying the original Object. if you were to use Object.assign(obj, { [key]: value }); back to being destructive!

Note: For deep cloning, we need to use other alternatives because Object.assign() copies property values.

function nondestructivelyUpdateObject(oldObj, changes) {
  return Object.assign({}, oldObj, changes);
}

const obj = { eggs: {uno: 1, due: 2}, tre: 3 };

const changes = { eggs: {uno: 1, quattro: 4}}

const newObj = nondestructivelyUpdateObject( obj, changes );
// => { eggs: {uno: 1, quattro: 4}, tre: 3 };

newObj;
// => { eggs: {uno: 1, quattro: 4}, tre: 3 };
// instead of:
// => { eggs: {uno: 1, quattro: 4, due: 2}, tre: 3 };

obj;
// => { eggs: {uno: 1, due: 2}, tre: 3 };

Object.keys()

const wednesdayMenu = {
  cheesePlate: {
    soft: 'Brie',
    semiSoft: 'Fontina',
    hard: 'Provolone'
  },
  fries: 'Sweet potato',
  salad: 'Southwestern'
};

Object.keys(wednesdayMenu);
// => ["cheesePlate", "fries", "salad"]

NOTE: The sequence in which keys are ordered in the returned Array is not consistent across browsers and should not be relied upon.


Delete

const wednesdayMenu = {
  cheesePlate: {
    soft: 'Brie',
    semiSoft: 'Fontina',
    hard: 'Provolone'
  },
  fries: 'Sweet potato',
  salad: 'Southwestern'
};

delete wednesdayMenu.salad;
// => true

wednesdayMenu;
// => { cheesePlate: { soft: "Brie", semiSoft: "Fontina", hard: "Provolone" }, fries: "Sweet potato" }

Summary

function updateDriverWithKeyAndValue(driver, key, value) {
	return Object.assign({}, driver, { [key]: value })
}

function destructivelyUpdateDriverWithKeyAndValue(driver, key, value) {
	driver[key] = value
	return driver
}

function deleteFromDriverByKey(driver, key) {
	const newDriver = { ...driver };
	delete newDriver[key]
	return newDriver
}

function destructivelyDeleteFromDriverByKey(driver, key) {
	delete driver[key]
	return driver
}

Arrays are Objects

typeof [];
// => "object"


const myArray = [];

myArray.summary = 'Empty array!';

myArray;
// => [summary: "Empty array!"]


myArray.push(2, 3, 5, 7);
// => 4

myArray;
// => [2, 3, 5, 7, summary: "Empty array!"]


myArray.length;
// => 4

myArray[0];
// => 2

myArray[myArray.length - 1];
// => 7
const myArray = [];

myArray[0] = 'Will this be an `Object` property or an `Array` element?';
// => "Will this be an `Object` property or an `Array` element?"

// Moment of truth...
myArray.length;
// => 1
myArray['0'] = 'What about this one?';
// => "What about this one?"

myArray.length;
// => 1

myArray;
// => ["What about this one?"]

This is hitting on a fundamental truth: all keys in Objects and all indexes in Arrays are actually strings. Under the hood the JavaScript engine automatically converts that to the string "0".

const myArray = [2, 3, 5, 7];

myArray['1'] = 'Hi';
// => "Hi"

myArray;
// => [2, "Hi", 5, 7]

myArray['01'] = 'Ho';
// => "Ho"

myArray;
// => [2, "Hi", 5, 7, 01: "Ho"]

myArray[01];
// => "Hi"

myArray['01'];
// => "Ho"


myArray.length;
// => 4

Object.keys(myArray);
// => ["0", "1", "2", "3", "01"]
  • For accessing elements in an Array, always use integers.
  • Be wary of setting Object-style properties on an Array. There's rarely any reason to, and it's usually more trouble than it's worth.
  • Remember that all Object keys, including Array indexes, are strings. This will really come into play when we learn how to iterate over Objects, so keep it in the back of your mind.

Clone this wiki locally