2019年3月11日 星期一

Web技術相關好文&重點整理

JS:

1.我知道你懂 hoisting,可是你了解到多深?
https://github.com/aszx87410/blog/issues/34

此文章從ECMAScript的規格去剖析hoisting的原理,非常不錯。以下節錄結論:
當我們在進入一個 EC 的時候(你可以把它想成就是在執行 function 後,但還沒開始跑 function 內部的程式碼以前),會按照順序做以下三件事:
1. 把參數放到 VO 裡面並設定好值,傳什麼進來就是什麼,沒有值的設成 undefined
2. 把 function 宣告放到 VO 裡,如果已經有同名的就覆蓋
3. 把變數宣告放到 VO 裡,如果已經有同名的則忽略

2. Microtasks/Macrotasks and their relations to Promises/Event
https://javascript.info/microtask-queue

這也是一篇很有趣的文章,網站整體的內容也很充實。重點在認識V8對於Promise和Event的處理順序,對於理解Node非同步很有幫助。可以幫助釐清promise chain中的非同步處理的順序。ex: 不同then裡面若各自有非同步的呼叫,並無法保證前一個then中的會先執行完,也就是說Promise chain在使用上,要注意then裡頭若是有非同步呼叫,(很有)可能會在這個then block結束後才完成,因此下一個then block不能相依於此then block的非同步處理結果

System Design:

1.搶購系統設計
https://codertw.com/程式語言/573527/

此文章深入探討搶票/搶購系統的架構設計,內容含括前/後端業務邏輯,也提出許多解決方案,值得參考。

ES6 features:

1. import/export Modules:
https://stackoverflow.com/questions/36795819/when-should-i-use-curly-braces-for-es6-import/36796281#36796281

扼要回答了ES6 Module的使用方式,也解釋了export default(預設導出/入)和 { A as B } ( named export renaming)的意涵。

React:

Life Cycle demo:
https://codesandbox.io/s/8k9k5zm060

一個簡單的demo,可以了解元件的生命週期,和state update的一些呼叫順序。

2019年1月29日 星期二

MongoDB document schema 設計思維入門

本文摘要自Thinking in Documents: Part 2.

此文章將介紹MongoDB的Document schema設計思維,包含如何用embedding及referencing來整理相關資料,也會介紹一點索引及MongoDB的transaction model。

定義你自己的Document Schema

首先,最重要的是你的app查詢資料的模式,也別忘記善用document model的彈性,意即其embedding和豐富的BSON based資料結構支援。

app資料存取的模式首要在以下幾點:

1. 資料庫的讀寫比(R/W ratio)
2. 資料庫所做的查詢指令及資料更新的類型(type of queries and updates)
3. 資料的生命週期以及document的成長速度

若你有關聯式資料庫的背景,可以先想像以下兩點:

1. 你會如何使用關聯式DB來實作這些操作。
2. 在MongoDB要怎麼實作。

另外也可以透過RDBMS的日誌來分析最常使用的查詢指令或是最常被共同查詢的資料,作為是否合併在單一document下的考量。

使用Embedding或Referencing來建立關聯

直接embed或是對其他collection的document做reference的時機並無絕對,但是還是有一些準則可供參考。

Embedding

對於一對一(one-to-one)或是一對多(one-to-many)的對應關係,很適合使用embedding模式,因為這些資料很可能本身就是依存在parent document之下的一種特性或是資訊。此時parent document就像是data owner或是container一樣。可以想像當parent document消滅,若該document將會失去意義的話,就是屬於這種類型。

若是需要被一起更動(update atomically)的資料,也應該使用此模式,詳細可參照下面關於Transaction model的說明。

然而,並不是所有的一對一或是一對多對應關係都適合embedding。舉例來說,以下的時機適合使用referencing:

1. 當一個document很頻繁被讀取,但是其內的embedded document卻幾乎不會被存取到。舉例來說,一個顧客紀錄document之中的年度總報告。由於年度總報告並不會被經常使用,嵌入它只會增加該collection消耗的記憶體。
2. 某一document其中一部分很頻繁被更新且不斷成長,但是剩下的部份卻相對沒有變動。
3. document size超過MongoDB目前的 16 MB限制。

Referencing

Referencing讓資料正規化(data normalization)變得可能,也比embedding有彈性。但是MongoDB伺服器卻需要追加查詢來解析關聯,因此會需要多次讀取動作,消耗較多時間。

實作上通常是在一個document中儲存另一個document的_id field當作參照,接著再由app執行查詢來取得參照的資料。

Referencing應該被用在:

1. 當使用embedding會造成資料冗餘(data duplication),無法期待效能向上時。
2. 物件從各種不同來源被關聯時。
3. 多對多(many-to-many)關係時。
4. 大型、階層式的資料集時。

不同的設計目標

從以上觀察我們可以發現RDBMS和document model的根本上差異:

1. RDBMS對資料的處理是以儲存空間效率來考量(因為早期儲存空間是系統中成本最高的元件)。
2. MongoDB的document model則是以app存取資料的效率為考量(因為開發者的時間和上線速度現在比儲存空間還要受重視)。

MongoDB Transaction Model

MongoDB提供了如關聯式DB的document-level ACID compliance,包含可以atomically update embedded arrays以及sub-document,且無須付出如RDBMS的耗時ACID operation,以及在不同table之間維護關聯完整性(referential integrity)。

Document-level ACID compliance保證在document改動時的隔離性(isolation),任何錯誤都會讓資料還原到操作前的樣貌,且用戶端也會收到原本的document view。

NOTE:在 MongoDB 4.0之後,加入了 multi-document ACID transactions來確保replica sets之間的資料是一致的,並確保任何執行都是「有或全無(all-or-nothing)」來維護資料完整性。在4.0版本之前,您還是可以透過findandmodify或是two-phase commit來達到一樣的目的。

如何找出我的資料?

MongoDB使用B-tree indexes,並原生支援secondary indexes。因此,SQL背景的人很快就能上手。不過索引也還是一樣會在寫入及資源使用上增加負擔,如同所有的資料庫。預設MongoDB會在document的 _id欄位建立索引,其他user-defined indexes都是secondary indexes。
任何欄位都能當作secondary index,包含在arrays中的欄位。在MongoDB中的索引有:

1. Compound Indexes
2. Geospatial Indexes
3. Text Search Indexes
4. Unique Indexes
5. Array Indexes
6. TTL Indexes
7. Sparse Indexes
8. Hash Indexes

另外,MongoDB也支援index intersection以使用多個索引做查詢。

2019年1月17日 星期四

How to clone an object in Javascript with Object.defineProperties?

TL;DR

Sometimes we want to clone an object in JS, we will use something like:


const obj = {foo:'bar'};
const newobj = {};
for (const key in obj){
    newobj[key] = obj[key];
}

This will work fine under most circumstances, since you might never adjust the property flags of the object. Also, when we create an object property, theare default to true, so everything is "default normal." And you might even not know there are such flags. Property flags, in brief, are metadata for the property in the object. They define the configuration of the property such as if the property is writable/enumerable/configurable.

Here are some simple explanation about the flags:

Writable: can reassign value or not.
Enumerable: can enumerate it with for...in loop or not.
Configurable: can change the flags shown above and this flag or not.

Okay, the problem with the above cloning code is that when someone changes the flags to non-default values, and you unconsciously clone the object without knowing those property flags. In a nutshell, if you want to clone an object along with the flags, please do as following:


let newobj = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

Here we use Object.defineProperties(object, descriptors) and Object.getOwnPropertyDescriptors(object) to accomplish this. The former will define properties for the objects in the first argument ( here is an empty object literal. ) with the property descriptors in the second argument ( here is the latter function getOwnPropertyDescriptors. ). However, although the code is cleaner, this method should be employed only when you really need to clone the property descriptors since it is slower and not straight-forward enough.

For more on Property flags and descriptors: