Reference notes for a short series of lectures on the latest ~JavaScript language features.
- Strings: Templates, Methods
- Functions: Arrow Functions, Default Parameter Values, Spread & Rest Operators
- Object-Oriented: Prototypes, Classes, The Module Pattern, Lexical Context
- Flow Control: Promises, Async & Await, Generators & Iterators
- Immutability: Why Immutability?, Primitives, Non Primitives, Object Assignment
- Objects: assign, values, keys, entries, freeze, seal
- Arrays: filter, every, includes, some, from, of, forEach, copyWithin, fill, find, findIndex
- Meta Programming: Reflect, Proxy
- React: How ES Next syntax makes your React code cleaner and easier to manage.
[[TOC]]
- Const - A constant never changes it's primitive value.
- Let - Let can be updated
- Block scoped - invisible outside of the scope in which they were assigned.
- Can't be re-assigned (referenced to another name-space)
A constant never changes it's primitive value.
// Const can re-reference Var
const a = 2
a = 2
// Uncaught TypeError: Assignment to constant variable.
// Const can re-reference Var
var a = 1
const a = 2
console.log(a)
// Var cannot re-reference Const
const a = 1
var a = 2
// ...or...
a = 2
// SyntaxError: "Identifier 'a' has already been declared"
// (Execution of your script will halt!)
Const and Var are both blocked scoped.
{
const a = 2
}
console.log(a)
// Uncaught ReferenceError: a is not defined
You can assign to a name-space that is also used in the parent block.
const a = 1
{
const a = 2
console.log(a)
}
Let can be updated.
let a = 1
a = 2
console.log(a)
// 2
Let cannot be re-assigned.
let a = 1
let a = 2
// SyntaxError: Identifier 'a' has already been declared
for (let i = 0; i < 10; i++) {
console.log(i)
}
console.log(i)
// ReferenceError: i is not defined
if (true) {
const a = 1
}
const a = 2
console.log(a)
// 2
- Never use
var
in a react application. - Always use
const
andlet
.
Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 specification.
const name = 'Al'
const output = `My name is: ${name}`
console.log(output)
// My name is: Al
const cool = function(strings, foo, bar, baz) {
console.log(strings, foo, bar, baz)
return 'Psych!'
}
const foo = 111
const bar = 222
const baz = 333
const output = cool`One ${foo} two ${bar} three ${baz}.`
console.log(output)
// Psych!
const cool = function(str, foo, bar, baz) {
console.log(strings, foo, bar, baz)
return str[0] + foo + str[1] + bar + str[2] + baz + str[3]
}
const foo = 111
const bar = 222
const baz = 333
const output = cool`One ${foo} two ${bar} three ${baz}!`
console.log(output)
// One 111 two 222 three 333!
console.log(`
This
is
valid!
`)
//
// This
// is
// valid!
// ... aka ...
// \nThis\nis\nvalid!
import React from "react";
import ReactDOM from "react-dom";
const MyComp = (props) => (
<div>
<h1>Hello, {props.name}!</h1>
<h2>{`Hello, ${props.name}!`}</h2>
</div>
)
function App() {
return (
<div>
<MyComp name="World"/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
const val = 'Foo'
console.log(val.repeat(10))
// FooFooFooFooFooFooFooFooFooFoo
const val = 'Foo'
const pattern = '123456789'
console.log(val.padStart(4, pattern))
// 1Foo
console.log(val.padStart(8, pattern))
// 1234Foo
console.log(pattern)
const val = 'Foo'
const pattern = '123456789'
console.log(val.padEnd(4, pattern))
// 1Foo
console.log(val.padEnd(8, pattern))
// 1234Foo
console.log(pattern)
The old way
const a = 'foo'
const b = 'bar'
const c = 'baz'
const obj = {a: a, b: b, c: c}
console.log(obj)
// {a: "foo", b: "bar", c: "baz"}
const a = 'foo'
const b = 'bar'
const c = 'baz'
const obj = {a, b, c}
console.log(obj)
// {a: "foo", b: "bar", c: "baz"}
getData = () => {
const method = "GET";
const url = "https://jsonplaceholder.typicode.com/posts/1";
const obj = { url: url, method: method };
// Object Literal Shorthand
// const method = "GET";
// const url = "https://jsonplaceholder.typicode.com/posts/1";
// const obj = { url, method };
fetchData(obj).then(data => this.setState({ data }));
};
The old way
function rotate(props) {
var x = props.x
var y = props.x
var z = props.x
var originX = props.origin.x
var originY = props.origin.y
var originZ = props.origin.z
// ... apply transformation matrix
}
rotate({
x: 0,
y: 1,
z: 0,
origin: {
x: 0,
y: 0
}
})
The new way
const rotate = props => {
let {
x,
y,
z,
origin: {x: originX, y: originY}
} = props
// ... apply transformation matrix
}
rotate({
x: 0,
y: 1,
z: 0,
origin: {
x: 0,
y: 0
}
})
const rotate = coords => {
let [x, y, z] = coords
console.log(x, y, z)
}
const coords = [0, 1, 2]
rotate(coords)
const rotate = ({x, y, z}) => {
console.log(x, y, z)
}
const coords = {x: 0, y: 1, z: 0}
rotate(coords)
Arrays
const rotate = ([x, y, z]) => {
console.log(x, y, z)
}
const coords = [0, 1, 0]
rotate(coords)
const MyOldComponent = props => (
<div>
<p>
{props.a}, {props.b}, {props.c}
</p>
</div>
);
const MyDestructuredComponent = ({ a, b, c }) => (
<div>
<p>
{a}, {b}, {c}
</p>
</div>
);
const log = msg => {
console.log(msg)
}
log('Hi!')
const log = msg => console.log(msg)
log('Hi!')
const log = msg => console.log(msg)
log('Hi!')
function curry(a) {
return function(b) {
return function(c) {
return function(d) {
return a + b + c + d
}
}
}
}
console.log(curry(1)(2)(3)(4))
// Logs 10
// Currying with Arrow Functions
const curry = a => b => c => d => {
return a + b + c + d
}
console.log(curry(1)(2)(3)(4))
// Logs 10
Promises without arrow functions:
function getData(url) {
return new Promise(function(resolve) {
return fetch(url).then(function(response) {
resolve(response.json())
})
})
}
getData('https://jsonplaceholder.typicode.com/posts/1').then(data =>
console.log(data)
)
Promises with arrow functions:
const getData = url =>
new Promise(resolve =>
fetch(url).then(response => resolve(response.json()))
)
getData('https://jsonplaceholder.typicode.com/posts/1').then(data =>
console.log(data)
)
(async () {
const getData = async url => JSON.parse((await fetch(url)).data)
const data = await getData('https://jsonplaceholder.typicode.com/posts/1')
console.log(data)
})()
const MyComponent = () => <div/>;
doSomething = () => {
alert("Something!");
};
const adder = (a, b) => {
return a + b
}
const result = adder(2, 2)
console.log(result)
// 4
const adder = (a, b) => {
return a + b
}
const result = adder()
console.log(result)
// NaN
const adder = (a, b) => {
a = a || 2
b = b || 2
return a + b
}
const result = adder()
console.log(result)
// 4
const adder = (a = 2, b = 2) => {
return a + b
}
const result = adder()
console.log(result)
// 4
// You need parentheses with arrow functions and the rest operator
const adder = (start, ...vals) => {
let result = start
vals.forEach(val => {
result += val
})
return result
}
const start = 1
const result = adder(start, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512)
console.log(result)
// 1024
// You need parentheses with arrow functions and the rest operator
const adder = (start, ...vals) => {
let result = start
vals.forEach(val => {
result += val
})
return result
}
const start = 1
const values = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
const result = adder(start, ...values)
console.log(result)
// 1024
const vals = [2, 3, 4]
var a = [1, ...vals, 5]
console.log(a)
//Β [1, 2, 3, 4, 5]
const myTag = (strs, ...vals) =>
vals
.map((val, i) => {
return strs[i] + val
})
.concat(strs[strs.length - 1])
.join('')
const foo = 111
const bar = 222
const baz = 333
const output = myTag`One ${foo} two ${bar} three ${baz}!`
console.log(output)
// One 111 two 222 three 333!
Demo: Spread Template Tags
class Greeting extends React.Component {
render() {
console.log(this.props)
return <h1 {...props}>Hello</h1>
}
}
function Dog() {}
console.log(Dog.prototype)
function Dog(name) {
this.name = name
}
const buddy = new Dog('Buddy')
console.log(buddy)
function Dog(name) {
this.name = name
}
Dog.prototype.bark = function() {
console.log(`Woof, my name is ${this.name}!`)
}
const buddy = new Dog('Buddy')
buddy.bark()
// Woof, my name is Buddy!
function Dog(name) {
this.name = name
}
Dog.prototype.bark = function() {
console.log(`Woof, my name is ${this.name}!`)
}
function FlyingDog(name) {
Dog.call(this, name)
}
FlyingDog.prototype.fly = function() {
console.log("I'm flying!")
}
FlyingDog.prototype.constructor = Dog
FlyingDog.prototype = new Dog()
const buddy = new Dog('Buddy')
const bella = new FlyingDog('Bella')
buddy.bark()
// Woof, my name is Buddy!
bella.bark()
// Woof, my name is Bella!
bella.fly()
// I'm flying!
class Dog {}
const buddy = new Dog()
console.log(buddy)
class Dog {
bark() {
console.log('woof')
}
}
const buddy = new Dog()
buddy.bark()
class Dog {
constructor(name) {
this.name = name
}
bark() {
console.log(`Woof, my name is ${this.name}!`)
}
}
const buddy = new Dog('Buddy')
const bella = new Dog('Bella')
buddy.bark()
bella.bark()
- Extends()
- Super()
class Dog {
constructor(name) {
this.name = name
}
bark() {
console.log(`Woof, my name is ${this.name}!`)
}
}
class FlyingDog extends Dog {
constructor(name) {
// Inheritance
super(name)
}
// Polymorphism
fly() {
console.log("I'm flying!")
}
}
const buddy = new Dog('Buddy')
const bella = new FlyingDog('Bella')
buddy.bark()
bella.bark()
bella.fly()
// buddy.fly()
// TypeError: buddy.fly is not a function
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
The context of this
inside foo.log
is foo
.
const foo = {
bar: 123,
log: function() {
console.log(this.bar)
}
}
foo.log()
// 123
The context of this
inside inner
is Window
.
const foo = {
bar: 123,
log: function() {
const inner = function() {
console.log(`bar = ${this.bar}`)
console.log(this)
}
inner()
}
}
foo.log()
// bar = undefined
The context of this
inside inner
is foo
.
const foo = {
bar: 123,
log: function() {
const inner = function() {
console.log(this.bar)
}
inner.call(this)
}
}
foo.log()
// 123
Lexical scope is referenced in react components. React components are classes.
class Welcome extends React.Component {
state = {
name: 'Alistair'
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
βA closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.β
;(function iAmEnclosed() {
const a = 1
})()
// ReferenceError: a is not defined
console.log(a)
const createDog = function dogClosure(name) {
const secret = 'I hate squirrels!'
const dog = {
name,
bark: function() {
console.log(`Woof, my name is ${this.name}!`)
},
tellSecret: function() {
console.log(secret)
}
}
return dog
}
const buddy = createDog('Buddy')
buddy.bark()
// Woof, my name is Buddy!
console.log(buddy.secret)
// undefined
buddy.tellSecret()
// I hate squirrels!
What is "Immutability"?
- Immutability is about being "Non-Destructive"
- "Immutable" software means the original referenced value 1) does not change and 2 can not change.
- Every time you update an object, you should receive a new copy of the original.
- (The different between "Save" and "Save As")
See YouTube Video: ReactCasts #9 - Immutability in JavaScript
- Why is immutability important? (in no particular order)
- Re-usability
- Providing Stable State
- Reduce/eliminate of unintended side-effects (functional programming)
- Better data control
- Replaying State at specific points (Time Travel)
- Tracking bugs, etc.
- Undo/Redo implementations
- Performance boosts with DOM Tree Diff'ing
- Parallelization (Multi Core)
An idempotent operation produces the result in the same state even if you call it more than once, provided you pass in the same parameters.
function createPerson () {
let name
let age
const person = {
getName: () => {
return name
},
getAge: () => {
name = 'Bob'
return age
},
setName: function (newName) {
name = newName
},
setAge: function (newAge) {
age = newAge
}
}
return person
}
const person = createPerson()
person.setName('Alistair')
person.setAge(37)
console.log(person)
// {getAge: Ζ, updateName: Ζ, updateAge: Ζ, name: "Alistair", age: 37}
console.log(`Name: ${person.getName()}, Age: ${person.getAge()}`)
// Name: Alistair, Age: 37
console.log(`Name: ${person.getName()}, Age: ${person.getAge()}`)
// Name: Bob, Age: 37
-
JavaScript is not immutable by nature.
-
React is born in the immutable paradigm.
-
Immutable
- Haskell (Purely functional)
- Erlang
- Scala
- R
- Lisp
- Logo
- Scheme
- ML
- Ocaml
- Some types of JavaScript value are "Primitive".
- When you assign primitive variables, the value gets copied in memory.
const a = 'foo'
console.log(a) // foo
const b = a
console.log(b) // foo
b = 'bar'
console.log(a) // foo
console.log(b) // bar
Primitive types include:
- Number
- String
- Boolean
Some types of JavaScript value are "Referential".
- When you assign referential variables, the reference to the memory location is passed into the variable.
const a = {foo: 'bar'}
console.log(a) // {foo: "bar"}
const b = a
console.log(b) // {foo: "bar"}
b = {oh: 'what?!'}
// Both values are now the same!
// They have been pointed to the same reference in memory.
console.log(a) // {oh: 'what?!'}
console.log(b) // {oh: 'what?!'}
The object {foo: "bar"} is now orphaned, will be picked up my garbage collection and should get deleted from memory on the next garbage cycle.
Referential types include:
- Object
- Array
- Function
Object a
remains unchanged.
const a = {foo: 'bar'}
const b = Object.assign({}, a)
console.log(b) // {foo: "bar"}
b.ping = 'pong'
console.log(a) // {foo: "bar"}
console.log(b) // {foo: "bar", ping: "pong"}
- Object
a
remains unchanged.
const a = ['foo', 'bar', 'baz']
console.log(a) // ['foo', 'bar', 'baz']
const b = a.concat('qux')
console.log(b) // ['foo', 'bar', 'baz', 'qux']
- We can also use Spread
- Object
a
remains unchanged.
const a = ['foo', 'bar', 'baz']
console.log(a) // ['foo', 'bar', 'baz']
const b = [...a, 'qux']
console.log(b) // ['foo', 'bar', 'baz', 'qux']
- Object
a
remains unchanged.
const a = ['foo', 'bar', 'baz']
console.log(a) // ['foo', 'bar', 'baz']
const b = a.filter(n => n !== 'bar' ? n : null)
console.log(b) // ['foo', 'baz']
Immutability Challenge #1 Immutability Solution
A simple callback:
function addButNoHurry (a, b, callback) {
setTimeout(() => {
callback(a + b)
}, 1000)
}
const a = 2
const b = 2
addButNoHurry(a, b, function (result) {
console.log(result)
}))
You could clean this up:
function addButNoHurry (a, b, callback) {
setTimeout(() => {
callback(a + b)
}, 1000)
}
function callback (result) {
console.log(result)
}
var a = 2
var b = 2
addButNoHurry(a, b, callback)
// But what if I need my result here...
// π ?
// var result = addButNoHurry(a, b, callback)
// console.log(result)
var result
function addButNoHurry (a, b, callback) {
setTimeout(() => {
result = a + b
}, 1000)
}
var a = 2
var b = 2
addButNoHurry(a, b, callback)
// π
console.log(result)
// - Talk about why nothing happens
// - Talk about undesired-side-effects
- Callback Soup
- Callback Hell
- Pyramid of Doom
With callbacks:
function getUserProfile (success, error) {
$.ajax.get('/user/profile', success, error)
}
function getUserTestResults (success, error) {
$.ajax.get('/user/results', success, error)
}
function getUserClasses (success, error) {
$.ajax.get('/user/classes', success, error)
}
function getUserDashBoardData () {
var profile
var results
var classes
getUserProfile(function (response) {
profile = JSON.stringify(response.data.profile)
getUserTestResults(function (response) {
results = JSON.stringify(response.data.results)
getUserClasses(function (response) {
classes = JSON.stringify(response.data.classes)
UserDashboard.show(profile, results, classes)
}, function (err) {
ErrorModal.show('Could not load test results for user')
})
}, function (err) {
ErrorModal.show('Could not load test results for user')
})
}, function (err) {
ErrorModal.show('Could not load user profile')
})
}
With promises:
const getUserProfile = () => fetch('/user/profile')
const getUserTestResults = () => fetch('/user/results')
const getUserClasses = () => fetch('/user/classes')
const getUserDashBoardData = () => {
Promises.all([
getUserProfile,
getUserTestResults,
getUserClasses
])
.then(responses => responses.map(response => response.json()))
.then(data => UserDashboard.show(...data))
.catch(err => ErrorModal.show(err.message))
}
const myPromise = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hi!')
}, 1000)
})
myPromise().then(result => {
console.log(result)
})
// PromiseΒ {<pending>}
// Hi!
const myPromise = () => new Promise((resolve, reject) => {
setTimeout(() => {
//resolve('Hi!')
reject('Oops!')
}, 1000)
})
myPromise().then(result => {
console.log(result)
}).catch(err => {
console.error(err)
})
// PromiseΒ {<pending>}
// Error: Oops!
const tryToFlipHeads = () => new Promise((resolve, reject) => {
const rnd = Math.round(Math.random(1))
const result = rnd ? 'heads' : 'tails';
setTimeout(() => {
if (result === 'heads') {
resolve('HEADS! :)')
} else {
reject('TAILS :(')
}
}, 1000)
})
tryToFlipHeads().then(result => {
console.log(result)
}).catch(err => {
console.error(err)
}).finally(status => {
console.log('DONE!')
})
// PromiseΒ {<pending>}
// Heads! :)
// ... or ...
// Tails :(
// DONE!
const fetch = require('node-fetch')
const fetchAll = endpoints => new Promise((resolve, reject) => {
const promises = []
endpoints.forEach(endpoint => {
promises.push(fetch(endpoint))
})
return Promise.all(promises).then(resolve).catch(reject)
})
const responseToJson = fetched => new Promise((resolve, reject) => {
const promises = []
fetched.forEach(data => {
promises.push(data.json())
})
return Promise.all(promises).then(resolve).catch(reject)
})
const endpoints = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
'https://jsonplaceholder.typicode.com/posts/4',
'https://jsonplaceholder.typicode.com/posts/5'
]
fetchAll(endpoints)
.then(responseToJson)
.then(items => {
items.forEach((item, i) => {
console.log(`${i}: ${item.title}`)
})
})
// 0: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
// 1: qui est esse
// 2: ea molestias quasi exercitationem repellat qui ipsa sit aut
// 3: eum et est occaecati
// 4: nesciunt quas odio
const fetch = require('node-fetch')
const fetchOne = endpoints => new Promise((resolve, reject) => {
const promises = []
endpoints.forEach(endpoint => {
promises.push(fetch(endpoint))
})
return Promise.race(promises).then(resolve).catch(reject)
})
const endpoints = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
'https://jsonplaceholder.typicode.com/posts/4',
'https://jsonplaceholder.typicode.com/posts/5'
]
fetchOne(endpoints)
.then(result => result.json())
.then(json => console.log(json.title))
// A different title is output each time
Challenge #2 - Creating a Promise
const myPromise = () => new Promise(resolve => {
setTimeout(() => {
resolve('Hi!')
}, 1000)
})
const msg = await myPromise()
console.log(msg)
const fetchAll = endpoints => new Promise((resolve, reject) => {
const promises = []
endpoints.forEach(endpoint => {
promises.push(fetch(endpoint))
})
return Promise.all(promises).then(resolve).catch(reject)
})
const responseToJson = fetched => new Promise((resolve, reject) => {
const promises = []
fetched.forEach(data => {
promises.push(data.json())
})
return Promise.all(promises).then(resolve).catch(reject)
})
const get = async endpoints => {
const fetched = await fetchAll(endpoints)
// console.log(fetched)
const jsonItems = await responseToJson(fetched)
// console.log(jsonItems)
jsonItems.forEach((item, i) => {
console.log(`${i}: ${item.title}`)
})
}
const endpoints = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
'https://jsonplaceholder.typicode.com/posts/4',
'https://jsonplaceholder.typicode.com/posts/5'
]
get(endpoints)
function *() {}
// A Node.js helper library for generators
const co = require('co')
const delay = name => new Promise(resolve => {
const delay = Math.random() * 1000
setTimeout(() => resolve({name, delay}), delay)
})
co(function *() {
const foo = yield delay('foo')
console.log(foo)
const bar = yield delay('bar')
console.log(bar)
const baz = yield delay('baz')
console.log(baz)
})
- Generator Functions
- Iterators
- next
const run = generator => {
const iterator = generator()
const iteration = iterator.next()
const iterate = iteration => {
if (iteration.done) {
return iteration.value
}
const promise = iteration.value
return promise.then(result => iterate(iterator.next(result)))
}
return iterate(iteration)
}
const delay = name => new Promise(resolve => {
const delay = Math.random() * 1000
setTimeout(() => resolve({name, delay}), delay)
})
run(function *() {
const foo = yield delay('foo')
console.log(foo)
const bar = yield delay('bar')
console.log(bar)
const baz = yield delay('baz')
console.log(baz)
})
For this section, we are going to walk through examples of the array method documentation from Developer.Mozilla.org/JavaScript.
... executes a provided function once for each array element.
var array1 = ['a', 'b', 'c'];
array1.forEach(function(element) {
console.log(element);
});
There is no way to stop or break a forEach() loop other than by throwing an > exception. If you need such behavior, the forEach() method is the wrong tool.
Early termination may be accomplished with:
A simple loop, for...of loop, every(), some(), find(), findIndex()
... creates a loop iterating over iterable objects for the value of each distinct property of the object. - Can itterative over Strings, Arrays, Array-like's, NodeList objects, TypedArrays, Maps, Sets and user-defined iterables.
let iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
... creates a new array with all elements that pass the test implemented by the provided function.
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result); // ["exuberant", "destruction", "present"]
The every method executes the provided callback function once for each element present in the array until it finds one where callback returns a falsy value.
function isBelowThreshold(currentValue) {
return currentValue < 40;
}
var array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold)); // true
...tests whether at least one element in the array passes the test implemented by the provided function.
...executes the callback function once for each element present in the array until it finds one where callback returns a truthy value.
var array = [1, 2, 3, 4, 5];
var even = function(element) {
// checks whether an element is even
return element % 2 === 0;
};
console.log(array.some(even));
// expected output: true
...determines whether an array includes a certain element, returning true or false as appropriate. It uses the sameValueZero algorithm to determine whether the given element is found.
var array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
var pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false
returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
var array1 = [5, 12, 8, 130, 44];
var found = array1.find(function(element) {
return element > 10;
});
console.log(found); // 12
... returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.
var array1 = [5, 12, 8, 130, 44];
function findFirstLargeNumber(element) {
return element > 13;
}
console.log(array1.findIndex(findFirstLargeNumber)); // 3
... creates a new Array instance with a variable number of arguments, regardless of number or type of the arguments.
const ary1 = Array.of(7)
const ary2 = Array(7)
console.log(ary1) // [7]
console.log(ary2) // [ , , , , , , ]
... creates a new, shallow-copied Array instance from an array-like or iterable object.
const str = 'foo';
const result = Array.from(str);
console.log(Array.from(str)) // ["f", "o", "o"]
const ary = [4, 8, 16];
const map = val => val * 2
const result = Array.from(ary, map)
console.log(result) // ["f", "o", "o"]
... fills all the elements of an array from a start index to an end index with a static value. The end index is not included.
var array1 = [1, 2, 3, 4];
// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4)) // [1, 2, 0, 0]
// fill with 5 from position 1
console.log(array1.fill(5, 1)); // [1, 5, 5, 5]
console.log(array1.fill(6)); // [6, 6, 6, 6]
... shallow copies part of an array to another location in the same array and returns it, without modifying its size.
arr.copyWithin(target)
arr.copyWithin(target, start)
arr.copyWithin(target, start, end)
var array1 = [1, 2, 3, 4, 5];
// place at position 0 the element between position 3 and 4
console.log(array1.copyWithin(0, 3, 4)); // [4, 2, 3, 4, 5]
// place at position 1 the elements after position 3
console.log(array1.copyWithin(1, 3));
// expected output: Array [4, 4, 5, 4, 5]
const val = undefined + undefined
console.log(val) // NaN
console.log(Number.isNaN(val)) // true
const ary = []
console.log(Array.isArray(ary)) // true
const obj = {}
console.log(Array.isArray(obj)) // false
... determines whether two values are the same value.
console.log(123 === 123) // true
console.log(Object.is(123, 123)) // true
This is not the same as being equal according to the == operator. The == operator applies various coercions to both sides (if they are not the same Type) before testing for equality (resulting in such behavior as "" == false being true), but Object.is doesn't coerce either value.
This is also not the same as being equal according to the === operator. The === operator (and the == operator as well) treats the number values -0 and +0 as equal and treats Number.NaN as not equal to NaN.
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
console.log(0 === -0)
console.log(Object.is(0, -0))
... used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
const obj1 = {1: 'Hello,'}
const obj2 = {2: 'world!'}
const obj3 = Object.assign({}, obj1, obj2)
console.log(obj3) // {1: "Hello,", 2: "world!"}
var foo = {
a: 1,
b: 2,
c: 3
}
console.log(Object.keys(foo)) // ["a", "b", "c"]
... returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
var obj = {
2: 'A',
1: 'B',
0: 'C',
}
console.log(Object.values(obj))
... returns an array of a given object's property names, in the same order as we get with a normal loop.
var obj = {
a: 1,
b: 2,
c: 3
}
console.log(Object.keys(obj)) // ["a", "b", "c"]
... returns an array of a given object's own enumerable property [key, value] pairs, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
var obj = {
a: 1,
b: 2,
c: 3
}
console.log(Object.entries(obj))
// [
// ["a", 1],
// ["b", 2],
// ["c", 3]
// ]
Challenge 1 Challenge 1 - Solution
The Object.freeze() method freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed, it also prevents the prototype from being changed. The method returns the passed object.
const object1 = {
property1: 42
};
const object2 = Object.freeze(object1);
object2.property1 = 33;
The Object.isFrozen() determines if an object is frozen.
const object1 = {
property1: 42
};
console.log(Object.isFrozen(object1));
// expected output: false
Object.freeze(object1);
console.log(Object.isFrozen(object1));
...seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable.
const object1 = {
property1: 42
};
Object.seal(object1);
object1.property1 = 33;
console.log(object1.property1);
// expected output: 33
delete object1.property1; // cannot delete when sealed
console.log(object1.property1);
// expected output: 33
The Object.isSealed() method determines if an object is sealed.
const object1 = {
property1: 42
};
console.log(Object.isSealed(object1));
// expected output: false
Object.seal(object1);
console.log(Object.isSealed(object1));
// expected output: true
Meta-Programming is a programming technique in which computer programs have the ability to treat programs as their data. It means that a program can be designed to read, generate, analyse or transform other programs, and even modify itself while running. In some cases, this allows programmers to minimize the number of lines of code to express a solution, thus reducing the development time. It also allows programs greater flexibility to efficiently handle new situations without recompilation.
Starting with ECMAScript 2015, JavaScript gains support for the Proxy and Reflect objects allowing you to intercept and define custom behavior for fundamental language operations (e.g. property lookup, assignment, enumeration, function invocation, etc). With the help of these two objects you are able to program at the meta level of JavaScript.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Meta_programming
The most common use of symbols by programmers is for performing language reflection (particularly for callbacks), and most common indirectly is their use to create object linkages.
- Unique
- Immutable
- They are tokens to be used as unique ids
- Symbol-keyed properties will be completely ignored when using JSON.stringify():
var a = String('A')
var b = String('A')
console.log(a === b) // true
var a = Symbol('A')
var b = Symbol('A')
console.log(a === b) // false
var foo = {
[Symbol('bar')]: 123
}
console.log(foo.bar) // undefined
console.log(Object.getOwnPropertySymbols(foo)[0]) // [Symbol(bar)]
console.log(foo[Object.getOwnPropertySymbols(foo)[0]]) // 123
console.log(foo[Reflect.ownKeys(foo)[0]]) // 123
var myPrivateMethod = Symbol();
this[myPrivateMethod] = function() {...};
In computer science, reflection is the ability of a computer program to examine, introspect, and modify its own structure and behavior at runtime.
const foo = {a: 1, b: 2}
const result = Reflect.has(foo, 'a')
console.log(result) // true
const bar = {a: 1, b: 2}
const keys = Reflect.ownKeys(bar)
console.log(keys) // ['a', 'b']
const baz = {a: 1, b: 2}
Reflect.ownKeys(baz).map(key => {
baz[key] = "π"
})
console.log(baz) // {a: "π", b: "π"}
const qux = {a: 1, b: 2}
console.log(qux) // {b: 2}
console.log(Reflect.isExtensible(qux)) // false
Reflect.set(qux, 'c', 3)
console.log(qux) // {b: 2}
Reflect.preventExtensions(qux)
console.log(Reflect.isExtensible(qux)) // false
Reflect.set(qux, 'd', 4)
// Nothing was added!
console.log(qux) // {a: 1, b: 2, c: 3}
// Delete still works!
console.log(Reflect.deleteProperty(qux, 'a')) // true
console.log(qux)
// But seal stops delete too!
Object.seal(qux)
console.log(Reflect.deleteProperty(qux, 'b')) // false
console.log(qux)
[Traps are the] methods that provide property access. This is analogous to the concept of traps in operating systems.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
- get()
- set()
- has()
- ownKeys()
- apply()
- call()
- construct()
- ...etc.
const target = {foo: 'bar'};
const handler = {
set: (target, prop, val) => {
target[prop] = val.replace('π»', 'π')
}
}
const proxy = new Proxy(target, handler);
proxy.foo = 'BOO! π»';
console.log(target.foo);