2021年2月25日 星期四

jQuery trggie 傳參數

$('#test').on('change', function (event, a1, a2) {
    console.log(event); //The event object is always passed as the first parameter to an event handler

    console.log(a1); //aaa
    console.log(a2); //bbb
});

//第二個列陣參數,可用來傳其它參數
$('#test').trigger('change', ['aaa', 'bbb']);


參考:
https://api.jquery.com/trigger/
https://stackoverflow.com/questions/16401538/passing-parameters-on-jquery-trigger




2021年1月23日 星期六

Go 語言筆記

  1. 空白標識符、匿名佔位符(blank identifier、anonymous placeholder),底線「_」
    佔位置,不用對不須要的資料多設一個變數
    例1:
    func test() (string, string) {
        return "X", "Y"
    }
    func main() {
        var a, _ = test()
        fmt.Println(a) //X
    }
    

    例2:
    arr := [3]int{10, 20, 30}
    sum := 0
    for _, v := range arr {
        sum += v
    }
    fmt.Println(sum)//60

  2.  不定數量參數,「...」
    例1:陣列元素不定數量
    arr := [...]string{"A", "B", "C"}
    fmt.Println(len(arr))//3

    例2:function參數不定數量
    func test(args ...string) {
        fmt.Println(args) //[A B C]
    }
    func main() {
        test("A", "B", "C")
    }

    例3:slice打散
    sliceA := []string{"A", "B", "C"}
    sliceB := []string{"D", "E"}
    sliceC := append(sliceA, sliceB...)
    fmt.Println(sliceC)//[A B C D E]
  3. [...]string{"A", "B", "C"} 和 []string{"A", "B", "C"} 的差異
    [...]string{"A", "B", "C"} 產生的是陣列 array
    []string{"A", "B", "C"} 產生的是切片 slice  (背後產生一個隱性陣列)
    參考:Confusion with “…” operator in golang
    https://stackoverflow.com/questions/28692410/confusion-with-operator-in-golang
  4. 沒有 while,用 for 實現
    例1:
    i := 0
    for i < 10 {
        i++
    }

    例2:無窮迴圈
    for {
        //break
    }
  5. 沒有 class,用結構(struct)實現。
    沒有繼承,用嵌入組合、介面解決,避免繼承產生的維護難度(多用合成/聚合,少用繼承)。
  6. struct指標語法糖。
    根據struct接受的參數型態,自動進行取址(reference &)或取值。(dereference *)進行操作
    例1:指標自動轉換取值操作
    type aa struct {
        m int
    }
    func main() {
        var ptr = &aa{10}
        fmt.Println(ptr) //&{10}
        ptr.m = 20       //自動等同 (*ptr).m=10
        fmt.Println(ptr) //&{20}
    }

    例2:指標自動轉換取址操作
    type aa struct {
        m int
    }
    func (x *aa) add(v int) { //aa須為指標
        x.m = x.m + v
    }
    func main() {
        var p = aa{10}
        fmt.Println(p) //{10}
        p.add(20)      //自動等同 (&p).add(20)
        fmt.Println(p) //{30}
    }

    例3:指標自動轉換取值操作
    type aa struct {
        m int
    }
    func (x aa) add(v int) { //aa須為值
        x.m = x.m + v
        fmt.Println(x) //{30}
    }
    func main() {
        var p = &aa{10}
        fmt.Println(p) //&{10}
        p.add(20)      //自動等同 (*p).add(20)
        fmt.Println(p) //&{10} 傳值過去,所以原本的變數仍為10
    }
  7. errors
    errors.New()
    fmt.Printf("%#v\n", errors.New("test err"))
    //&errors.errorString{s:"test err"}

    fmt.Errorf()
    fmt.Printf("%#v\n", fmt.Errorf("test err %d", 456))
    //&errors.errorString{s:"test err 456"}
    
    //使用 %w 包裹(嵌套)錯誤
    fmt.Printf("%#v\n", fmt.Errorf("test err2 %w", errors.New("test err")))
    //&fmt.wrapError{msg:"test err2 test err", err:(*errors.errorString)(0xc000032e70)}

    errors.Unwrap() 解開包裹的錯誤,取得裡層的錯誤
    fmt.Printf("%#v\n", errors.Unwrap(fmt.Errorf("test err2 %w", errors.New("test err"))))
    //&errors.errorString{s:"test err"}

    errors.Is(errA, errB) bool
    判斷 errA 是否為 errB 的錯誤
    如果 errA 是包裹的錯誤,會一層一層解開,跟 errB 比對,任一層相符即為true
    var errB = errors.New("test err")
    var errWrap1 = fmt.Errorf("w1 %w", errB)     //包1層
    var errWrap2 = fmt.Errorf("w2 %w", errWrap1) //包2層
    
    fmt.Printf("%#v\n", errB)     //&main.myErr{msg:"test err"}
    fmt.Printf("%#v\n", errWrap1) //&fmt.wrapError{msg:"w1 test err", err:(*errors.errorString)(0xc000032210)}
    fmt.Printf("%#v\n", errWrap2) //&fmt.wrapError{msg:"w2 w1 test err", err:(*fmt.wrapError)(0xc000004500)}
    
    fmt.Printf("%t\n", errors.Is(errB, errWrap1))     //false
    fmt.Printf("%t\n", errors.Is(errWrap1, errWrap2)) //false
    fmt.Printf("%t\n", errors.Is(errWrap1, errB))     //true
    fmt.Printf("%t\n", errors.Is(errWrap2, errB))     //true, errWrap2是包裹的錯誤,會一層一層解開比較
    fmt.Printf("%t\n", errors.Is(errWrap2, errWrap1)) //true

    errors.As(errA, targetB interface{}) bool
    判斷 errA 是否為 targetB 類型的錯誤,如果 errA 是包裹的錯誤,會一層一層解開,跟 targetB 比對
    將第一個比對符合的錯誤,指派到 targetB,並回傳 true
    type myErr struct {
        msg string
    }
    
    func (e *myErr) Error() string {
        return e.msg
    }
    func main() {
        var targetErr *myErr
        errWrap1 := fmt.Errorf("w1 %w", &myErr{msg: "test err"}) //包1層
        var errWrap2 = fmt.Errorf("w2 %w", errWrap1)             //包2層
    
        fmt.Printf("%#v\n", targetErr) //(*main.myErr)(nil)
        fmt.Printf("%#v\n", errWrap1)  //&mt.wrapError{msg:"w1 test err", err:(*main.myErr)(0xc0000321f0)}
        fmt.Printf("%#v\n", errWrap2)  //&fmt.wrapError{msg:"w2 w1 test err", err:(*fmt.wrapError)(0xc0000044a0)}
    
        fmt.Printf("%t\n", errors.As(errWrap1, &targetErr)) //true
        fmt.Printf("%#v\n", targetErr)                      //&main.myErr{msg:"test err"}
    
        fmt.Printf("%t\n", errors.As(errWrap2, &targetErr)) //true
        fmt.Printf("%#v\n", targetErr)                      //&main.myErr{msg:"test err"}
    }
  8. defer 延遲執行,採LIFO後進先出的順序(Last In, First out)
    通常可使用在解鎖互斥鎖,或關閉文件defer f.Close()避免最後忘記關閉原先開啟的文件。
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    //結果
    3
    2
    1
  9. panic() 讓程式失敗中斷,類似其他語言的 throw exception
    可使用在巢狀深層呼叫,發生錯誤時,便於向外傳播
    panic("發生錯誤")
    fmt.Println("hello,world") //不會執行到這裡,程式在前一行已崩潰停止
    //執行結果
    panic: 發生錯誤
    goroutine 1 [running]:
    main.main()
    	d:/Go/test/test.go:29 +0x45
    Process exiting with code: 0
  10. recover(),recover 搭配 defer 可捕捉 panic 產生的錯誤,避免程式中斷崩潰停止
    recover() 回傳值為傳給 panic() 的值,但(所以)在以下情況會是 nil
    A.panic's argument was nil;
    B.the goroutine is not panicking;
    C.recover was not called directly by a deferred function.

    例1:捕捉錯誤,避免系統中斷崩潰停止,但後續程式仍不會執行
    defer func() {
        log.Println("AA")
        err := recover()
        fmt.Println(err)
        log.Println("BB")
    }()
    panic("發生錯誤")
    fmt.Println("hello,world") //雖然程式不會崩潰停止,但依然不會執行到這裡
    //執行結果
    2021/01/18 23:23:29 AA
    2021/01/18 23:23:29 BB
    發生錯誤
    Process exiting with code: 0

    例2:捕捉錯誤,避免系統中斷崩潰停止,後續程式仍可執行
    參考 https://golang.org/ref/spec#Handling_panics
    Handling panics
    //定義一個 protect() 函式,可傳入另一個可能發生panic的函式執行,
    func protect(g func()) {
        defer func() {
            log.Println("done") // Println executes normally even if there is a panic
            if x := recover(); x != nil {
                log.Printf("run time panic: %v", x)
            }
        }()
        log.Println("start")
        g()
    }
    
    func main() {
        protect(func() {
            panic("發生錯誤")
        })
        log.Println("hello,world")
        
        //執行結果
        2021/01/18 23:41:03 start
        2021/01/18 23:41:03 done
        2021/01/18 23:41:03 run time panic: 發生錯誤
        2021/01/18 23:41:03 hello,world
        Process exiting with code: 0
    }
  11. 疑問:log.Printf()、fmt.Println() 穿插寫,每次執行輸出的順序可能不一樣?
    log.Printf("A")
    fmt.Println("B")
    log.Printf("C")
    
    //執行結果1
    2021/01/15 23:38:59 A
    B
    2021/01/15 23:38:59 C
    Process exiting with code: 0
    
    //執行結果2
    2021/01/15 23:38:36 A
    2021/01/15 23:38:36 C
    B
    Process exiting with code: 0
  12. goroutine 併發
    語法:
    go funciotn或method
  13. channel 通道
    多個goroutine併發,之間溝通的管道

    無緩衝通道,Ex:make(chan int)
    有緩衝通道,Ex:make(chan int, 緩衝大小)
  14. 通道 deadlock 死鎖
    • 從沒資料的通道中,接收資料
      ch := make(chan int, 1)
      //ch <- 5
      A := <-ch //
      log.Print(A)
      
    • 空的 select
      select {}
    • 無緩衝通道(或有緩衝通道滿了之後),再發資料給此通道,發送、接收須用goroutine併發分開處理,否則會發生deadlock死鎖

      Ex1:未超出緩衝,不用另一個goroutine接收
      ch := make(chan int, 1) //1個緩衝區
      ch <- 5 //剛好用完緩衝區
      A := <-ch 
      log.Print(A) //A
      Ex2:緩衝滿了之後,發送和接收,須用goroutine併發處理
      ch := make(chan int, 1) //1個緩衝區
      
      ch <- 5 //剛好用完緩衝區
      go func() {
          ch <- 6 //緩衝用完,須用goroutine併發處理
          ch <- 7
      }()
      
      A := <-ch
      log.Print(A) //5
      A = <-ch
      log.Print(A) //6
      A = <-ch
      log.Print(A) //7
    • select 若沒 default, case敘述句跟沒資料的通道取資料、或發送資料給無緩衝區的通道,會出現deadlock死鎖
      ch := make(chan int, 1)
      //ch <- 5
      select {
      case <-ch:
      default:
          log.Print("避免deaklock")
      }
  15. select 通道中的有同時多個 case 條件都符合時,是隨機執行的
    ch := make(chan int, 1)
    //ch <- 5
    select {
    case ch <- 1:
    case ch <- 2:
    case ch <- 3:
    default:
        log.Print("避免deaklock")
    }
    A := <-ch
    log.Print(A) //1、2、3 隨機出現
  16. sync.Mutex 加鎖,避免併發競爭問題
    競爭檢測分析
    go run -race ./main.go
    加入互斥鎖
    var mux sync.Mutex
    mux.Lock()
    defer mux.Unlock()
    唯讀鎖定
    var rwMux sync.RWMutex
    rwMux.Lock()
    defer rwMux.Unlock()
    
    rwMux.RLock() //唯讀鎖定
    defer rwMux.RUnlock()
  17. sync.WaitGroup,等待所有任務完成,可避免併發尚未執行完畢,主程式已結束
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        ....
    }()
    wg.Wait()
  18. reflect 反射
  19. Unit Test 單元測試
    go test 指令會執行所有檔名後綴為「_test.go」檔案內的函式
    go test
    go test -v
    go test -v run 指定要執行的函式
    「_test.go」檔案內的函式須為Test開頭
    例如:
    func TestAdd(t *testing.T) { /* ... */ } //後面接英文,須大寫
    func Test_add(t *testing.T) { /* ... */ }
    範例:
    add.go 專案程式檔案
    package main
    
    import (
        "fmt"
    )
    
    func add(a, b int) int {
        return a + b
    }
    
    func main() {
        fmt.Println(add(1, 2))
    }
    add_test.go 單元測試檔案
    package main
    
    import "testing"
    
    func TestAddA(t *testing.T) {
        if add(1, 2) != 3 {
            t.Error("1+2 err")
        }
    }
    
    func TestAddB(t *testing.T) {
        if add(20, 30) != 50 {
            t.Error("20+30 err")
        }
    }
    執行單元測試
    >go test    
    PASS
    ok      hello/UnitTest  0.216s
    
    >go test -v
    === RUN   TestAddA
    --- PASS: TestAddA (0.00s)
    === RUN   TestAddB
    --- PASS: TestAddB (0.00s)
    PASS
    ok      hello/UnitTest  0.203s
    
    
    >go test -v -run TestAddB
    === RUN   TestAddB
    --- PASS: TestAddB (0.00s)
    PASS
    ok      hello/UnitTest  0.214s





參考:


2020年12月28日 星期一

Redis Windows 版

Redis 官網,只提供 Linux 的版本。
但 Microsoft Open Tech 有提供 windows 版本
https://github.com/microsoftarchive/redis/releases
有.msi的安裝檔和免安裝的縮壓檔。

如果用安裝檔,安裝後,會將 Redis 設定成系統服務開機自動啟動,
安裝過程也可選擇是否加入系統 PATH 環境變數、記憶體限制、是否開放防火牆...

如果使用免安裝的縮壓檔,之後想設定成系統服務。
可參考資料夾下的說明文件範例:
安裝成系統服務
redis-server --service-install redis.windows-service.conf --loglevel verbose

移除安裝的系統服務
redis-server --service-uninstall

啟動
redis-server --service-start

停止
redis-server --service-stop

安裝並啟動三個獨立的Redis服務範例:
redis-server --service-install --service-name redisService1 --port 10001
redis-server --service-start --service-name redisService1
redis-server --service-install --service-name redisService2 --port 10002
redis-server --service-start --service-name redisService2
redis-server --service-install --service-name redisService3 --port 10003
redis-server --service-start --service-name redisService3



其他:
https://opensource.microsoft.com
https://github.com/microsoftarchive

2020年12月24日 星期四

Linux 網卡新增多個IP

CentOS,在一張名稱為 enp0s8 的網路卡,設定多個IP



方法一:
使用 ifconfig 指令動態新增IP(網卡重啟後失效)

#新增一個別名 enp0s8:xyz (較常見使用 enp0s8:0、enp0s8:1、...),用來設定新增的IP
$ ifconfig enp0s8:xyz 192.168.56.111/24 up
#關閉別名為 enp0s8:xyz 的設定
$ ifconfig enp0s8:xyz down



方法二:
使用 ip 指令動態新增(網卡重啟後失效)

#不設定別名,直接新增IP
$ ip addr add 192.168.56.111/24 dev enp0s8
#新增一個別名 enp0s8:xyz (較常見使用 enp0s8:0、enp0s8:1、...),用來設定新增的IP
$ ip addr add 192.168.56.111/24 dev enp0s8 label enp0s8:xyz
#刪除指定的IP設定
$ ip addr del 192.168.56.111/24 dev enp0s8 label enp0s8:xyz




方法三:
於原網路卡設定檔,參數加後綴,設定新增的IP

$ vi /etc/sysconfig/network-scripts/ifcfg-enp0s8
.......
###第1個新增的IP
IPADDR1=192.168.56.111
PREFIX1=24
###第2個新增的IP
IPADDR2=192.168.56.112
PREFIX2=24
重啟網路
$ systemctl restart network
或
$ ifdown enp0s8 ; ifup enp0s8;



方法四:
新增網路卡別名設定檔,設定新增的IP

$ vi /etc/sysconfig/network-scripts/ifcfg-enp0s8:xyz1
DEVICE="enp0s8:xyz1"
#/usr/share/doc/initscripts-9.49.53/sysconfig.txt
#ONBOOT="yes" #(not valid for alias devices; use ONPARENT)
ONPARENT="yes"
IPADDR="192.168.56.111"
PREFIX="24" #NETMASK=255.255.255.0
$ vi /etc/sysconfig/network-scripts/ifcfg-enp0s8:xyz2
DEVICE="enp0s8:xyz2"
#ONBOOT="yes" #(not valid for alias devices; use ONPARENT)
ONPARENT="yes"
IPADDR="192.168.56.112"
PREFIX="24" #NETMASK=255.255.255.0
啟用新增的網卡別名設定
$ ifup enp0s8:xyz1
$ ifup enp0s8:xyz2



參考:

AVD Manager 啟動Android模擬器出現 HAXM is not installed on this machine

問題: 
啟動 Android 模擬器,出現錯誤
「emulator: ERROR: x86_64 emulation currently requires hardware acceleration!
Please ensure Intel HAXM is properly installed and usable.
CPU acceleration status: HAXM is not installed on this machine」


解決:

  1. 確定 BIOS 中的「Intel Virtualization Technology」已開啟
  2. 確定 SDK Manager 中已安裝「 Exreas > intel x86 Emulator Accelerator(HAXM installer)」
  3. 到 「android-sdk\extras\intel\Hardware_Accelerated_Execution_Manager」資料夾下,
    執行
    silent_install.bat
    安裝 HAXM

    反安裝 HAXM
    silent_install.bat -u

    檢查是否已安裝 HAXM
    silent_install.bat -v

    檢查 VT/NX 支援
    silent_install.bat -c



參考:
https://www.itread01.com/p/2322.html
Android stdio 解決HAXM安裝不上問題