Objects

We've explored numbersstringsbooleansundefined and functions a bit, but we haven't really talked about objects yet.

Objects are essentially:

  • an "arbitrary collections of properties"
  • …and their associated values
  • these properties can be added, removed and modified


Can anyone think of analogous types in other languages? →

  • HashMaps in Java
  • associative arrays in PHP
  • dictionaries in Python
  • Hashes in Ruby


(ES6 introduces a Map data structure; regular objects have excess baggage, like inherited properties, making them more than just a map)

Creating Objects

Here's an example of an object:

const course = {name:'AIT', section:8, undergraduate:true};

Object literals consist of:

  • surrounding curly braces - {}
    • an empty object is just {}
  • property/value pairs separated by commas - ,
  • properties and values separated from each other with a colon - :
    • properties that aren't valid variables names or valid numbers must be quoted
    • for example: {"still a property name":true}
 

Object Literals Continued…

Ah, that's better.

Internal white space and newlines won't cause any syntax issues. →

  • you could use them for formatting
  • indentation also helps with readability
const course = {
    name: 'Applied Internet Technology',
    section: 8,
    undergraduate: true
};
 

Properties

  • properties can be any value, including numbers, strings, and booleans
  • they can also contain other objects… even functions
  • method is a property that contains a function
  • almost all JavaScript values have properties
    • the only exceptions are null and undefined
    • strings, numbers and booleans act like they have properties
  • which implies that almost everything in JavaScript is an object (or again, acts like an object for some values)!
 

ES6 Shorthand Property Names

There's also a shortcut to creating properties and values if you already have variables defined. →

  • In ES6…
  • creating an object that consists of only variable names
  • will initialize an object with those variable names as properties

 

const a = 'foo';
const b = 'baz';
const obj = {a, b};
console.log(obj) // { a: 'foo', b: 'baz' }
 

Computed Property Names

An object can be initialized with property names created dynamically →

This can be done by using an expression surrounded by square brackets as a key:

const k = 'first';
const person = {
  [k + 'Name']: 'Joe'
};
console.log(person);
 

Accessing Properties

Sooo… how do we access properties? Using an object from a previous slide:

const course = {
    name: 'Applied Internet Technology',
    section: 8,
    undergraduate: true
};

There are two ways to access properties:

  • the dot operator
// gives us 8
console.log(course.section);
  • square brackets
// gives us 8
console.log(course["section"]);
 

Accessing Properties Continued

Using dot (.) vs using ([]) to access properties: →

course.section;
course["section"];
  • when using dot, the part after the dot directly names the property
    • the property name must be a valid variable names. (what were they again? →)
    • start with a letterunderscore ( _ ), or dollar ( $ ) 
    • following characters can be any of above, and/or digits (0-9)
  • when using square brackets, the part within the brackets is evaluated and is used as the property name
    • this allows for dynamically created property names
    • also allows property names that are not valid variable names obj["I'm ok"] = true (oof, maybe avoid that))

Dynamic Properties

The code below uses brackets for dynamic property access →

  1. it asks for user input, specifying a key / property name
  2. and it should output the value at the key
// using the same object from previous slides...
const course = { name:'Applied Internet Technology', section:8, undergraduate:true };
// setting up user input
const readline = require('readline');
const p = readline.createInterface({ input: process.stdin, output: process.stdout });
p.question('Type in an object key\n>', function (resp) {
	// TODO: print value at key
    p.close();
});

Here, we have to use bracket notation: console.log(course[resp]).

Property Names

All property names are turned into a string prior to being used as a key

This poses a bit of a problem:

  • any object can be used as a key
  • but the string version of two different objects may be the same!
  • by default objects that we create are converted to the string [object Object] when cast to a String.
  • for example: String({}) returns [object Object]
 

Objects as Property Names!? Don't

Here's an example of the issue:

const k1 = {foo:'bar'};
const k2 = {baz:'qux'};
const obj = {};
obj[k1] = 1;
obj[k2] = 2;
console.log(obj);
// { '[object Object]': 2 }
// uh oh! ... you can see that there's only one prop
 

Methods (Again)

It's worthwhile to repeat that an object property can be a function.

  • if object's property is a function, it's sometimes called a method
  • let's try creating some methods…
const obj = {};
function f() {
    console.log("Hi, I'm a method!");
}
obj.doStuff = f;
const obj = {
    doStuff: function() {
        console.log("Hi, I'm a method!");    
    },
};
const obj = {};
obj.doStuff = function() {
    console.log("Hi, I'm a method!");    
};
 

ES6 Shorthand Methods

It's pretty common to create methods on objects, so ES6 introduces a shortcut for creating methods on objects simply by setting properties equal to function expressions: →

const obj = {
    f() {console.log('fffff!');},
    g() {console.log('ggggg!');},
};
obj.f();
obj.g();

Contrast this with the ES5 way of creating methods:

const obj = {
  f: function() {console.log('fffff!');},
  g: function() {console.log('ggggg!');},
};
 

Reading, Modifying and Deleting

  • if the property doesn't exist, we'll get back undefined:
// → gives us undefined
console.log(course.nothingToSeeHere);
  • you can assign values to properties by using the = operator:
course.nothingToSeeHere = 'maybe something';
console.log(course.nothingToSeeHere);
  • you can remove properties by using the delete operator:
delete course.nothingToSeeHere;
console.log(course.nothingToSeeHere);
 

Objects and Mutability

Uh… so what's the implication here regarding objects and mutability? →

  • clearly objects are mutable
    • functions are objects; they're mutable too!
    • arrays are objects; they're mutable too (we'll see this again later)!
  • primitives (such as numbers, strings, booleans, null, and undefined) are not, though!
 

Mutability and References

What will this print out? →

const a = {'foo':1, 'bar':2};
const b = a;
b['baz'] = 3;
b.qux = 4;
console.log(a);
{ foo: 1, bar: 2, baz: 3, qux: 4 }
 

Detecting Properties Continued

There are two ways to determine if a property actually exists (rather than being undefined by default). Using the course object from before:

  • Object.hasOwn - method that tests object passed in has property passed in
  • Object.hasOwn(course, 'name'); // true
    Object.hasOwn(course 'oh no, not here'); // false
  • in - an operator that tests if left operand (a string or number) is property of object in right operand… picks up "inherited" properties
  • 'name' in course; // true
    'oh no, not here' in course; // false

 

Use Object.hasOwn for now… so you won't have to worry about "inherited" properties.

 

Looping Over Properties

Use a for (const prop in obj) loop: →

  • note that prop can be const declared
  • make sure that you use Object.hasOwn in loop to exclude inherited properties
  • avoid using this kind of loop for Arrays
    • does not preserve order
for (const property in course) {
	if (Object.hasOwn(course, property)) {
		console.log(property +  " is " + course[property]);
	}
}
 

Looping Over Object Props/Vals II

An alternative way to loop over an object is to use Object.entries →

  • Object.entries(someObj) returns an Array representing the object passed in
  • the Array consists of two-element sub-arrays, each containing a property and value as elements
  • for example: {a: 1, :b 2} becomes [['a', 1], ['b', 2]]
  • with Array destructuring to "unpack" each two element Array as for loop variables:
    for (const [prop, val] in Object.entries(course)) {
      console.log(prop +  " is " + val);
    }

Object.hasOwn vs obj.hasOwnProperty

JavaScript objects typically have a method called hasOwnProperty.

  • this checks for properties just as Object.hasOwn
  • however, it is a method called on some object: myObj.hasOwnProperty(propName)
  • it's possible that an object doesn't have a hasOwnProperty (this is rare and probably a sign of more significant issues)

Consequently, the preferred method of testing for a property is Object.hasOwn

 

Some Behind the Scenes

In reality, though, stringsnumbers and booleans aren't objects; they're primitives (you know, kind of like Java).

However, as soon as you perform an object-like operation on them, such as a method call:

  • JavaScript creates an actual String, Number or Boolean object that wraps that primitive…
  • and throws it away immediately, once the operations is done
  • this does mean, however, that you can't create arbitrary properties on primitives

 

See this article on the secret life of JavaScript primitives!

Chaining

Note that you can chain method calls or property access on objects (if the prior method call or property returns an object with the next method/attribute accessible__→

const obj = {
  getData: function(n) {return [1, 2, 3]}
} 

const res = obj
  .getData()
  .pop();

console.log(res);
 

There be Nulls!

What happens if a property access within the chain results in undefined or null? →

const obj = {
  getData: function(n) {return null}
} 

const res = obj
  .getData()
  .pop();

console.log(res);

We actually get a runtime error because null does not have a method called pop.

(we would have to check each intermediary object for a null or undefined value to prevent this kind of error)

 

Optional Chaining

The optional chaining operator?., helps prevent runtime errors, and instead results in undefinedbeing returned if a property access or method call in the chain results in null or undefined →

const obj = {
  getData: function(n) {return null}
} 

const res = obj
  .getData()
  ?.pop();

console.log(res);
 

Wait… is this JSON? 🤷‍♂️

JSON or JavaScript Object Notation is a data storage and communication format based off of JavaScript object literals… but with a few modifications:

  • all property names are surrounded by double quotes
  • values are restricted to simple data: no function calls, variables, comments or computations


Conversion to-and-from JSON can be done using the following methods on the built-in JSON object:

  • stringify(value) - returns a JSON string representation of the value passed in
  • parse(text) - returns an object created from the supplied JSON text
  • for example: JSON.parse("[1, 2, 3]")
 

And Finally a Map

We'll see later that objects have some "built in" properties and methods, so if we really just want keys and values, we can use a Map instead:

  • created with constructor, new Map()
  • managing keys and values achieved through get and set methods
const states = new Map();
states.set('NJ', 'New Jersey');
console.log(states.get('NJ')) // New Jersey
console.log(states.get('CA')) // undefined
posted @ 2023-01-26 01:13  M1stF0rest  阅读(360)  评论(0编辑  收藏  举报