動物皮草太殘忍 洛杉磯市議會全體贊成禁售

摘錄自2018年9月21日蘋果日報美國洛杉磯報導

洛杉磯市議會周二(18日)通過議案,將立法禁止銷售皮草衣飾。議會全體投下贊成票,立場堅定;洛杉磯將成為美國禁售皮草的最大城市,可望為其他時尚重鎮帶來示範作用。

洛杉磯市議會以12比0的票數,一致贊成禁止商業皮草。立法機構負責草擬法規,由市議會審核,正式法規將在通過審議的兩年後生效。預計這類法規會為宗教目的、合法漁獵執照持有者另闢途徑,允許合法使用或生產動物皮草。

加州舊金山、西好萊塢、柏克萊都已限制皮草,但像洛杉磯這麼大規模的城市還是首例。提出此議案的議員科瑞茲(Paul Koretz)表示,洛杉磯是世界時尚之都,期許此舉能成為世界典範,紐約、芝加哥和邁阿密等大城可以跟進。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

中國發佈EVOP電動汽車運營平臺 打造電動汽車網聯大腦

日前,中能工業智慧技術研究院發佈了EVOP電動汽車運營平臺,打造中國最強電動汽車網聯大腦。

從傳統意義上來說,電動汽車只是“行駛+充電”的物理組合,滿足人們最基本的代步需求;而EVOP在此基礎上,通過互聯網和智慧化平臺,將電動汽車和充電設備打造成為能源互聯網產業鏈的重要一環。

和其他智慧化汽車應用相比,EVOP平臺基於中國最強工業大腦DPEN而打造,將資料、資訊和互聯網相結合,讓電動汽車產業鏈變得更智慧。DPEN支持數千萬個採集節點。在DPEN的引領之下,源源不斷的資料進入EVOP平臺,分門別類進行存儲和分析,並通過互聯網傳遞到每一輛電動汽車或者充電設備上,指導設備智慧化、高效率運行,並實現充電網、互聯網、車聯網“三網融合”。

在充電端,EVOP可以輕鬆實現智慧充電功能,它可以即時檢測並調整充電狀態,加強電池的健康管理,引導智慧有序充電、計量計費,並讓車主通過手機隨時瞭解充電情況;而商業地產、物業管理公司、電動汽車廠商等運營商和服務商可以通過雲平臺實現充電樁和車載電池的智慧管理,提供良好的增值服務。

在行駛端,由EVOP平臺海量採集的資料經過精確梳理和分析,通過雲平臺提供給每一位車主,在EVOP營造的車聯網中智慧、高效、安全出行。人們不僅可以隨時瞭解車輛和電池的資訊,快速查詢身邊的充電設備、預約充電;也可以在EVOP的指導下獲得最佳行車路線和最佳能效使用方案;亦可以在EVOP的社交平臺中交流經驗、分享資訊、找到志同道合的朋友,享受 “大資料+互聯網”的時尚車生活。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

go中的關鍵字-select

1. select的使用

  定義:在golang裡頭select的功能與epoll(nginx)/poll/select的功能類似,都是堅挺IO操作,當IO操作發生的時候,觸發相應的動作。

1.1 一些使用規範

  在Go的語言規範中,select中的case的執行順序是隨機的,當有多個case都可以運行,select會隨機公平地選出一個執行,其他的便不會執行:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6     ch := make (chan int, 1)
 7 
 8     ch<-1
 9     select {
10     case <-ch:
11         fmt.Println("隨機一")
12     case <-ch:
13         fmt.Println("隨機二n")
14     }
15 }

  輸出內容為隨機一二里面的任意一個。

  case後面必須是channel操作,否則報錯;default子句總是可運行的,所以沒有default的select才會阻塞等待事件 ;沒有運行的case,那麼將會阻塞事件發生報錯(死鎖)。

1.2 select的應用場景

timeout 機制(超時判斷)
 1 package main
 2 
 3 import (
 4     "fmt"
 5     "time"
 6 )
 7 
 8 func main() {
 9     timeout := make (chan bool, 1)
10     go func() {
11         time.Sleep(1*time.Second) // 休眠1s,如果超過1s還沒I操作則認為超時,通知select已經超時啦~
12         timeout <- true
13     }()
14     ch := make (chan int)
15     select {
16     case <- ch:
17     case <- timeout:
18         fmt.Println("超時啦!")
19     }
20 }

  也可以這麼寫:

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "time"
 6 )
 7 
 8 func main() {
 9     ch := make (chan int)
10     select {
11     case <-ch:
12     case <-time.After(time.Second * 1): // 利用time來實現,After代表多少時間后執行輸出東西
13         fmt.Println("超時啦!")
14     }
15 }

  判斷channel是否阻塞(或者說channel是否已經滿了)

 1 package main
 2 
 3 import (
 4     "fmt"
 5 )
 6 
 7 func main() {
 8     ch := make (chan int, 1)  // 注意這裏給的容量是1
 9     ch <- 1
10     select {
11     case ch <- 2:
12     default:
13         fmt.Println("通道channel已經滿啦,塞不下東西了!")
14     }
15 }

  退出機制

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "time"
 6 )
 7 
 8 func main() {
 9     i := 0
10     ch := make(chan string, 0)
11     defer func() {
12         close(ch)
13     }()
14 
15     go func() {
16         DONE: 
17         for {
18             time.Sleep(1*time.Second)
19             fmt.Println(time.Now().Unix())
20             i++
21 
22             select {
23             case m := <-ch:
24                 println(m)
25                 break DONE // 跳出 select 和 for 循環
26             default:
27             }
28         }
29     }()
30 
31     time.Sleep(time.Second * 4)
32     ch<-"stop"
33 }

2. select的實現

  select-case中的chan操作編譯成了if-else。如:

1  select {
2  case v = <-c:
3          ...foo
4  default:
5          ...bar
6  }

  會被編譯為:

1  if selectnbrecv(&v, c) {
2          ...foo
3  } else {
4          ...bar
5  }

  類似地

1  select {
2  case v, ok = <-c:
3      ... foo
4  default:
5      ... bar
6  }

  會被編譯為:

1  if c != nil && selectnbrecv2(&v, &ok, c) {
2      ... foo
3  } else {
4      ... bar
5  }

  selectnbrecv函數只是簡單地調用runtime.chanrecv函數,不過是設置了一個參數,告訴當runtime.chanrecv函數,當不能完成操作時不要阻塞,而是返回失敗。也就是說,所有的select操作其實都僅僅是被換成了if-else判斷,底層調用的不阻塞的通道操作函數。

  在Go的語言規範中,select中的case的執行順序是隨機的,那麼,如何實現隨機呢?

  select和case關鍵字使用了下面的結構體:

1 struct    Scase
2   {
3       SudoG    sg;            // must be first member (cast to Scase)
4       Hchan*    chan;        // chan
5       byte*    pc;            // return pc
6       uint16    kind;
7       uint16    so;            // vararg of selected bool
8       bool*    receivedp;    // pointer to received bool (recv2)
9   };
1  struct    Select
2      {
3      uint16    tcase;            // 總的scase[]數量
4      uint16    ncase;            // 當前填充了的scase[]數量
5      uint16*    pollorder;        // case的poll次序
6      Hchan**    lockorder;        // channel的鎖住的次序
7      Scase    scase[1];        // 每個case會在結構體里有一個Scase,順序是按出現的次序
8  };

  每個select都對應一個Select結構體。在Select數據結構中有個Scase數組,記錄下了每一個case,而Scase中包含了Hchan。然後pollorder數組將元素隨機排列,這樣就可以將Scase亂序了。

 3. select死鎖

  select不注意也會發生死鎖,分兩種情況:

  如果沒有數據需要發送,select中又存在接收通道數據的語句,那麼將發送死鎖

1 package main
2 func main() {  
3     ch := make(chan string)
4     select {
5     case <-ch:
6     }
7 }

  預防的話加default。

  空select,也會引起死鎖。

1 package main
2 
3 func main() {  
4     select {}
5 }

 4. select和switch的區別

select

select只能應用於channel的操作,既可以用於channel的數據接收,也可以用於channel的數據發送。如果select的多個分支都滿足條件,則會隨機的選取其中一個滿足條件的分支, 如規範中所述:

If multiple cases can proceed, a uniform pseudo-random choice is made to decide which single communication will execute.

`case`語句的表達式可以為一個變量或者兩個變量賦值。有default語句。

31 package main                                                                                                                                              32 import "time"
33 import "fmt"                                                                                                                                              
35 func main() {                                                                                                                                             36     c1 := make(chan string)
37     c2 := make(chan string)                                                                                                                               38     go func() {
39         time.Sleep(time.Second * 1)                                                                                                                       40         c1 <- "one"
41     }()                                                                                                                                                   42     go func() {
43         time.Sleep(time.Second * 2)                                                                                                                       44         c2 <- "two"
45     }()                                                                                                                                                   46     for i := 0; i < 2; i++ {
47         select {                                                                                                                                          48             case msg1 := <-c1:
49             fmt.Println("received", msg1)          
50 case msg2 := <-c2: 51 fmt.Println("received", msg2)
52 } 53 }

switch

  switch可以為各種類型進行分支操作, 設置可以為接口類型進行分支判斷(通過i.(type))。switch 分支是順序執行的,這和select不同。

 1 package main                  
 2 import "fmt"
 3 import "time"  
 4 
 5 func main() {                                                                                                                             
 6      i := 2
 7      fmt.Print("Write ", i, " as ")  
 8      switch i {
 9          case 1:
10          fmt.Println("one")
11          case 2:                                                                                                                                  
12          fmt.Println("two")
13          case 3:                                                                                                                      
14          fmt.Println("three")
15      }                                                                                                                                             
16      switch time.Now().Weekday() {
17          case time.Saturday, time.Sunday:
18          fmt.Println("It's the weekend")
19          default:                                                                                                                                      
20          fmt.Println("It's a weekday")
21      }                                                                                                                                                 
22      t := time.Now()
23      switch {                                                                                                                                         
24          case t.Hour() < 12:
25          fmt.Println("It's before noon")                                                                                                              
26          default:
27          fmt.Println("It's after noon")                                                                                                                  
28      }
29      whatAmI := func(i interface{}) {                                                                                                                   
30          switch t := i.(type) {
31              case bool:                                                                                                                              
32              fmt.Println("I'm a bool")
33              case int:                                                                                                                                 
34              fmt.Println("I'm an int")
35              default:                                                                                                                                 
36              fmt.Printf("Don't know type %T\n", t)
37          }
38      }
39      whatAmI(true)                                                                                                                                     
40      whatAmI(1)
41      whatAmI("hey")                                                                                                                                 
42  }

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

Tesla年產50萬電動車牛皮吹太大,供應商坦承沒把握

特斯拉(Tesla)計畫在2018年大規模量產平價電動車Model 3,時程較原先計畫提早兩年,雖然展現特斯拉執行長穆斯克(Elon Musk)強大企圖心,但也飽受專家質疑,甚至特斯拉旗下供應商對於是否能達成目標也沒把握。   Model 3已開放預定,特斯拉目前累計收到37.3萬輛的訂單、預計2017年底開始交車。但外界所不知道的是,供應商是在三個月之前才被告知增產計畫提前,相關準備作業是否先完成令人質疑。據路透社引述知情人事消息報導指出,特斯拉最新計畫把2017年Model 3產出規模提高兩倍至10萬輛,2018年更將達成40萬輛的產出水平。   穆斯克4月28日曾在財報法說會上對分析師誇下海口,2018年整體產出規模(含Model S、Model X、Model 3)將來到50萬輛,是去年的十倍,也較預設時程快了兩年。不過Model 3設計目前還要等到6月才定案,距初次量產僅剩13個月。   電動車零組件何其多,且缺一不可,特斯拉如何在短時間內搞定供應商、備齊所有零組件,雖然並非完全不可能,但挑戰難度絕對相當高,這也考驗穆斯克的管理協調能力。值得一提的是,北美目前僅有少數汽車產線有年產50萬輛的能力,且背後都有幾十年經驗的車廠在運籌帷幄。   (本文內容由授權使用;首圖來源: CC BY 2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

jwt 實踐應用以及特殊案例思考

JSON Web Token 是 出的一份標準,使用 JSON 來傳遞數據,用於判定用戶是否登錄狀態。

jwt 之前,使用 session 來做用戶認證。

以下代碼均使用 javascript 編寫。

  • 原文鏈接:

session

傳統判斷是否登錄的方式是使用 session + token

token 是指在客戶端使用 token 作為用戶狀態憑證,瀏覽器一般存儲在 localStorage 或者 cookie 中。

session 是指在服務器端使用 redis 或者 sql 類數據庫,存儲 user_id 以及 token 的鍵值對關係,基本工作原理如下。

在服務器端使用 sessions 存儲鍵值對

const sessions = {
  "ABCED1": 10086,
  "CDEFA0": 10010
}

每次客戶端請求帶權限數據時攜帶 token,在服務器端根據 token 與 sessions 獲取 user_id, 完成認證過程

function getUserIdByToken (token) {
  return sessions[token]
}

如果存儲在 cookie 中就是經常聽到的 session + cookie 的登錄方案。其實存儲在 cookielocalStorage 甚至 IndexedDB 或者 WebSQL 各有利弊,核心思想一致。

關於 cookie 以及 token 優缺點,在 中有討論。

如果不使用 cookie,可以採取 localStorage + Authorization 的方式進行認證,更加無狀態化

// http 的頭,每次請求權限接口時,需要攜帶 Authorization Header
const headers = {
  Authorization: `Bearer ${localStorage.get('token')}`
}

推薦一個前端的存儲庫 ,使用 IndexedDBWebSQL 以及 IndexedDB 做鍵值對存儲。

無狀態登錄

session 需要在數據庫中保持用戶及token對應信息,所以叫 有狀態

試想一下,如何在數據庫中不保持用戶狀態也可以登錄。

第一種方法: 前端直接傳 user_id 給服務端

缺點也特別特別明顯,容易被用戶篡改成任意 user_id,權限設置形同虛設。不過思路正確,接着往下走。

改進: 對 user_id 進行對稱加密

服務端對 user_id 進行對稱加密后,作為 token 返回客戶端,作為用戶狀態憑證。比上邊略微強點,但由於對稱加密,選擇合適的算法以及密鑰比較重要

改進: 對 user_id 不需要加密,只需要進行簽名,保證不被篡改

這便是 jwt 的思想:user_id,加密算法和簽名組成 token 一起存儲到客戶端,每當客戶端請求接口時攜帶 token,服務器根據 token 解析出加密算法與 user_id 來判斷簽名是否一致。

Json Web Token

jwt 根據 HeaderPayload 以及 Signature 三個部分由 . 拼接而成。

Header

Header 由非對稱加密算法和類型組成,如下

const header = {
  // 加密算法
  alg: 'HS256',
  type: 'jwt'
}

Payload

Payload 中由 以及需要通信的數據組成。這些數據字段也叫 Claim

Registered Claim 中比較重要的是 "exp" Claim 表示過期時間,在用戶登錄時會設置過期時間。

const payload = {
  // 表示 jwt 創建時間
  iat: 1532135735,

  // 表示 jwt 過期時間
  exp: 1532136735,

  // 用戶 id,用以通信
  user_id: 10086
}

Signature

SignatureHeaderPayload 以及 secretOrPrivateKey 計算而成。secretOrPrivateKey 作為敏感數據存儲在服務器端,可以考慮使用 vault secret 或者 k8s secret

對於 secretOrPrivateKey,如果加密算法採用 HMAC,則為字符串,如果採用 RSA 或者 ECDSA,則為 PrivateKey。

// 由 HMACSHA256 算法進行簽名,secret 不能外泄
const sign = HMACSHA256(base64.encode(header) + '.' + base64.encode(payload), secret)

// jwt 由三部分拼接而成
const jwt = base64.encode(header) + '.' + base64.encode(payload) + '.' + sign

從生成 jwt 規則可知客戶端可以解析出 payload,因此不要在 payload 中攜帶敏感數據,比如用戶密碼

校驗過程

在生成規則中可知,jwt 前兩部分是對 header 以及 payload 的 base64 編碼。

當服務器收到客戶端的 token 后,解析前兩部分得到 header 以及 payload,並使用 header 中的算法與 secretOrPrivateKey 進行簽名,判斷與 jwt 中攜帶的簽名是否一致。

帶個問題,如何判斷 token 過期?

應用

由上可知,jwt 並不對數據進行加密,而是對數據進行簽名,保證不被篡改。除了在登錄中可以用到,在進行郵箱校驗,圖形驗證碼和短信驗證碼時也可以用到。

圖形驗證碼

在登錄時,輸入密碼錯誤次數過多會出現圖形驗證碼。

圖形驗證碼的原理是給客戶端一個圖形,並且在服務器端保存與這個圖片配對的字符串,以前也大都通過 session 來實現。

可以把驗證碼配對的字符串作為 secret,進行無狀態校驗。

const jwt = require('jsonwebtoken')

// 假設驗證碼為字符驗證碼,字符為 ACDE,10分鐘失效
const token = jwt.sign({}, secrect + 'ACDE', { expiresIn: 60 * 10 })

const codeImage = getImageFromString('ACDE')

// 給前端的響應
const res = {
  // 驗證碼圖片的 token,從中可以校驗前端發送的驗證碼
  token,
  // 驗證碼圖片
  codeImage,
}

短信驗證碼與圖形驗證碼同理

郵箱校驗

現在網站在註冊成功後會進行郵箱校驗,具體做法是給郵箱發一個鏈接,用戶點開鏈接校驗成功。

// 把郵箱以及用戶id綁定在一起
const code = jwt.sign({ email, userId }, secret, { expiresIn: 60 * 30 })

// 在此鏈接校驗驗證碼
const link = `https://example.com/code=${code}`

無狀態 VS 有狀態

關於無狀態和有狀態,在其它技術方向也有對比,比如 React 的 stateLess component 以及 stateful component,函數式編程中的副作用可以理解為狀態,http 也是一個無狀態協議,需要靠 header 以及 cookie 攜帶狀態。

在用戶認證這裏,有無狀態是指是否依賴外部數據存儲,如 mysql,redis 等。

案例

思考以下幾個關於登錄的問題如何使用 session 以及 jwt 實現,來更加清楚 jwt 的使用場景

當用戶註銷時,如何使該 token 失效

因為 jwt 無狀態,不保存用戶設備信息,沒法單純使用它完成以上問題,可以再利用數據庫保存一些狀態完成。

  • session: 只需要把 user_id 對應的 token 清掉即可
  • jwt: 使用 redis,維護一張黑名單,用戶註銷時把該 token 加入黑名單,過期時間與 jwt 的過期時間保持一致。

如何允許用戶只能在一個設備登錄,如微信

  • session: 使用 sql 類數據庫,對用戶數據庫表添加 token 字段並加索引,每次登陸重置 token 字段,每次請求需要權限接口時,根據 token 查找 user_id
  • jwt: 假使使用 sql 類數據庫,對用戶數據庫表添加 token 字段(不需要添加索引),每次登陸重置 token 字段,每次請求需要權限接口時,根據 jwt 獲取 user_id,根據 user_id 查用戶表獲取 token 判斷 token 是否一致。另外也可以使用計數器的方法,如下一個問題。

對於這個需求,session 稍微簡單些,畢竟 jwt 也需要依賴數據庫。

如何允許用戶只能在最近五個設備登錄,如諸多播放器

  • session: 使用 sql 類數據庫,創建 token 數據庫表,有 id, token, user_id 三個字段,user 與 token 表為 1:m 關係。每次登錄添加一行記錄。根據 token 獲取 user_id,再根據 user_id 獲取該用戶有多少設備登錄,超過 5 個,則刪除最小 id 一行。
  • jwt: 使用計數器,使用 sql 類數據庫,在用戶表中添加字段 count,默認值為 0,每次登錄 count 字段自增1,每次登錄創建的 jwt 的 Payload 中攜帶數據 current_count 為用戶的 count 值。每次請求權限接口時,根據 jwt 獲取 count 以及 current_count,根據 user_id 查用戶表獲取 count,判斷與 current_count 差值是否小於 5

對於這個需求,jwt 略簡單些,而使用 session 還需要多維護一張 token 表。

如何允許用戶只能在最近五個設備登錄,而且使某一用戶踢掉除現有設備外的其它所有設備,如諸多播放器

  • session: 在上一個問題的基礎上,刪掉該設備以外其它所有的token記錄。
  • jwt: 在上一個問題的基礎上,對 count + 5,並對該設備重新賦值為新的 count。

如何显示該用戶登錄設備列表 / 如何踢掉特定用戶

  • session: 在 token 表中新加列 device
  • jwt: 需要服務器端保持設備列表信息,做法與 session 一樣,使用 jwt 意義不大

總結

從以上問題得知,如果不需要控制登錄設備數量以及設備信息,無狀態的 jwt 是一個不錯的選擇。一旦涉及到了設備信息,就需要對 jwt 添加額外的狀態支持,增加了認證的複雜度,此時選用 session 是一個不錯的選擇。

jwt 不是萬能的,是否採用 jwt,需要根據業務需求來確定。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

中國財政部稱對新能源汽車騙補的現場核查已完成

財政部日前發佈聲明,表示對新能源汽車推廣騙補核查的第一部分工作,即現場核查,已經完成,目前仍處於對核查結果的會審階段,並稱對於核查和處理情況將按資訊公開有關規定及時公開。

業內人士原來普遍預期財政部能在4月或最遲5月公佈此項調查結果,但現在看來調查比預計的更耗時。

1月下旬,財政部、工信部等多個部委聯合發佈新能源汽車推廣應用核查工作的通知,在全國範圍對獲得中央財政補貼的新能源汽車及其生產企業和用戶展開核查。

在財政補貼的刺激下,2015年新能源汽車生產 34.05萬輛,銷售33.11萬輛,同比分別增長3.3倍和3.4倍。今年第一季度,增長有所放緩,但4月份又開始加速。

據中汽協統計,今年4月國內新能源汽車生產31266輛,銷售 31772輛,同比分別增長178.3%和190.6%。前4個月累計產銷量均超9萬輛,相比去年同期增長近130%。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

Tesla電動車需求激增,Panasonic美國電池廠量產提前

特斯拉(Tesla)電動車需求火熱,日本電池製造商Panasonic為滿足大客戶需求,其位在美國那華達州的鋰電池工廠量產進度也配合提早約數個月。   按原先時程表,Panasonic美國工廠要等到特斯拉2016會計年度,也就是2017年初期才會投產。但據日經新聞周二報導,Panasonic工廠今年七月就將啟用,並在十一月開始生產電池芯。特斯拉首輛大眾化電動車Model 3自從發表後,訂單應接不暇,想必Panasonic加快量產時程應該與此有關。   除此之外,Panasonic擴廠腳步也將同時加速,據某Panasonic高層表示,原先16億美元、分八期的投資計畫可能會做修改,目的是避免特斯拉因電池供應不及而被迫中斷生產。   特斯拉目前正在中國尋找合適設廠地點,美國媒體報導指出,上海先馳得點,其國營金橋集團已與特斯拉簽訂不具約束力協定,使上海雀屏中選的機率大增。如果未來成案,特斯拉與金橋將各出資45億美元,其中金橋主要負責取得工廠用地。   (本文內容由授權使用;首圖來源: CC BY 2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

中國強推動力電池技術,三元電池材料需求增

大陸國家動力電池創新中心昨日(6月30日)舉行成立大會,目的在於將研究資源進行整合,力求在動力電池核心技術取得突破;該創新中心將以國聯公司作為重要合作機構,聚合一汽、東風、北汽等諸多大型汽車集團。

陸媒報導指出,大陸動力電池主要為磷酸鐵鋰電池或三元電池,而三元電池比磷酸鐵鋰電池的能量密度更高、標準電壓更強、電芯占空間更少。同時作為電動車的心臟,動力電池的續航能力也是非常重要的一點,三元電池的續航里程和儲能強於傳統鋰電池,鋰電池每月衰減3%的電量,而三元電池反復使用後每月衰減僅1%-2%,近期已有多家大陸主流汽車廠商表示已生產和使用三元電池。

另從大陸工信部新能源汽車產銷數據來看,大陸電動汽車產業已進入快速增長階段,而作為電動車必備的動力電池需求量也已水漲船高;目前,三元材料較多應用在乘用車領域,作為三元電池的主要需求端,多款車型在政策支持力道不斷提升的同時,對三元材料的需求也在快速增長。

在此之前,大陸科技部發佈的《國家重點研發計畫新能源汽車重點專項實施方案》明確表示,轎車動力電池到2020年單體電池指標達到350Wh,系統達到250Wh以上。中泰證券研究報告預計,到2020年,車用三元材料將達20萬噸,年均增速將達到54%。大陸工信部預測,2018年三元電池的出貨量可望首度超過鋰電池,成為續航電池的主流材料。業內人士預計,三元電池產業可望迎接加速增長,進一步佔領動力電池的市場份額。

(本文由授權提供)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

上海市半年內推出八項新能源汽車政策

2016上半年,中國與新能源汽車相關的中央及地方各類政策已經推出了80餘項,包括新能源汽車及充電基礎設施推廣規劃、補貼標準、充電費、充電服務費標準、專項資金補貼、及指標交通等配套優惠政策。雖然政策密集,但2016上半年新能源汽車產業的發展依然掣肘於政策的完善性。   2016年上半年,上海市共推出了8項新能源汽車相關政策,包括購買和使用新能源汽車暫行辦法、操作流程、補貼標準,充電基礎設施建設規劃、補貼標準,新能源汽車專項資金,支援新能源貨車推廣計畫,促進新能源汽車分時租賃業務發展指南,以及嘉定區補貼方案。這些政策在內容上覆蓋了從生產、銷售、購買、使用整個新能源汽車生產鏈,也對技術研發給予專項資金的鼓勵支援,同時覆蓋了新能源汽車的分時租賃業務。   相對比2015年上海市新能源汽車的補貼標準,2016年上海的補貼標準在乘用車、客車及專用車領域都有所消退,同時,上海市還首次提出了補貼「按量退坡(減少)」的概念,即超過一定量後補貼會繼續減少,這體現了上海由補貼向市場引導新能源汽車發展的策略。除了補貼標準的降低外,上海市對補貼的審批也更加嚴格,對廠商的銷售資格及消費者的購買資格都做出了嚴格要求。   除此之外,上海市的推廣政策還照顧到了分時租賃業務,通過支持運營車輛額度需求及相關補貼政策鼓勵新能源汽車分時租賃業務發展。  
文章來源:第一電動網(中國大陸)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

nodejs入門之模塊

  • nodejs模塊語法與開閉原則
  • nodejs模塊的底層實現

 一、nodejs模塊語法與開閉原則

關於nodejs模塊我在之前的兩篇博客中都有涉及,但都沒有對nodejs模塊的底層做做任何探討,但是為了使相關內容更方便查看比對理解,這裏還是先引入一下之前兩篇博客的連接:

1.1 exports、module.exports、require()實現模塊導出導入:

 1 //示例一:導出原始值數據
 2 //a.js--用於導出數據
 3 let a = 123;
 4 module.exports.a=a;
 5 //inde.js--用於導入a模塊的數據
 6 let aModule = require('./a.js');
 7 console.log(aModule.a); //123
 8 
 9 //示例二:導出引用值數據
10 //a.js--同上
11 function foo(val){ 
12     console.log(val);
13 }
14 module.exports.foo = foo;
15 //index.js--同上
16 let aModule = require('./a.js');
17 let str = "this is 'index' module"
18 aModule.foo(str); //this is 'index' module
19 
20 //示例三:導出混合數據
21 a.js--同上
22 let a = 123;
23 function foo(val){ 
24     console.log(val);
25 }
26 module.exports = {
27     a:a,
28     foo:foo
29 }
30 //inde.js--同上
31 let aModule = require('./a.js');
32 let str = "this is 'index' module"
33 console.log(aModule.a);//123
34 aModule.foo(str); //this is 'index' module

在上面這些示例中,沒有演示exports的導出,暫時可以把它看作與同等於module.exports,例如:

 1 //a.js -- 導出模塊
 2 let a = 123;
 3 function foo(val){ 
 4     console.log(val);
 5 }
 6 exports.a = a;
 7 exports.foo = foo;
 8 
 9 //inde.js -- 引用模塊a
10 let aModule = require('./a.js');
11 let str = "this is 'index' module"
12 console.log(aModule.a);//123
13 aModule.foo(str); //this is 'index' module

但是使用exports導出模塊不能這麼寫:

 1 //a.js
 2 let a = 123;
 3 function foo(val){ 
 4     console.log(val);
 5 }
 6 exports = {
 7     a:a,
 8     foo:foo
 9 }
10 
11 //index.js
12 let aModule = require('./a.js');
13 let str = "this is 'index' module"
14 console.log(aModule);// {} -- 一個空對象

至於為什麼不能這麼寫,暫時不在這裏闡述,下一節關於nodejs模塊底層實現會具體的分析介紹,這裏先來介紹nodejs模塊的一個設計思想。

1.2 nodejs模塊的開閉原則設計實現

1 //a.js -- 導出模塊
2 let num = 123;
3 let str = "this is module 'a'";
4 exports.a = a;
5 
6 //index.js -- 引用模塊a
7 let aModule = require('./a.js');
8 console.log(aModule.num);//123
9 console.log(aModule.str);//undefined

這裏你會發現只有被exports執行了導出的num成員才能被正常導出,而str成員沒有被執行導出,在依賴a.js模塊的index.js中是不能引用到a.js模塊中的str成員。可能你會說這不是很正常嗎?都沒有導出怎麼引用呢?

不錯,這是一個非常正常情況,因為語法就告訴了我們,要想引用一個模塊的成員就必須先在被引用的模塊中導出該成員。然而這裏要討論的當然不會是導出與引用這個問題,而是模塊給我實現了一個非常友好的設計,假設我現在在a.js中有成員str,在index.js模塊中也有成員str,這回衝突嗎?顯然是不會的,即使在a.js中導出str並且在index.js中引用a.js模塊,因為index.js要使用a.js模塊的成員str,需要使用接收模塊變量aModule.str來使用。

 1 //a.js
 2 let num = 123;
 3 let str = "this is module 'a'";
 4 exports.num = num;
 5 exports.str = str;
 6 
 7 //index.js
 8 let aModule = require('./a.js');
 9 let str = "this is module 'index'"
10 console.log(aModule.num);//123
11 console.log(aModule.str);//this is module 'a'
12 console.log(str);//this is module 'index'

基於開閉原則的設計方式,封閉可以讓模塊的內部實現隱藏起來,開放又可以友好的實現模塊之間的相互依賴,這相對於之前我們常用的回調函數解決方案,程序設計變得更清晰,代碼復用變得更靈活,更關鍵的是還解決了js中一個非常棘手的問題——命名衝突問題,上面的示例就是最好的證明。這裏需要拋出一個問題,看示例:

1 //下面這種寫法有什麼問題?
2 //a.js
3 let num = 123;
4 module.exports = num;
5 
6 //index.js
7 let aModule = require('./a.js');
8 let str = "this is module 'index'"
9 console.log(aModule);//123

這種寫法不會報錯,也能正常達到目前的需求,如果從能解決目前的功能需求角度來說,它沒錯。但是開閉原則的重要思想就是讓模塊保持相對封閉,又有更好的拓展性,這樣寫顯然不合適,比如就上面的代碼寫完上線以後,業務又出現了一個新的需求需要a.js模塊導出一個成員str,這時候顯然需要同時更改a.js模塊和index.js模塊,即使新需求不需要index.js來實現也是需要改的。所以維持模塊的開閉原則是良好的編碼風格。

 二、nodejs模塊的底層實現原理

2.1 module.exports與exports的區別:

//a.js
console.log(module.exports == exports);//true

//然後在控制台直接執行a.js模塊
node a.js

實際上它們是沒有區別的,那為什麼在之前的exports不能直接等於一個對象,而module.exports可以呢?這關乎於js的引用值指向問題:

 

 當export被賦值一個對象時,就發生了一下變化:

這時候我們可以確定node不會導出exports,因為前面的示例已經說明了這一點,但是值得我們繼續思考的是,node模塊是依據module.exports、exports、還是它們指向的初始對象呢?這裏你肯定會說是module.exports,因為前面已經有示例是module.exports指向一個新的對象被成功導出,但是我並不覺得前面那些示例能說服我,比如下面這種情況:

 1 //a.js模塊
 2 let num = 123;
 3 function foo(val){
 4     console.log(val);
 5 }
 6 module.exports = {
 7     num:num
 8 }
 9 exports = {
10     foo:foo
11 }
12 //index.js模塊
13 let aModule = require('./a.js');
14 console.log(aModule);//這裡會打印出什麼?

我們現不測試也不猜測,先通過下面的示圖來看下現在的a.js模塊中module.exports、exports、以及它們兩初始指向的空對象的關係圖:

 

 這時候我們來看一下index.js執行會輸出什麼?

{ num: 123 }

所以從這個結果可以看出,最後require()最後導入的是被引用模塊的module.exports。探討到這裏的時候並沒有到達node模塊的終點,我們這裏module.exports、exports、require()是從哪裡來的?node系統內置變量?還是別的?

2.2 node模塊的底層實現原理

這部分的內容其實也沒有太多可以說的,就前面提出來的問題其實有一個方式就可以讓你一目瞭然,只需要在一個js文件中編寫以下代碼,然後使用node執行這個js文件就可以了:

1 console.log(require);      // 一個方法
2 console.log(module);       //  一個對象
3 console.log(exports);      //  一個空對象
4 console.log(__dirname);    //  當前模塊所在路徑
5 console.log(__filename);   //  當前文件的路徑

 這時因為node模塊實際上底層是被放到一個立即執行函數內(不要在乎xyz這個名稱,因為我也不知道node底層到底用的什麼名稱),這些變量其實就是這個函數的參數,這個函數大概是一下形式:

1 function xyz(module.exports,require,module,__filename,__dirname){
2     //...
3     //  這裏就是我們在模塊中寫入的代碼
4     //...
5     return module.exports;
6 }

通過上面的推斷就可以得到下面這樣的結果:

1 console.log(module.exports == arguments[0]);//true
2 console.log(require == arguments[1]);//true
3 console.log(module == arguments[2]);//true
4 console.log(__filename == arguments[3]);//true
5 console.log(__dirname == arguments[4]);//true

通過執行這段打印代碼也確實可以得到這樣的結果,到這裏又有一個值得我們關注的內容,就是每個模塊的module參數:

 1 console.log(module);
 2 Module {
 3     id: '.',//當前模塊的id都是'.',在後面的parent和children裏面的模塊對象上的id就是的對應模塊的filename
 4     exports: {},//這裡是模塊導出對象
 5     parent: null,//這裡是當前模塊被那些模塊引用的模塊對象列表,意思是當前模塊作為那些模塊的父級模塊
 6     filename:'',//這裡是當前文件路徑的絕對路徑
 7     loaded: false,//模塊加載狀態,如果在模塊內部輸出module對象它永遠都會是false,因為只有這個模塊加載完成之後才會被修改成true
 8     children: [
 9         // 這裡是引用模塊module對象列表,意思是當前模塊作為了那些模塊的子模塊
10     ],
11     paths:[ 
12         // 這裡是外部模塊包的路徑列表,從最近的路徑(模塊所在同級路徑)到系統盤路徑所有的node_modules文件夾路徑
13      ] 
14     }

到這裡有可能你還會問為什麼底層實現裏面只有module.exports,沒有export,這個解釋起來真的費勁,下面這一行代碼幫你搞定:

let exports = module.exports;

這篇博客主要介紹了node模塊的內部內容,並未就node模塊基於commonjs規範做任何介紹,是因為在之前的博客中已經有了非常全面的解析,詳細參考博客開始時的連接,關於node模塊加載相關內容也是在那篇博客。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享