JavaScript is a multi-paradigm language, combining tools for imperative, functional, and object-oriented programming. FYI, the style of programming we'll learn to use during our course is best described as procedural.
Up until now we've covered the imperative (variable declarations, if, and for statements) and functional (ternary expressions, functions, recursion, and callbacks AKA higher order functions) aspects of programming with JavaScript. However, JavaScript is a fundamentally object-oriented language.
Every data type that we have worked with (numbers, booleans, strings, arrays, dictionaries, functions) is an object.
Functions are a data type in JavaScript.An object is the combination of data and logic. Up until now we have treated data as inert, something to be operated on and passed to functions, but objects have something called methods. Methods are special functions that reference the values of the data stored in the object.
you can call a method with dot notation object.method(arguments). The period between object and method is the dot in dot notation.
Because all of the data types we've been working with are objects, they all have a bunch of built-in methods and special properties.
All objects have toString and valueOf methods.
valueOf just returns the value of the object, which isn't very exciting, but toString turns things into strings (which sounds mildly more interesting).
So let's do that:
let num = 3
num.toString() // '3'
let func = (num) => num + 1
func.toString() // '(num) => num + 1'
let bool = true
bool.toString() // 'true'
let str = 'hello'
str.toString() // 'hello'
let arr = [1, 2, 'three']
arr.toString() // '1,2,three'
let dic = {value: 3}
dic.toString() // '[object Object]'Booleans, strings, and numbers all have pretty expected results, a string representation of the value.
Functions give you the actual function definition as a string.
Arrays turn everything into a concatenated string separated by commas.
Dictionaries just give you a string that says '[object Object]'.
Each of these objects has their own unique implementation of the toString method.
Dictionaries aren't really a JavaScript data type. They're just objects. We've been using a limited subset of the object's functionality to work with a dictionary like data structure.When programming with JavaScript we don't generally care that booleans, numbers and functions are objects. Their methods aren't that useful most of the time.
the most helpful built-in methods are on strings and arrays.
We'll talk more about dictionaries/objects and methods below when we talk about creating our own methods.
First let's talk about length. length is a dynamic property of string and array objects. It's value is the length of the sequence, i.e. (usually) the number of elements in the sequence.
The string 'I love programming' has a length of 18. We can access the length like this:
let sentiment = 'I love programming'
sentiment.length // 18We say that length is a dynamic property because it changes with the number of elements. Of course strings are immutable, so they never change, but arrays can have new elements inserted, and in that case, the value of length will reflect the change.
Here are a few interesting string methods:
let string = 'I love programming'
string.replace('I', 'You') // 'You love programming'
string.slice(0, 6) // 'I love'
string.concat(' with JavaScript') // 'I love programming with JavaScript'
string.split(' ') // ['I', 'love', 'programming']
// indexOf gives you the index of a substring if it exists, or -1 if it doesn't
string.indexOf('p') // 7
string.indexOf('q') // -1None of the methods we just mentioned mutate the string (strings are immutable), they return a new version of the string which you can then work with as you like.
If you like you can take a look at the documentation for the string object here. The methods are listed as String.prototype.method in the sidebar. We'll talk more about prototypes in the next module.As well as the length property, arrays share many methods with strings. Methods like slice, concat, and indexOf work the same way on arrays as they do on strings.
The major difference between arrays and Strings is that arrays are mutable. This doesn't mean that slice, concat, and indexOf will mutate arrays (they will return a new array), but it does mean that there are other methods that can mutate arrays.
Let's take a look at some array methods that don't mutate the array, just return a new one:
let arr = [1, 2, 3]
// Remember when we made our own map?
// The array actually has a map method.
// Because the data context of the map is the array,
// we don't have to pass an array as an argument,
// we just pass it a function.
let addOne = (num) => num + 1
arr.map(addOne) // [2, 3, 4]
// Here'a another map like method
// that doesn't mutate the array:
// Filter removes items from the new array
// if the callback function returns false.
let isOdd = (num) => num % 2 !== 0
arr.filter(isOdd) // [1, 3]There's something worth mentioning at this point. When people use functions like reduce and filter with callback functions, they don't usually define the function first.
JavaScript allows you to declare anonymous functions, functions whose only use is to be immediately invoked or passed as a callback.
Let's take a look at running our map and filter with anonymous functions:
let nums = [4, 5, 6]
nums.map((num) => num + 3) // [7, 8, 9]
nums.filter((num) => num % 2 !== 0) // [5]At first it's a little harder to read, because of the nested parentheses. But it means you don't have to jump around to read the function declaration and then look how it's used. You can read the whole expression in one line.
When a method returns an object, it's possible to immediately call another method without defining the result as a variable. Let's chain call map and filter:
let ns = [1, 2, 3, 4, 5]
ns.map((n) => n + 1).filter((n) => n % 2 !== 0) // [3, 5]We can make this more readable by adding some whitespace:
ns
.map((n) => n + 1)
.filter((n) => n % 2 !== 0) // [3, 5]As we know, an array is an object with data and methods. Some of the array's methods can mutate it's data instead of returning a new object. Let's consider a few:
let letters = ['b', 'c', 'a', 'd']
// Sort by character order.
letters.sort() // ['a', 'b', 'c', 'd']
letters // ['a', 'b', 'c', 'd']
// Add a new item to the end of the array.
letters.push('e')
letters // ['a', 'b', 'c', 'd', 'e']
// Remove the last item from the array
// and return it.
letters.pop() // 'e'
letters // ['a', 'b', 'c', 'd']
// Remove a subarray from the array
// and return it.
letters.splice(3, 4) // ['d']
letters // ['a', 'b', 'c']As you can see, the value of letters has been changed by each of the above method calls.
While you're becoming familiar with JavaScript you should always ask yourself if the array method your calling is going to mutate the array.
Objects are actually dictionaries. JavaScript doesn't have a dictionary data type, it just has objects, and objects all act as dictionaries, but with super powers.
We can create our own generic objects using the dictionary syntax we learned previously.
We know how to set and access properties on generic objects with [ brackets ] obj['key'] = 'value'. We used bracket notation because it makes sense when we're treating an object like a data structure with keys that work similar to the indexes of an array.
When we treat an object like an object we use dot . notation to access properties obj.key = 'value'.
Let's create a new object:
let pizza = {
style: 'Napolitana',
size: 14, // inches
price: 1600, // cents
slices: 8
}
pizza.slices // 8Because functions are just data like objects, we can actually add functions as values in objects:
let namespace = {
addOne: (num) => num + 1
}
namespace.addOne(3) // 4This is called namespacing. It allows us to keep dictionaries full of functions, without polluting the global namespace with our addOne function. This practice is very important when you have tens of scripts running on your page, each with many hundreds of lines of code, and each written by their own teams. It's a way of avoiding naming clashes.
Methods are like our regular => arrow functions, except they have access to special variable called this.
this is a reference to the object on which the method is defined, it allows the method function to use or mutate the object's data.
Let's declare and use a method on our pizza object:
pizza = {
eat(slices) {
this.slices = this.slices - slices
},
style: 'Napolitana',
size: 14, // inches
price: 1600, // cents
slices: 8
}
pizza.eat(3)
pizza.slices // 5...and that's how you define a method:
methodName(parameters) { function logic }
We just learned that all our data types are also objects, and that those objects have methods. We learned to use some useful array and string methods We also learned to create our own generic objects with methods.
Let's create a function that translates multi-word strings (badly) to simplified Mandarin Chinese:
let translate = (message) => {
let dic = {
hello: '你好',
this: '我',
is: '上午',
dog: '狗'
}
return message
.split(' ')
.map((word) => {
let lowerCased = word.toLowerCase()
return dic[lowerCased]
})
.join(' ')
}
translate('Hello this is dog') // '你好 我 上午 狗'Let's inspect this:
let obj = {
value: 'hello',
getThis() {
console.log(this)
}
}
obj.getThis() // Object {value: "hello"}Let's turn our English to Chinese dictionary into an object:
let dictionary = {
definitions: {
hello: '你好',
this: '我',
is: '上午',
dog: '狗'
},
translate(word) {
return this.definitions[word.toLowerCase()]
}
}
dictionary.translate('Hello') // '你好'let makePizza = (options) => {
let rad = options.size / 2
let area = rad * rad * 3.14
let areaPerSlice = area / options.slices
let pizza = {
area,
areaPerSlice,
eat(slices) {
let eaten = slices * this.areaPerSlice
this.slices = this.slices - slices
this.area = this.area - eaten
return eaten
}
}
return Object.assign(pizza, options)
}
let order = {
style: 'Napolitana',
price: 1600, // cents
size: 14, // inches
slices: 8
}
let pizza = makePizza(order)
pizza.style // 'Napolitana'
pizza.eat(4) // 76.93
pizza.slices // 4
pizza.area // 76.93given this data structure:
let coords = [
{x: -3, y: 4},
{x: 0, y: 4},
{x: 2, y: 1},
{x: 1, y: 2}
]- use
.mapto only get the x values - use
.mapto only get the y values - use
.filterto remove all coordinates with x less than 0 - use
.filterto only keep coordinates where y is 4 - use
.concatto add these additional coordinates[{x: 0, y: 1}] - use
.sliceto get the last 2 coordinates - chain
.filterand.mapto only get x greater than 0 values
Create a pizza restaurant object that sells pizza objects for money.

