筆記、advanced JS 整理


Posted by s103071049 on 2021-08-03

1. scope

scope means variable access.

  1. functions have access to any variables in the root scope.
  2. let (es6):every time it's wrapped around a curly bracket, it creates a new scope versus.
  3. const (es6):you can change the property of the object (pass by reference) but you can't reassign the variable

ex1

var a = 'test1'
function test() {
  a = 'test2' // 未宣告,global 有宣告,所以將 global 的值改成 test2
}
test()
console.log(a) // test2
// ES6 以前變數的作用域基本單位是 function,只有 function 可以產生新的作用域
var a = 2;
if (true) {
    var a = 5;
    alert(a); // 5
}
alert(a); // 5
function q3() {
    window.a = "hello";
}

// you must first run q3() in your console to add the a property to the window. then run q32()
function q32() {
    alert(a); //"hello"
}
// you must first run q2() in your console to add the new value. then run q22()
var a = 0;
function q2() {
    a = 5;
}

function q22() {
    alert(a); //5
}

2. es6

destructuring

select the properties you want from the object easier.

const obj = {
  player: 'bobby',
  experience: 100,
  wizardLevel: false
}

const player = obj.player
const experience = obj.experience
let wizardLevel = obj.wizardLevel

// es6 destructuring

const {player, experience} = obj // 解構不要忘記前面的宣告 const 或 let
let {wizardLevel} = obj

new way to declare object properties

in ES6 we can have sth dynamic

const name = '王小明'

const obj = {
  name: 'peter',
  ['江' + '小美']: 'hi'
}

obj-note

if property and value are the same, we can remove the declaration

const a = 'peter'
const b = true
const c = {}
const obj = {
  a,
  b,
  c
}
// 等價 const obj = {a, b, c}

default argument

when sb calls the function and they don't provide these arguments, my function won't fail cause i have default argument.

function greet(name = '請輸入姓名', age = '?') {
  return `hello, ${name} you seem to be ${age - 10}`
}

greet() //瀏覽器上執行 "hello, 請輸入姓名 you seem to be NaN"

symbol

the symbol value is used as an indent fire, mostly for object properties. (如果你要幫一個 object 添加新的屬性,很容易會造成名稱衝突,Symbol 就是用來解決這件事情的)

unique types => no conflict

let sym1 = Symbol()
let sym2 = Symbol('foo')
let sym3 = Symbol('foo')
console.log(sym2 === sym3) // false
// Symbol
// Create a symbol: "This is my first Symbol"
const symbol = Symbol('This is my first Symbol')

3. advanced function

clean mechanism

The variable created inside the function. Including the parameters are local to the function.

every time we run the first function, this block of code gets executed and the great variable gets created every time.

function first() {
  var greet = 'Hi'
  function second() {
    alert(greet)
  }
  return second;
}
var newfunc = first()
newfunc()

// ES6
const first = () => {
  const greet = 'Hi'
  const second = () => {
    alert(greet)
  }
  return second
}
const newfunc = first()
newfunc()

we make sure that if our program, every time we run first remember to greet, well, it will have conflicts because it's a constant and we constantly reassign the same thing to a variable that already exists.

So this way, within a function, we make sure that every time we run it, it's a clean slate. You can name the variables however you like because there's nothing else that will collide with it.

closure

the great variable isn't within the scope of second.

const first = () => {
  const greet = 'Hi'
  const second = () => {
    alert(greet)
  }
  return second
}
const newfunc = first()
newfunc()

// const newfunc = first() 等價
const newfunc = second = () => {
    alert(greet)
  }

What closure's does, and this is a rule in JavaScript is that the child SCOP always has access to the parent scope.

So inside the Web browser, the Web browser says, oh, this second function needs greet. So I'm going to remember it.

closure - a function ran. a function executed. It's never going to execute again. But, it's going to remember that there're references to those variables so the child scope always has the access to the parent scope.

children always have access to their parents cope. But parents scope don't have access to their children.

currying

currying is the process of converting a function that takes multiple arguments into a function that takes them one at a time.

currying means we're changing this function to only accept one parameter at a time.

const multiply = (a, b) => a * b
const curriedMultiply = a => b => a * b

curriedMultiply(3) // 瀏覽器上執行得到 b => a * b,a function inside of a function, so it returns a function.

curriedMultiply(3)(4) // 12

why we need currying ? because its's more extensive.

const multiplyBy5 = curriedMultiply(5)
multiplyBy5(2)
multiplyBy5(10)

compose

Campose is the act of putting two functions together to form a third function where the output of one function is the input of the other.

const compose = (f, g) => a => f(g(a))

const sum = num => num + 1
compose(sum, sum)(5)
  1. f = g = 5,a = 5
  2. (sum, sum) exeucted the first part of the function => return a => f(g(a)) another function => give a = 5 ie f(g(5))
  3. run the inner function g ie f(sum(5)) => due to const sum = num => num + 1 and hence f(6)
  4. f function run => sum(6) => 7

avoiding side effects & functional purity.

side effects are any of these things, any of actions that happen.
Inside of the function that we don't really know anything about, if it interacts or reads or writes to an external variable for example, or console logs, well, that's a side effect.

by avoiding these side effects, we have something called functional purity and functional purity is a concept where we say in order for us to write really, really good programs, we want to avoid.

By avoiding side effects and always returning, we create something that we call deterministic.

Def of determinism, where anything you put into the function, it always returns the same thing.

conclusion

closure, currying and compose is very important in advanced function for JS.

You don't need to know the definition of them. You just need to be able to read a piece of code and understand what's going on.

  1. Deterministic --> always produces the same results given the same inputs
  2. No Side Effects --> It does not depend on any state, or data, change during a program’s execution. It must only depend on its input elements.

4. advanced array

for each (es5)

for each just says I'm going to move over these elements and I'm going to multiply a number by two but we're not changing the array.

We're just randomly multiplying the numbers by two but we're not really storing it anywhere.

const array = [1, 2, 3, 4]
const newArray = array.forEach((num) => {
  num * 2
})

console.log(newArray) // 瀏覽器:undefined

If we want to return a new array we have to create our own array and then push

const array = [1, 2, 3, 4]
const double = []
const newArray = array.forEach((num) => {
  double.push(num * 2)
})

console.log(double) // [2, 4, 6, 8]

map

map transforms the array

the way map works is that you always need to return something because what's different for map than it is for foreach.

for each just loop's over something and it just does whatever the function says versus with the array. We can do what we couldn't with for each which is loop over each element each number and return a new array.

const array = [1, 2, 3, 4]
const mapArray = array.map((num) => {
  return num * 2
})
console.log('map', mapArray) // [2, 4, 6, 8]
  1. every time the array loop => the first one of num is 1 => return 1 times 2 => gets put into map array which is now 2
  2. the next num is 2 => return 2 times 2 => gets put into map array which is now 4
  3. get added to the array 3 times 2
  4. get added to the array 4 times 2

these two seem both doing the same thing. Whenever you want to loop do a simple loop and take some action on something like an array. You want to use map over for each with for each the operation may do nothing.

const array = [1, 2, 3, 4]
const double = []
const newArray = array.forEach((num) => {
  double.push(num * 2)
})

console.log('foreach', double) // [2, 4, 6, 8]

const mapArray = array.map((num) => {
  return num * 2
})

console.log('map', mapArray) // [2, 4, 6, 8]

all for each cares about is to iterate which is going one by one to iterate over a collection of elements like 1 to 3 and 4 and apply whatever operation we tell it to on each element.

Map on the other hand has a restriction on the operation. It expects the operation to return an element. The map iterates again loops through over a collection of elements applying the operation on each element and then finally storing the result of each invocation of the operation. Hence, map allows us to write pure function.

And the other important thing is that we're not changing the array. This array stays the exact same with math because we're always just making a new copy of the array we're never mutating the data.

filter

Now with filter we can say filter array and as the name suggests we can filter our array with a condition.

And as with map this returns a new array so we have to return something because filter array is going to contain that information.

const array = [1, 2, 10, 16]
const filterArray = array.filter(num => {
  return num > 5
})

console.log('filter', filterArray) // [10, 16]
  1. filter this array array = [1, 2, 10, 16] as you're going one by one
  2. num = 1 => return 1 > 5 (false) => we're not adding this into filter array
  3. iterate to 2 => 2 > 5 (false) => it's not going to go into the filter array
  4. 10 > 5 (true) => 10 is going to go into the filter array
  5. 16 > 5 (true) => 10 is going to go into the filter array
    ```js
    const array = [1, 2, 10, 16]
    const filterArray = array.filter(num => {
    return num === 5
    })

console.log('filter', filterArray) // [] because nothing equal to five

All you have to do is return true or false if it returns false. It won't go into the array if it returns true while it will go into the array.

## reduce
Again this returns a new array we save reduce and reduce takes something called an accumulator and the number.

Let me explain accumulator plus number and again because we reduce we're returning an array.

```js
const array = [1, 2, 10, 16]
const reduceArray = array.reduce((accumulator, num) => {
  return accumulator + num
}, 0)

console.log(reduceArray) // 29
  1. num = 1, 2, 10, 16;accumulator is something where we can store the information that happens in the body of the function.
  2. iterate:accumulator + 1 => 假設 accumulator = 0 => 1
  3. when two comes around => accumulator + 2 => 1 + 2 (because the accumulator remembers what was there previously and then)
  4. ten comes around => 3 + 10
  5. sixteen comes around => 13 + 16 => 29

In reduce after the function you have the second parameters. And here we can specify what we want our accumulator to start with the default value.

const array = [1, 2, 10, 16]
const reduceArray = array.reduce((accumulator, num) => {
  return accumulator + num
}, 5)

console.log(reduceArray) // 34

conclusion for map, filter and reduce

They're pure which means every time we do an operation whatever inputs we get in it always returns a value. And there are no side effects.

ex

// Complete the below questions using this array:
const array = [
  {
    username: "john",
    team: "red",
    score: 5,
    items: ["ball", "book", "pen"]
  },
  {
    username: "becky",
    team: "blue",
    score: 10,
    items: ["tape", "backpack", "pen"]
  },
  {
    username: "susy",
    team: "red",
    score: 55,
    items: ["ball", "eraser", "pen"]
  },
  {
    username: "tyson",
    team: "green",
    score: 1,
    items: ["book", "pen"]
  },

];

Q1:Create an array using forEach that has all the usernames with a "!" to each of the usernames

let newArray = []
array.forEach(element => {
  // let username = element.username
  let { username } = element
  username = username + '!'
  newArray.push(username)
})
console.log(newArray) // ["john!", "becky!", "susy!", "tyson!"]

Q2:Create an array using map that has all the usernames with a "? to each of the usernames

const mapArray = array.map(element => {
  let { username } = element // let username = element.username
  return username + '?'
})
console.log(mapArray)

Q3:Filter the array to only include users who are on team: red

const result = array.filter(element => {
  return element.team === 'red'
})
console.log(result)

===
結果:
0: {username: "john", team: "red", score: 5, items: Array(3)}
1: {username: "susy", team: "red", score: 55, items: Array(3)}
length: 2
[[Prototype]]: Array(0)

Q4:Find out the total score of all users using reduce

const totalScore = array.reduce((accumulator, user) => {
  return user.score + accumulator
}, 0)
console.log(totalScore)

Q5:
(1), what is the value of i? i is index of the array
(2), Make this map function pure: => remove console.log and alert

const arrayNum = [1, 2, 4, 5, 8, 9];
const newArray = arrayNum.map((num, i) => {
    console.log(num, i);
    alert(num);
    return num * 2;
})

BONUS: create a new list with all user information, but add "!" to the end of each items they own.

let newList = array.map(user => {
  user.items = user.items.map(item => {
    return item + '!'
  })
  return user
})
console.log(newList)

5. advanced object

three things important with object:(1) reference type (2) context
(3) instantiation

reference type

Objects are what's called the 'reference types' in Javascript.

'numbers', 'null','undefined', 'booleans', 'strings' even 'symbols' they're all defined by the programming language. So Javascript, the person who wrote javascript and ECMAScript the standard says these are primitive types.

Now a 'reference type' which is a non primitive type are not defined by the programming languages. What that means is that they're created by the programmer.

So let's look over here as an example. When I say variable. Number one equals one. Javascript knows what one is. It's a number. It's a primitive type. It's always the same thing. It's immutable every time it uses the one that's the same one that javascript knows about.

When I do variable number two, yah it's using the same number one that javascript knows about.

There are 'reference types'. So what we said was I want 'object1' create a new object, will say this box over here this filing cabinet and in it, put 'value 10' into this box.

'object2' says, I want you to just reference and that's where the
word comes from, reference 'object1', saying hey! I want whatever's inside this box.

When 'object3' gets created, it's a new object because we have the new brackets. And it's saying put 'value 10' in this box.

context

context get confused a lot with scope.

'scope' is created when it sees curly brackets. 'context' tells you, where we are within the object.

So what 'this' means, is what is the object environment that we're in right now, the best way to think about it is, what is to the left of the dot ? (以下面代碼為例,window.)

在瀏覽器上執行 => console.log(this) this is a window object

console.log(this) // {window: Window, self: Window, document: document, name: "", location: Location, …}

console.log(this === window) // true

this.alert('hi')

So right now, I'm able to do 'alert' because I'm inside the 'window object'. So doing 'window' or doing 'this' is the same thing.

'this' just refers to what object it's inside of.

ex1

Still the window object 'this' still refers to the function. It has to be what object we're inside of right now technically function 'a'.

function a() {
  console.log(this)
}

a() // window
window.a() // can use this way to run the function, to the left of the dot is window

ex2

Now this is the object, which is 'object4'.

const object4 = {
  a: function() {
    console.log(this)
  }
}
object4.a() // {a: ƒ}

'this' is really important when we do 'instantiation', 'instantiation' is when you make a copy of an object and reuse the code.

instantiation

Instantiation means that you are making instances or multiple copies of an object.

ES6 class

class Player {
  constructor(name, type) {
    console.log(this)
    this.name = name
    this.type = type
  }
  introduce() {
    console.log(`Hi, I'm ${this.name}, I'm ${this.type}`)
  }
}

class Wizard extends Player {
  constructor(name, type) {
    super(name, type)
  }
  play() {
    console.log(`WEEEEE I'm a ${this.type}`)
  }
}

const wizard1 = new Wizard('amy', 'healer')
const wizard2 = new Wizard('peter', 'dark magic')
  1. capitalize a class

  2. think of a 'class' as something that I want to make a copy of.

  3. within the class we have constructor -- taking below codes as ex:Every time I'm making a copy of a 'Player' the first thing that gets run is the 'constructor' function and this 'constructor' function is gonna create these properties on the 'Player' object. So 'name' and a 'type'.

  4. create anything that I want for the 'Player'. -- ex: create a method such as 'introduce()'

  5. the reason we use 'this' in constructor is that when we create a 'Player' we can access the name and type property. In order to access the 'Player' and make copies of it. You'll have to run a 'constructor' which is all the properties and methods that you want the 'Player' to have.

  6. we want to now copy this but add onto it. => extends => ex -- I'm saying, I want 'wizard' to extend whatever 'Player' has. Again, because it's a 'class', I have to do a 'constructor'. We have to do something called 'super' with the properties that we want to pass to the 'constructor'. In this case, 'name' and 'type'.

  7. console.log gives us Wizard {}

class Player {
  constructor(name, type) {
    console.log('Player', this) // Wizard {}
    this.name = name
    this.type = type
  }
  introduce() {
    console.log(`Hi, I'm ${this.name}, I'm ${this.type}`)
  }
}

class Wizard extends Player {
  constructor(name, type) {
    super(name, type)
    console.log('Wizard', this) // Wizard {name: "amy", type: "healer"}
  }
  play() {
    console.log(`WEEEEE I'm a ${this.type}`)
  }
}

const wizard1 = new Wizard('amy', 'healer')

before ES6 -- classical inheritance

var Player = function(name, type) {
  this.name = name
  this.type = type
}
Player.prototype.introduce = function() {
  console.log(`I'm ${this.name}, I'm a ${this.type}`)
}
var wizard1 = new Player('shelly', 'wind magic')

wizard1.play = function () {
  console.log(`WEEEEE I'm a ${this.type}`)
}

ex

create two classes: an Animal class and a Mamal class.

create a cow that accepts a name, type and color and has a sound method that moo's her name, type and color.

class Animal {
  constructor(name, type, color) {
    this.name = name,
    this.type = type,
    this.color = color
  }
}

class Mamal extends Animal {
  constructor {
    super(name, type, color)
  }
  sound() {
    console.log(`Moooo I'm ${this.name},  my type is ${this.type} and my color is ${this.color} `)
  }
}

const cow = new Mamal('anna', 'cow', 'white')

6. by value VS by reference

Primitive types are immutable. we can't really change them in order to change them We need to completely remove the primitive type.

copy by value

  • 舉例、變數 A = 5,若想改變變數 A 的值,我必須將它從記憶體移除然後再創造一個新的,例如:A = 10
  • 定義:pass by value simply means we copy the value and we create that value somewhere else in memory.
var a = 5 // 0x01
var b = 10 // 0x02
var b = a // 0x03 copy the value and put it into the new memory space

pass by reference

objects in javascript are stored in memory and are passed by reference.

var c = [1, 2, 3, 4]
var d = c
d.push(1111111)

console.log(c) // [ 1, 2, 3, 4, 1111111 ]
console.log(d) // [ 1, 2, 3, 4, 1111111 ]
  • 優點:by just having one object here we're saving space and memory. We're not copying and cloning the object creating multiple version. We can simply save memory reference just one location instead of just loading up our memory heap.(節省記憶體空間)
  • 缺點:we might have this issue where by mistake somebody else changes a property on that referenced object.

ex1-clone the array

maybe we do want to own an object copy it so that we don't modify it like this. (用 array 實作)

var c = [1, 2, 3, 4]
var d = [].concat(c) // which pushes whatever I have in C into this empty array
console.log(c) // [ 1, 2, 3, 4]
console.log(d) // [ 1, 2, 3, 4, 1111111 ]

ex2-clone the object

using Object.assign

let obj = {a: 'a'}
let clone = Object.assign({}, obj)
obj.a = 5
console.log(clone) // { a: 'a' }

using spread

let obj = {a: 'a'}
let clone = Object.assign({}, obj)
let clone2 = {...obj}
obj.a = 5
console.log(clone) // { a: 'a' }
console.log(clone2) // { a: 'a' }

as previous

let obj = {a: 'a', b: {deep: 'try and copy me'}}
let clone = Object.assign({}, obj)
let clone2 = {...obj}
obj.a = 5
console.log(obj) // { a: 5, b: { deep: 'try and copy me' } }
console.log(clone) // { a: 'a', b: { deep: 'try and copy me' } }
console.log(clone2) // { a: 'a', b: { deep: 'try and copy me' } }

each object get passed by reference

  1. obj : 0x01
  2. obj 裡面的 b : 0x02

this is shallow cloning where we can only clone the first layer.

// shallow cloning
let obj = {a: 'a', b: {deep: 'try and copy me'}}
let clone = Object.assign({}, obj)
let clone2 = {...obj}
obj.b.deep = 'xxx'
console.log(obj) // { a: 5, b: { deep: 'xxx' } }
console.log(clone) // { a: 5, b: { deep: 'xxx' } }
console.log(clone2) // { a: 5, b: { deep: 'xxx' } }

doing deep clone using JSON

JSON.stringify turn things into string after that we parse it and turn the string back into a object

會有效能損失,如果你要考貝的物件超大,所以其實這樣的方法並不是很常用的。

// deep clone
let obj = {a: 'a', b: {deep: 'try and copy me'}}
let clone = Object.assign({}, obj)
let clone2 = {...obj}
let superClone = JSON.parse(JSON.stringify(obj)) // 會有效能損失

obj.b.deep = 'xxx'

console.log(obj) // { a: 5, b: { deep: 'xxx' } }
console.log(clone) // { a: 5, b: { deep: 'xxx' } }
console.log(clone2) // { a: 5, b: { deep: 'xxx' } }

console.log(superClone) // { a: 'a', b: { deep: 'try and copy me' } }

7. type coercion

type caution means that when the appearance that is the things to the left and to the right of the operator are different types ,one of them will be converted into an equivalent value by the JavaScript engine.

  • 定義:the language converting a certain type to another type.
  • 說明 ==:compare the two values and if they have different types try to well coerce one into the same type.
  • 說明 ===:compare to values but don't try to coerce one into the same type.
  • 結論:always use ===
  • it can happen in if statement:turn 1 into true and turn 0 into false
  • Object.is() 大多數情況同 ===,但可以判別的比 === 更準。
1 == '1' // true since '1' turns to 1

1 === '1' // false

if (1) {
  console.log(5) // 5
}

+0 === -0 // true 但他們其實是不同的東西
Object.is(+0, -0) // false

NAN === NAN // false 但他們是相同的東西
Object.is(NAN, NAN) // true

do all languages have type caution ?

yes they do. we always need to convert types between programs to do things in memory. So there is some sort of type caution at different levels of the stack.

It just so happens that JavaScript has an especially heavy type cause in nature to it because it's dynamically type.

it can happen in if statement










Related Posts

1/27

1/27

給自己看的 JS 進階-變數

給自己看的 JS 進階-變數

資料夾結構

資料夾結構


Comments