Skip to content

Instantly share code, notes, and snippets.

@CodeDraken
Created October 9, 2018 20:00
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save CodeDraken/870a732174f54601d63b92f2fc1818d1 to your computer and use it in GitHub Desktop.
Save CodeDraken/870a732174f54601d63b92f2fc1818d1 to your computer and use it in GitHub Desktop.
Code for JavaScript Essentials: Objects
// Wolf
// - Properties
// -- fur color
// -- eye color
// -- size
// -- gender
// -- age
// - Actions
// -- walk/run
// -- eat
// -- sleep
// Dog extends Wolf
// - Properties
// -- species name
// - Actions
// -- pee on fire hydrant
const jeff = {
age: 25,
name: 'Jeff',
greet: () => {
console.log("g'day mate!")
}
}
// access by key name
jeff.age // 25
// access by variable or string keyname
const n = 'name'
jeff[n] // 'Jeff'
// methods are the same
jeff.greet() // g'day mate!
jeff['greet']()
// This Context Examples
function contextLogger () {
return this
}
// global context
contextLogger(this) // window object with JS methods on it
// set context to an object
const obj = {
test: 'value',
secret: 'key'
}
// .call lets us set the context/this and pass arguments
contextLogger.call(obj) // { test: 'value', secret: 'key' }
this automatically set inside an object
const person = {
name: 'Bob',
greet: function () {
return 'Hello, I am ' + this.name
},
deep: {
deepFunc: function () {
return this.name
}
},
}
person.greet() // 'Hello, I am Bob'
person.deep.deepFunc() // { deepFunc: [Function: deepFunc] }, undefined
person.deep.deepFunc.call(person) // { Person Object }, 'Bob'
// This Context Examples
function logThis() {
return this
}
// global context
logThis() // window/global - has JS methods on it
// set context to an object
const obj = {
test: 'value',
secret: 'key'
}
// .call lets us set the context/this and pass arguments
logThis.call(obj) // { test: 'value', secret: 'key' }
// from another function
function fromFunc() {
return logThis()
}
fromFunc() // window/global
const fromObj = {
name: 'this in an object',
t: function() {
return this
},
deepThis: function() {
const setname = function(n) {
this.name = n
return this // this no longer refers to obj
}
return setname('Deep this')
}
}
fromObj.t() // { name: 'this in an object', t: ... }
fromObj.deepThis() // window/global object
// window.name // 'Deep this'
const deepThisFix = {
name: 'this in an object',
fix: function () {
// create a variable to access this deeply
const self = this
const setname = function (n) {
self.name = n
return self
}
return setname('Deep with a variable')
}
}
deepThisFix.fix() // { name: 'Deep with a variable', ... }
Default This
function thisDefault() {
return this
}
thisDefault() // window/global
function thisStrict() {
"use strict"
return this
}
thisStrict() // undefined
// Arrow Function Binding
const arrow = () => {
return this
}
arrow() // global/window
arrow.call({ test: 'binding' }) // global/window
function c () {
return this
}
c.call({ test: 'binding' }) // { test: 'binding' }
const fromObj = {
name: 'this in an object',
deepThis: function() {
const setname = (n) => {
this.name = n
return this
}
return setname('Deep this')
}
}
fromObj.deepThis() // { name: 'Deep this', deepThis: [Function: deepThis] }
// Implicit Binding
this automatically set inside an object
const person = {
name: 'Bob',
greet: function () {
return 'Hello, I am ' + this.name
}
}
person.greet() // 'Hello, I am Bob'
'new' Keyword Binding
const ObjConstructor = function (x, y, z) {
this.x = x
this.y = y
if (z) this.z = z
}
const one = new ObjConstructor(3, 2, 10) // { x: 3, y: 2, z: 10 }
const two = new ObjConstructor(7, 5) // { x: 7, y: 5 }
// Object Literals
// made using {}
const jeff = {
age: 25,
name: 'Jeff',
greet() {
console.log('My name is ' + this.name)
}
}
// access by key name
jeff.age // 25
// access by variable or string keyname
const n = 'name'
jeff[n] // 'Jeff'
// methods are the same
jeff.greet() // My name is Jeff
jeff['greet']() // My name is Jeff
// Getters and Setters
// Getter
// allows us to return a custom value
// when property is accessed
const postsController = {
postIds: [ 5, 3, 11, 22 ],
get latest() {
return this.postIds[this.postIds.length - 1];
}
}
postsController.latest // 22
// Setter
// similar to Getter except
// it handles setting values
const browser = {
active: 'google',
history: [],
set currentPage(val) {
this.history.push(this.active)
this.active = val
}
}
browser.currentPage = 'bing'
browser.currentPage = 'medium'
console.log(browser)
{
active: 'medium',
history: [ 'google', 'bing' ],
currentPage: [Setter]
}
// Key-Pair Array to Object
// Convert this to an Object
const arr = [
[ 'key', 'value' ],
[ 'x', 100 ],
[ 'y', 200 ]
]
// Use Object.fromEntries(iterable)
// results in
const obj = Object.fromEntries(arr)
{
key: 'value',
x: 100,
y: 200
}
// convert back
Object.entries(obj)
[
[ 'key', 'value' ],
[ 'x', 100 ],
[ 'y', 200 ]
]
// Make an Object Immutable
// Use Object.freeze(obj)
const obj = {
prop: 500
}
Object.freeze(obj)
obj.prop = 1
obj // { prop: 500 }
const arr = [ 1, 2, 3 ]
Object.freeze(arr)
arr[0] = 5
arr.push(2) // error
arr // [ 1, 2, 3 ]
// Shallow Copy
// Use Object.assign(target, ...sources)
// target being the new object,
// sources are objects you copy from
const a = {
prop: 'value'
}
const b = {
pos: {
x: 500,
y: 200
}
}
const c = Object.assign({}, a)
// { prop: 'value' }
// values are copied, different objects
c === a // false
Object.is(c, a) // false
// references are still the same
const d = Object.assign({}, b)
// { pos: { x: 500, y: 200 } }
d.pos === b.pos // true
delete d.pos.x
console.log(d, b)
// both objects were affected
// { pos: { y: 200 } } { pos: { y: 200 } }
// Spread operator also shallow copies
const e = {...b}
e.pos === b.pos // true
// Deep Copy
const basic = {
pos: {
x: 250,
y: 500
}
}
// Use JSON trick
// This will only work when there are no methods
// convert to a string then parse it back into JS
const clone = JSON.parse(JSON.stringify(basic))
clone.pos.x = 5
console.log(clone, basic)
// { pos: { x: 5, y: 500 } } { pos: { x: 250, y: 500 } }
// Object to JSON
const obj = {
pos: {
x: 250,
y: 500
},
privateKey: 123
}
// use JSON.stringify(val, replacer, space)
// Val usually being an object
// replacer for replacing values
// space for formatting
JSON.stringify(obj)
// ugly JSON string
'{"pos":{"x":250,"y":500},"privateKey":123}'
// make it pretty
JSON.stringify(obj, null, 2)
/*
{
"pos": {
"x": 250,
"y": 500
},
"privateKey": 123
}
*/
// Replacer
JSON.stringify(obj, (key, val) => {
if (key === 'privateKey') {
return undefined
}
return val
})
'{"pos":{"x":250,"y":500}}'
// Array Replacer Filter
// only allow these keys
const allowed = ['pos', 'x', 'y']
JSON.stringify(obj, allowed)
'{"pos":{"x":250,"y":500}}'
// Loop an Object
const room = {
x: 200,
y: 200,
children: {
badGuy: {
health: 500
}
}
}
// Loop Keys
Object.keys(room).forEach(key => {
const val = room[key]
console.log(key, val)
})
/*
x 200
y 200
children { badGuy: { health: 500 } }
*/
// Loop Values
Object.values(room).forEach(val => {
console.log(val)
})
/*
200
200
{ badGuy: { health: 500 } }
*/
// for loop, loops keys
for (let a in room) {
console.log(a)
}
/*
x
y
children
*/
// Entries
for (let [key, val] of Object.entries(room)) {
console.log(key, val)
}
/*
x 200
y 200
children { badGuy: { health: 500 } }
*/
// Check if a Key exists in an Object
const obj = {
a: 1
}
// Use 'in'
// be aware it will also look at prototype
'a' in obj // true
'b' in obj // false
'toString' in obj // true
// use obj.hasOwnProperty(prop)
// will not check prototype
obj.hasOwnProperty('a') // true
obj.hasOwnProperty('toString') // false
// Classes
// we'll start with an Animal,
// very basic and make few assumptions
// top level class
class Animal {
// the values we pass in go here
constructor(name) {
this.name = name
}
// static method - only exists on Animal constructor
static extinct(species) {
console.log(species + ' have gone extinct!')
}
}
// use it by calling new ConstructorName(props)
const firstAnimal = new Animal('Frank')
// Animal { name: 'Frank' }
// we'll say a mammal comes next in the chain
// a mammal is an animal and has all the same properties
class Mammal extends Animal {
constructor(name, hasFur) {
// call the Animal constructor passing the name along
super(name)
this.hasFur = hasFur
this.warmBlooded = true
this.level = 0
}
// instance method that applys
// to a specific mammal
eat(food) {
console.log(this.name + ' eats a ' + food)
this.level++
}
}
class Wolf extends Mammal {
constructor(name) {
// all wolves have fur, so pass true
super(name, true)
this.carnivore = true
}
}
// static properties have to be
// defined outside of the class body
Wolf.speciesName = 'wolves'
const bob = new Wolf('Bob')
const fido = new Wolf('Fido')
Wolf {
name: 'Fido',
hasFur: true,
warmBlooded: true,
carnivore: true,
level: 0
}
fido.eat('rabbit') // Fido eats a rabbit
// fido is now level 1, no other wolves affected
fido.extinct('Wolves') // undefined
Animal.extinct('Wolves') // Wolves have gone extinct!
// Prototype Example
// lets first take a look at a constructor we saw earlier
// but we'll add a method called 'getCoords'
const ObjConstructor = function (x, y) {
this.x = x
this.y = y
this.getCoords = () => {
return [ this.x, this.y ]
}
}
const one = new ObjConstructor(3, 2) // { x: 3, y: 2 }
const two = new ObjConstructor(7, 5) // { x: 7, y: 5 }
// testing that it works
one.getCoords() // [ 3, 2 ]
two.getCoords() // [ 7, 5 ]
// they each have their own function in memory!
one.getCoords === two.getCoords // false
Object.is(one.getCoords, two.getCoords) // false
// Now let's try with a prototype
// and see what happens
const withProto = function (x, y) {
this.x = x
this.y = y
}
withProto.prototype.getCoords = function() {
return [ this.x, this.y ]
}
const pOne = new withProto(3, 2) // { x: 3, y: 2 }
const pTwo = new withProto(7, 5) // { x: 7, y: 5 }
// testing that it works
pOne.getCoords() // [ 3, 2 ]
pTwo.getCoords() // [ 7, 5 ]
// only one function exists in memory!
pOne.getCoords === pTwo.getCoords // true
Object.is(pOne.getCoords, pTwo.getCoords) // true
pOne.prototype // undefined - has no own prototype
// links to parent's prototype
pOne.__proto__ // withProto { getCoords: [Function] }
// Prototype Chain Example
// simple dog object
const dog = {
sound: 'Woof!',
bark() {
console.log(this.sound)
}
}
dog.bark() // 'Woof!'
const max = {
sound: 'RAAARGH!'
}
// dont set proto like this
// just using __proto__ for demo purposes
max.__proto__ = dog
max.bark() // RAAARGH!
// it doesnt see bark method on max
// so it goes up the prototype chain to dog
// it still uses max as 'this'
const catDog = {
sound: 'Meowoof'
}
// 2 layers of prototypes!
catDog.__proto__ = max
catDog.bark() // Meowoof
// 1. catDog - does not have bark method
// 2. Go up prototype - max object does not have bark
// 3. Go up again - bark is on dog
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment