# 0413 變數及函式宣告與範圍、Ruby 續 0412
###### tags: `JavaScript` `Ruby / Rails`
- MDN (類似 JS 官方手冊) [連結位置](https://developer.mozilla.org/en-US/)
## 印個 Hello World (其實是123)
在終端機中輸入 `$ node -v` 檢查 node 版本
- node 是啥?
- 一個 Runtime (執行環境)
- 其實各個瀏覽器也都是 Runtime
- `console.log()` 會印出結果但是沒有回傳值
- ==在 JS 中,要在最後面加分號就全部加,不然就全部不加==
```shell=
$ code ttt.js # 建立一個 ttt.js 檔案然後開啟 VScode 編輯器
```
```javascript=
// 在檔案中輸入
console.log('123');
// 存檔
```
回到終端機
```shell=
$ node ttt.js # 用 node 當 runtime 執行 ttt.js
## 得到 123
```
## JS 宣告變數與範圍
### var 變數宣告
- 以==一個 function==為存活範圍
- 可重複宣告
```javascript=
x = 2;
console.log(x);
// 不要這樣做變數指定,這樣寫會汙染全域變數
// 在 ruby 裡用下面的得到相同結果
a = 2
puts a
```
```javascript=
var a = 1; // 宣告 a 變數為 1
console.log(a);
```
- Execution Context 執行環境
- 只要有某個方法執行就會產生進入一個執行環境
- 方法完成執行之後那個環境就會消失,包含裡面做的所有事情
```javascript=
var a = 2; // 在整個程式的執行環境宣告變數
function hello() {
var b = 1 ; // 在方法中宣告變數
console.log(b); // 在function裡執行才能印出
}
hello(); // 執行方法必須加()
console.log(a); // 得到 2
console.log(b); // 壞掉 (因為沒有進入 hello 方法的小泡泡裡面)
console.log(hello); // 得到一個 hello function
```
- 在 JS 裡面,要呼叫方法就==一定要在後面加小括號==
### Scope Chain 範圍鏈
```javascript=
a = 2; // 指定 a = 2 在全域物件裡面直接插旗指定
var c = 3; // 在執行環境大泡泡中宣告變數
function hello() {
var b = 1; // 在方法中宣告變數
console.log(b); // 在 function 裡執行才能印出
console.log(a); // 在 function 裡找不到會往外找
console.log(c); // 在 function 裡找不到會往外找
}
hello();
// 印出
// 1
// 2
// 3
console.log(a); // 印出 2
console.log(b) // 出錯
console.log(c) // 印出 3
```
- 比較在 ruby 中的變數範圍
```ruby=
a = 1
def b
p a
end
b # 執行後會出錯,因為 ruby 方法中找不到變數就會放棄
```
- 全域變數指定
```javascript=
function hello() {
b = 2; // 呼叫 hello 方法的時候指定下去
}
hello();
console.log(b); // 得到 2
```
- 在 function 內已宣告變數,接著指定新變數
```javascript=
var a = 1;
function hello() {
var a = 3;
a = 2; // 先找 function 裡面的,找不到才往外找指定的變數
console.log(a);
}
hello(); // 得到 2
console.log(a); // 得到 1
```
- function 裡有 function
```javascript=
var a = 1;
function hello() {
var a = 2;
function world(){
var a = 3;
console.log(a);
}
}
hello(); // 沒印出東西,單純宣告變數就結束
world(); // 錯誤,因為world()被包在hello()裡面,所以呼叫不到
```
- function 裡有 function
- 在 function 裡執行 function 裡的 function
```javascript=
var a = 1;
function hello() {
var a = 2;
function world(){
var a = 3;
console.log(a);
}
world();
}
hello(); // 印出 3
```
- function 裡有 function
```javascript=
var a = 1;
function hello() {
var a = 2;
function world(){
var a = 3;
}
world();
console.log(a);
}
hello(); // 印出 2
```
- function 裡有 function
```javascript=
function hello() {
var a = 2;
function world(){
console.log(a);
}
world();
}
hello(); // 印出 2 ,往外找
console.log(a); // 出錯
```
- function 裡有 function
```javascript=
function hello() {
var a = 2;
function world(){
console.log(a);
}
world();
}
console.log(a) // 出錯,因為 function hello 還沒被執行
hello();
```
### 變數提升 Variable Hoisting
```javascript=
console.log(a);
var a = 1;
// 得到 undefined,未定義(undefined 也是一個值)(不會出錯)
```
```javascript=
console.log(a);
// 出錯,得到錯誤訊息 a is not defined
```
```javascript=
var a;
console.log(a);
a = 2;
console.log(a);
// 印出
// undefined
// 2
```
---
### JS 的定義期與執行期
( 編譯期&直譯期、建立期 Creation Phase & 執行期 Execution Phase )
- JS 啟動後
- 先進入定義期去找所有指定的變數與 function code,看需要什麼資源
- (1A) 註冊名稱、(1B) 初始化
- 再進入執行期由上往下執行原始碼
- (2) 賦值 / 執行函式
- 用 var 宣告變數時會執行 1A 跟 1B
```javascript=
if (false) { // 判斷式不是 function,只是原始碼中的其中一段
var a = 1 判斷式 or 判斷是不是?
}
console.log(a) // 得到 undefined
```
### 超連結
* [var MDN解說](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/var)
* [認識 JS (MDN)](https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/First_steps)
---
### let 變數宣告
- let 不做 1B 而是暫時讓這個變數不能使用,TDZ 的狀況,就會使這個值不能使用,
- TDZ 暫時死區(Temporal Dead Zone)
- 作用範圍為==一個 block==
* 如果要讓 TDZ 被解除,就是給他一個值
### const 常數宣告
- 定義之後==不能==修改
- 常數也不做 1B
- 作用範圍為==一個 block==
```javascript=
const A = 1 // A 就是常數(通常會大寫,但不一定要大寫)
```
- 正常情況(做了 1A,然後在第 2 階段執行指定變數)
```javascript=
let a = 1
console.log(a)
// 1
```
- 因為沒有做 1B (初始化) 所以會出錯,但是在 1A 已經預留記憶體空間
```javascript=
console.log(a)
let a = 1
// ReferenceError: Cannot access 'a' before initialization
// 出錯,錯誤訊息如上示
```
---
## 函式 function
### 函式宣告
* 建立期:同時做完 1A 1B ( 定義 ) 2 ( 賦值 )
* 執行期:可被完整使用( 呼叫 )
* 在建立期也會為變數決定存活區間
* 匿名函式==不能==單獨存在
* 在 JS 裡面,要呼叫方法就==一定要在後面加小括號==
#### 普通函式宣告
```javascript=
function a() {
console.log('aaa');
}
a();
// 印出 'aaa'
```
- 因為已經完成所有程序,所以可被完整呼叫
```javascript=
a();
function a() {
console.log('aaa');
}
// 印出'aaa'
```
#### 宣告變數指向==匿名==函式 (anonymous function)
- 變數只會執行
- 1A 跟 1B (var)
- 1A (let, const)
```javascript=
var a = function () { // 在 1A 1B 執行,但是還沒執行 2
console.log('aaa');
}
a();
// 'aaa'
```
- 因為只有執行第一階段
```javascript=
a(); // 因為還沒執行 2 所以 a 是 undefined
var a = function () { // 在 1A 1B 執行,但是還沒執行 2
console.log('aaa');
}
// 錯誤,a is not a function
// 類似
var a
a(); // 因為還沒執行 2 所以 a 是 undefined
var a = function () { // 在 1A 1B 執行,但是還沒執行 2
console.log('aaa');
}
```
- 箭頭函式 Arrow function
- 沒有自己的 this
```javascript=
var c = (x) => {
console.log('ccc')
}
```
---
### JS 函式回傳值
- JS 不是預設最後一行就有回傳值,所以要記得 ==`return`== !!!!!
- 建立函式的 (?)
```javascript=
function f_to_c(temp){
return (temp - 32)*5/9;
}
console.log(f_to_c(20));
```
```javascript=
function a(tmp){
return console.log(tmp);
}
a(); // undefined JS 沒有預設參數也不會出錯
a(1); // 1
a(1, 2, 3); // 1,多餘的參數會省略
```
```javascript=
// 因為 JS 的四捨五入只能讓數字四捨五入到整數,所以自己建一個函式操作
function my_round(num, n = 2) {
return Math.round(num * (10 ** n)) / (10 ** n); // 10 的 n 次方
}
function f_to_c(temp) {
return my_round((temp - 32) / 1.8, 4);
}
console.log(f_to_c(120));
```
### 變數存活範圍
- let, const 作用以 block 為範圍,離開範圍就無效
- block: 大括號裡 (if, function....)
- var 以 function 為範圍
```javascript=
function x() {
if (true) {
var a = 1;
console.log(a);
}
}
```
## JS 中的 this
### 全域變數
- 在 chrome 瀏覽器中為 window
- 在 node.js 中為 global
```javascript=
console.log(this) // this 指向全域物件
```
- 誰呼叫的,誰就是 this,動態執行的時候才會決定
```javascript=
function x() {
console.log(this)
}
x() // 隱藏了一個全域物件 global.x() 或是 window.x()
```
- 呼叫時沒有開頭的就當作全域物件呼叫
- 箭頭函式沒有自己的 this
==定義不是執行~~~~~==
[超連結](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this)
## JS 中的物件
- 包在大括號裡面的 key: value 組合
```javascript=
let obj = {
name: 'aaa',
age: 123,
hello: function() {
console.log(this)
}
}
obj.name
obj.hello()
```
```javascript=
let hero = {
name: '一護',
hp: 100,
power: 5,
healing: function(n = 5){
this.hp += n
if (this.hp >= 100) { // 這邊的 this 等於物件本身
this.hp = 100
}
},
attack: function() {
console.log('attack!!!!!')
this.hp -= 5
}
}
hero.attack()
hero.attack()
hero.healing()
// 印出
// name: '一護',
// hp: 95,
```
- 因為箭頭函式沒有 this,所以下面的程式碼中 this 會找不到東西而指向全域變數
```javascript=
let hero = {
name: '一護',
hp: 100,
power: 5,
healing: (n = 5) => {
console.log(this.hp) // this 指向全域變數
}
}
hero.healing(20)
```
### 用 JS 監控式改變 HTML 原始碼
- HTML 原始碼
```htmlembedded=
<div id="clickme">
按我
</div>
```
- JS 原始碼
```javascript=
var d = document.querySelector('#clickme') // 利用類似 CSS 選取器的方法選取元素
d.addEventListener("click", function(){
this.innerHTML = "被按了"
})
// 建立監控動作 "Click" 及 Rollback function(修改文字)
// 得到效果是如果點擊到 "按我" 上面,文字會變成 "被按了"
```
---
## 續 0412 使用者登入介面
複習:模組跟類別是很像的,但是模組不能 new 不能繼承
類別方法中只能用類別方法
實體方法中只能用實體方法
兩者不能混用
模組方法就是類別方法,等級一樣
拉出來建立模組(感覺像是獨立的工具),裡面放類別方法就可以讓類別方法使用
- 是類別方法還是單純的方法而已啊?=> 如果在模組裡面定義單純的方法 => 看起來單純但實際上是實體方法
建立在外面的模組(或類別)內有模組(或類別)方法的話,就可在任何地方呼叫使用
```ruby=
將下面這個地方換掉
=begin
private
def encrypt(password)
Digest::SHA1.hexdigest(password)
end
def salted(password)
"123#{password}xx"
end
=end
module Hasher # 因為沒有想把它 new 出來,所以直接使用 module
require 'digest'
def self.encrypt(password)
Digest::SHA1.hexdigest(password)
end
def self.salted(password)
"123#{password}xx"
end
=begin
def self.salted(password, salt1, salt2)
"123#{password}xx"
end
=end
end
就可以接著在下面的類別中使用
class User < ApplicationRecord
def self.login(params)
email = params[:email]
password = params[:password]
salted_password = Hasher.salted(password)
encrypted_password = Hasher.encrypted(salted_password)
find_by(email: email, password: encrypted_password)
end
```
```ruby=
class Cat
def self.new
end
def self.a
Cat.new.b 或 self.new.b 或 new.b
end
def b
end
end
module
```
模組方法使用
```ruby=
module DD
module CC
def self.EE
end
end
end
Digest::SHA1.hexdigest(password)
```
## 環境變數建立與使用
較敏感的資訊會放在環境變數內
`
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
ENV.fetch
`
```shell=
$ CCC=123 rails c
```
部署系統的時候設定環境變數
### super 用法
```ruby=
class Animal
def a
puts "a in animal"
end
end
class Cat < Animal
def a
super
end
end
kitty = Cat.new
kitty.a
```
在方法中間插入 super => 呼叫上層的同名方法
如果後面沒有加小括號,代表把得到的引數全部往上丟,如果上層方法要的引數數量不同的話就會出錯
後面有加小括號,代表指定引數
有些程式語言支援 function overload (只要方法在特徵上不同就視為不同,不管他們名稱是否一樣)
```ruby=
class Animal
def a(x, y, x)
puts 'a in animal'
end
class Cat < Animal
def a(c, d)
super(c, d, '123')
end
end
```