Try   HackMD

Part 1: 程式基礎,以JavaSript為例

看自己本地環境的node js版本 node --version

 master  node --version   
v14.6.0

不同的javascript引擎,語法跑出來的結果不太相同
不同的js版本,LTS (長期支援 Long-term support)可能不一樣

Production applications should only use Active LTS or Maintenance LTS releases

y = `test` // string interpolation
let x = 10;  // 第一次出現時宣告


// 三種字串宣告方式:單引號、雙引號、反引號
let y = '100'; 
let a = "";

// grave, backtick 這個在js和其他程式語言的功能不一樣 
let b = `test`; 

let z = x + y;
console.log(z);

let userName = "John";
let greeting = "Hello, " + userName + ", how are you?";

// string interpolation
let greeting2 = `Hello, ${userName}, Nice to meet you. how are you?`;

console.log(greeting)
console.log(greeting2)

全域變數 / 區域變數

scope 變數作用域

以前版本的javascript只有:

  • global scope 全域變數
  • function scope 函式內區域變數

ruby 還有 block scope

以Ruby舉例

def foo
  x = 100
end


puts x  # 會出錯
# hello-ruby.rb:5:in `<main>': undefined local variable or method `x' for main:Object (NameError)

拿到x的方法

def foo
  x = 100
  puts x
end

foo() # 100

scope的概念

def foo
  x = 100
  puts x
end

x = 1000
puts x # 1000

以js舉例

let 放在function內(會出錯)

function foo() {
  let x1 = 100;
  // ReferenceError: x1 is not defined at Object.<anonymous> (/Users/tingtinghsu/Documents/projects/astro_js/Day1/hello-foo.js:6:13)

}
foo();
console.log(x1);
let x1 = 100;

function foo() {
  //let x1 = 100;

}
foo();
console.log(x1); //=> 100

如果放在function內但不宣告 => 變成全域變數

function foo() {
  x1 = 100;
}
foo();
console.log(x1); //=> 100

行為良好的程式語言是否該取得i?

for (var i = 0; i < 10; i++) {
}

console.log(i); // => 會印出10

為了不要讓i被取得,使用let
(超過可以使用的範圍再取用時就會噴錯)

for (let i = 0; i < 10; i++) {
}

console.log(i); 


ReferenceError: i is not defined
    at Object.<anonymous> (/Users/tingtinghsu/Documents/projects/astro_js/Day1/for.js:4:13)

let是新的語法
(rails webpacker: 拿到js的新語法,翻譯成大部分瀏覽器可以使用的舊語法)

比起var,用let的好處

function bar(x) {
  for (var j = 0; j < x; j++) {
    // 1. 拿得到 j
  }

  // 2. 拿得到 j
}

// 3. 拿不到 j

function bar(x) {
  for (let j = 0; j < x; j++) {
    // 1. 拿得到 j
  }

  // 2. 拿不到 j
}

// 3. 拿不到 j

變數提升:把變數的宣告提升到檔案的最上層

var userName1 = "Johnny Doe" 
console.log(userName1); // Johnny Doe

兩句交換,後為什麼會印出undefined?

console.log(userName);

var userName = "John Doe" // undefined
console.log(userName);

var userName = "John Doe" // 

//javascript直譯器跑完檔案時,會把上面那句話拆成兩層,
//然後`var userName`會被提升到檔案最上層
var userName
userName = "John Doe"

Function 函式

回傳值returnconsole.log的區別

function greeting(name) {
  // say a friendly hi
  console.log("Hello world! This is" + name)  // 印出Hello world! This istai
  return `name: ${name}` //回傳 name: tai
  }
  let ret = greeting('tai') 
  console.log(ret);

ruby的語法不同,如果沒有return,就不會有回傳值

function greeting1(name) {
  console.log("Hello world! This is" + name)`
}

let result = greeting1("Mary")
console.log(result) // undefined

ruby

def bar(x)
  if(x < 100) {
    return 100
  } else {
    300  # return 300 
  }
end

呼叫函式call, invoke時一定要加括弧 foo()

function greeting2(name) {
  // say a friendly hi
  console.log("Hello world! This is" + name)


  console.log("function ends") // 偵測到無法執行的程式碼

  return  // 自以為有分號,結果變成沒有回傳值
  `name: ${name}`
  
}

let result2 = greeting2("Mary")
console.log(result2) // undefined

method 和 function 有什麼不同?

Ruby: 如果一個function隸屬於一個物件,就是一個method

依照ruby的method規則,以下會出錯

p [1, 2, 3, 4, 5, 6].map def i
  i + 1
end

但是在JS裡,函式是自由的

以下是沒有名字的函式

let res8 = [1, 2, 3, 4, 5].map(function(i){ return i + 1})
console.log(res8) #=> [ 2, 3, 4, 5, 6 ]

把函式提出來 (JS函式的good part)

function add1(i){ return i + 1}

let res8 = [1, 2, 3, 4, 5].map(add1)
console.log(res8)

=====

== 會自動幫忙轉型
比較時,盡量使用嚴格比較===

let numberOne = 1
let stringOne = "1"
console.log(numberOne == stringOne) // true console.log(numberOne === stringOne) // false

(桃紅色的區域會發生很多奇怪的事!)

if-else

計算車資

let cartTotal = 1000;
let ratio;

function getRatio(total) {
  if(total > 1000) {
    return 0.8;
  } else if (total > 500) {
    return 0.9;
  } else {
    return 1;
  }
}

console.log(cartTotal * getRatio(cartTotal))

For迴圈

let array = [1, 2, 3, 4, 5, 6];
let res50 = [0, 0, ...array, 7, 8, 9]
res50[0] = 100;

// 彈性,瑣碎:開頭可以調整,跳的走也可以 eg i +=2
for(var i = 0; i < res50.length; i ++) {
  let result = res50[i] + 1;
  console.log('I got a number: ', result) // => I got a number:  101
}
console.log(i) // => 印出11, 最後的檢查會再做一次,但是發現條件不會符合

console

I got a number:  101
I got a number:  1
I got a number:  2
I got a number:  3
I got a number:  4
I got a number:  5
I got a number:  6
I got a number:  7
I got a number:  8
I got a number:  9
I got a number:  10
11

像ruby版本寫法的迴圈

for(let x of res50){
  console.log(x)  // 走的過程不會去亂動陣列本身
}

case 條件判斷


let gender = 'F', title; 
switch(gender) {
case 'M': 
    title = 'Mr.';
    break; 
case 'F': 
    title = 'Ms.';
    break;
default:
    title = '';
} 

console.log(title);

集合 Collection

  1. []
let array = [1, 2, 3, 4, 5, 6];
let res2 = array;
console.log(res2); // => [ 1, 2, 3, 4, 5, 6 ]

let res5 = array.pop();
console.log(res5); // => 6

let res8 = array.slice(1,4);
console.log(res8); // => [2, 3, 4]

// immutible: 不可被修改的
// push pop slice 這些函式會動到陣列本身
// functional派:不要動到array本身
let res12 = [...array, 7 ,8 ,9]
console.log(res12); // => [[ 1, 2, 3, 4, 5, 7, 8, 9]


  1. {}:鍵值對:像是字典的東西
    值可以重複,鍵不行

在JavaScript叫做Object

Object的兩種用法:

  1. 查表
  2. 表達物件的狀態
let areaCode = { // 查表
  'us': '01',
  'tw': '886',
  'hk': '86',
}

let student = { // 表達物件狀態
  'name': 'John',
  'age': 18,
  'gender': 'M',
  'favorite': ['music', 'eat'],
}

let areaCode = {
  'us': '01',
  'tw': '886',
  'hk': '86'
}

let country = 'hk'
console.log(areaCode[country]) // => 86

取值與設值的規則:
如果是為了拿到特定的結果,使用[],其他時候用.

let areaCode = {
  'us': '01',
  'tw': '886',
  'hk': '86'
}
let student = {
  'name': 'John',
  'age': 18,
  'gender': 'M',
  'favorite': ['music', 'eat']
}
let country = 'hk'
console.log(areaCode[country]) //=> 為了取出`86`這個值
console.log(student.age) 

手動表現出變數提升

function baz(x){

  let amount, total, qty; //先寫出來放著

  //do something
  amount = 100;
}

讓整個函數提升

function baz(x)這種方式定義function

把不重要的小函式藏在底下

baz(1000)

function baz(x){
  var amount, total, qty;

  //do something
  amount = 100;
  console.log(amount + x)
}

另外兩種定義函式的方式

  1. function指定給變數
putInPot('beef') // beef in pot

function putInPot(x) {
  console.log(`${x} in pot`)
}

// function指定給變數

let lalala = function (x) {
  console.log(`${x} in pot`)
}
  1. 箭頭函式 (ES6)
let addOne = function(x) {return x + 1}
let addOne2 = x => x + 1 // 前面是參數,後面是回傳值

// 第一步:把function拿掉
let addOne = (x) => {
  return x + 1
}

// 第二步:把{}和return拿掉
let addOne = (x) =>  x + 1

// 第三步:如果參數只有一個,()可以拿掉
let addOne = x =>  x + 1

如果參數有兩個,不能走第三步(不能把括弧拿掉)

let addOne = (x, y) => x +  y + 1

高階函式

處理函式的函式

原始的寫法長這樣:

putInPot('beef');
putInPot('water');
console.log('I make dinner with beef')

boomboom('chicken')
boomboom('coconut');
console.log('I make dinner with chicken')


function putInPot(x) {
  console.log(`${x} in pot`)
}

function boomboom(x) {
  console.log(`cooking ${x}`)
}

beef in pot
water in pot
I make dinner with beef
cooking chicken
cooking coconut
I make dinner with chicken

把pattern找出來

// cook('第一個食材'、'第二個食材','callback function': 這邊人家會透過參數丟一個函式進來,自己決定什麼時候要呼叫


cook('beef', 'water', putInPot);
cook('chicken', 'coconut', boomboom);

// 高階函式:處理函式的函式
function cook(i1, i2, f) {
  f(i1)
  f(i2)
  console.log(`I make dinner with ${i1}`)
}


cook('beef', 
     'water', 
     x => console.log(`${x} in pot`))

cook('beef', 
     'water', 
     x => console.log(`cooking ${x}`))


// 函數是自由的,console log也是一個function,所以可以提到外面來

let pp = console.log
cook('beef', 
     'water', 
     x => pp(`${x} in pot`))

cook('beef', 
     'water', 
     x => pp(`cooking ${x}`))

下堂課前情提要:
用函式產生函式:呼叫高階函式的時候,直接把函式定義在後面

[1, 2, 3, 4, 5].map(x => x + 1)
[1, 2, 3, 4, 5].select(x => x > 3)
[1, 2, 3, 4, 5].reduce(x, acc => x + acc)

let curriedAdd = x => y => z => x + y + z

eg. 雖然不知道什麼時候會發生,但某個事件觸發的時候,會呼叫後面的函式

<body>
  <button id="btn">Don't Click</button>

  <script>
    document
      .querySelector('#btn')
      .addEventListener('click', ()  => {
        for (let i = 0; i < 5; i++) {
          alert('Hey!!!! WTF')
        }
        alert('OK..')
      })
  </script>
</body>