CMK

Let's create something!


  • Home

  • Categories

  • Tags

  • Archives

  • About

Houzz front end interview prep

Posted on 2019-12-15 | In Full stack | Comments:

I. Ways to improve website performance

  • Minimize HTTP Requests

    Too many external files in <head> will cause slow loading, we can

    • combine all *.js to a file, *.css to a file
    • use css spirit
  • CDN, putting static files over the world

  • Reduce image size

    1. change resolution
    2. compress the image
    3. crop the picture
  • Put <script> on bottom

  • use async and defer keywords to load js script

  • Add expires and Cache-control in response Header

  • Gzip

  • Put <style> on top, for progressive rendering

  • Minify JavaScript and CSS

  • Avoid Redirects

  • Reduce the Number of DOM Elements

  • Minimize DOM Access

  • Avoid Empty Image src

II. Cookie

HTTP is stateless. The server cannot recognize a visitor after he visits. But we have needs to be able to know the visitor when he visits next time:

  1. Personalization: next time we show him related info, based on his first visits
  2. Session management: after user login, we don’t let him keep logging in when access sensitive data
  3. Tracking: analyzing user behavior, like how many time clicks on which section on the page

III. Session

Session is server-side storage, used to store recent user information, in order to keep a live conversation. Work with

  1. Cookie: store the sessionId in cookie. Every browser bring this ID card when converse with server
  2. Database: if there is too much info, need to store info in Database, like Redis

VI. Cache vs Cookie

Although cookies and cache are two ways to store data on client’s machine, but there are difference between cache and cookies and they serve different purposes.

  1. Cookie is used to store information to track different characteristics related to user, while cache is used to make the loading of web pages faster.
  2. Cookies stores information such as user preferences, while cache will keep resource files such as audio, video or flash files.
  3. Typically, cookies expire after some time, but cache is kept in the client’s machine until they are removed manually by the user.

V. What happens when we visit Google.com?

  1. Check if cached already, if so, reload cache content directly
  2. If no cache, then convert the domain name to IP, recursive DNS look up:
    • browser cache
    • OS cache
    • router cache
    • ISP cache
    • recursive DNS lookup
  3. Browser get the IP, and wrap http payload inside TCP packet, and then to even lower layer, pass to router to the Server
  4. Build up the TCP connect
  5. Browser sends HTTP request to Server
  6. Server send HTTP response with meta info in header to Browser
  7. Browser receives the response header and content
    • store cookie, and cache css or js
    • render the DoOM tree, CSS-OMTree
    • run JavaScript
    • Create Render tree
    • generate Layout
    • painting
  8. Browser keep request external js/ css file from Server, keeps render

VI. Positioning of Elements

https://juejin.im/post/5b52dec0f265da0fa21a8242

https://zhuanlan.zhihu.com/p/25455672

  1. offsetTop
  2. scrollTop
  3. clientHeight

VII. Throttle & Debounce

https://juejin.im/post/5b52dec0f265da0fa21a8242

https://zhuanlan.zhihu.com/p/25455672

VIII. Algorithm

  1. 分解质因数,比如给一个数20
    输出:
    20 = 2 2 5
    11 = 11
    followup:
    输出 20 = 2^2 5
    followup2:
    反过来输出: 20 = 5
    2^2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    let t1 = 20
    let t2 = 7
    let t3 = 81
    let t4 = 0
    console.log(factors(t1))
    console.log(factors(t2))
    console.log(factors(t3))
    console.log(factors(t4))

    function factors(n){
    if(n===null || isNaN(n) || n===0 || !Number.isInteger(n))
    return Error("Valid Expected")
    if( n === 1 ) return 1

    let res = []

    while( n%2 === 0 ){
    res.push(2)
    n /= 2
    }

    for( let i = 3; i*i<=n; i+=2 ){
    while(n%i===0){
    res.push(i)
    n /= i
    }
    }
    if(n>1) res.push(n)

    let str = ''
    let cnt = 1
    let prev = res[0]
    for( let i = 1; i < res.length; i++ ){
    if(res[i] !== prev){
    if(cnt>1) str += prev+'^'+cnt+'*'
    else str += prev+'*'
    cnt=1
    prev = res[i]
    }else{
    cnt++
    }
    }
    if(cnt>1) str += prev+'^'+cnt
    else str += prev
    return str
    }
  2. 一个字符串的list,输出其中有anagram的字符串。类似leetcode 49,很快最好一遍编译过

    Followup: Print lists according to the input order

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    let test1 = ["eat", "tea", "tan", "ate", "nat", "bat"]
    let test2 = ["eat", "tan", "bat"]
    let test3 = []
    console.log(anagrams(test1))
    console.log(anagrams(test2))
    console.log(anagrams(test3))



    function anagrams(arr){
    let map = new Map()
    arr.forEach(item=>{
    let p = mapping(item)
    if(!map.has(p)){
    map.set(p,[])
    }
    let list = map.get(p)
    list.push(item)
    map.set(p, list)
    })

    let res = []
    map.forEach(val=>{
    res.push(val)
    })
    return res
    }

    function mapping(str){
    let res = new Array(256).fill("-")
    for( let i = 0; i < str.length; i++ ){
    let index = str.charCodeAt(i)
    res[index] = '+'
    }
    return res.join('')
    }
  3. Largest rectangle in histogram

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /**
    * @param {number[]} heights
    * @return {number}
    */
    var largestRectangleArea = function(heights) {
    if(heights === null || heights.length === 0) return 0
    if(heights.length === 1) return heights[0]

    let rightFirstSmallerIndices = FirstSmallerIndices(heights, true)
    let leftFirstSmallerIndices = FirstSmallerIndices(heights, false)

    let res = 0
    for(let i = 0; i < heights.length; i++){
    let idLeft = leftFirstSmallerIndices[i]
    let idRight = rightFirstSmallerIndices[i]
    res = Math.max(res, heights[i]*(idRight-idLeft-1))
    }
    return res
    };


    function FirstSmallerIndices(arr, right){
    let res = new Array(arr.length).fill(arr.length)
    let ids = new Array(arr.length).fill(0).map((_,id)=>id)

    if(!right){
    res = new Array(arr.length).fill(-1)
    ids = ids.map((_,id)=>arr.length-id-1)
    }

    let stack = []
    stack.push(ids[0])

    for(let i = 1; i<ids.length; i++ ){
    let id = ids[i]

    while(arr[stack[stack.length-1]] > arr[id]){
    let topId = stack.pop()
    res[topId] = id
    }
    stack.push(id)
    }
    return res
    }
  4. LC 85/ 845

    不一样的是in addition要给出长方形的坐标,例如左上角点的坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    /**
    * @param {character[][]} matrix
    * @return {number}
    */
    var maximalRectangle = function(matrix) {
    if(matrix === null || matrix.length === 0 || matrix[0].length === 0)
    return 0

    let res = 0
    let r = matrix.length
    let c = matrix[0].length
    for(let i = 0; i < r; i++){
    for( let j = 0; j < c; j++ ){
    if(i-1>=0 && matrix[i][j]==="1" ) {
    matrix[i][j] = Number(matrix[i][j])+Number(matrix[i-1][j])
    }else
    matrix[i][j] = Number(matrix[i][j])
    }
    res = Math.max(res, largestRectangleArea(matrix[i]))
    }

    return res
    };


    function largestRectangleArea(heights) {
    if(heights === null || heights.length === 0) return 0
    if(heights.length === 1) return heights[0]

    let rightFirstSmallerIndices = FirstSmallerIndices(heights, true)
    let leftFirstSmallerIndices = FirstSmallerIndices(heights, false)

    let res = 0
    for(let i = 0; i < heights.length; i++){
    let idLeft = leftFirstSmallerIndices[i]
    let idRight = rightFirstSmallerIndices[i]
    res = Math.max(res, heights[i]*(idRight-idLeft-1))
    }
    return res
    };

    function FirstSmallerIndices(arr, right){
    let res = new Array(arr.length).fill(arr.length)
    let ids = new Array(arr.length).fill(0).map((_,id)=>id)

    if(!right){
    res = new Array(arr.length).fill(-1)
    ids = ids.map((_,id)=>arr.length-id-1)
    }

    let stack = []
    stack.push(ids[0])

    for(let i = 1; i<ids.length; i++ ){
    let id = ids[i]

    while(arr[stack[stack.length-1]] > arr[id]){
    let topId = stack.pop()
    res[topId] = id
    }
    stack.push(id)
    }
    return res
    }
  5. fisher-yates shuffle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function shuffle(arr){
    let n = arr.length
    while(n>0){
    let randId = Math.floor(Math.random() * n)
    let temp = arr[randId]
    arr[randId] = arr[n-1]
    arr[n-1] = temp
    n--
    }
    return arr
    }

    shuffle([1,2,3,4,5,6,7,8,9])
  6. LC 304 range sum query - immutab

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    class NumMatrix {

    private int[][] dp;

    public NumMatrix(int[][] matrix) {
    if( matrix == null
    || matrix.length == 0
    || matrix[0].length == 0 ){
    return;
    }

    int m = matrix.length;
    int n = matrix[0].length;

    dp = new int[m + 1][n + 1];
    for(int i = 1; i <= m; i++){
    for(int j = 1; j <= n; j++){
    dp[i][j] = dp[i - 1][j] + dp[i][j - 1] -dp[i - 1][j - 1] + matrix[i - 1][j - 1] ;
    }
    }
    }

    public int sumRegion(int row1, int col1, int row2, int col2) {
    int iMin = Math.min(row1, row2);
    int iMax = Math.max(row1, row2);

    int jMin = Math.min(col1, col2);
    int jMax = Math.max(col1, col2);

    return dp[iMax + 1][jMax + 1] - dp[iMax + 1][jMin] - dp[iMin][jMax + 1] + dp[iMin][jMin];
    }
    }

    /**
    * Your NumMatrix object will be instantiated and called as such:
    * NumMatrix obj = new NumMatrix(matrix);
    * int param_1 = obj.sumRegion(row1,col1,row2,col2);
    */
  7. LC 380 insertDeleteGetRandomO(1)

  8. LC 162 find peak element

  9. // Given multiple lists, each list contains few integer numbers

    // We want to get all the possible combinations within those lists, with the following condition:

    // one combination should contain one and at MOST one element from each list

// Example: input => [[1,2], [3,4]]

// output = [[1, 3], [1, 4], [2, 3], [2, 4]] // 8

要求用两种方法,都先排序,一个是用Index判断去重,还有一个是用set判断去重

问了时间空间复杂度,最好和最坏情况

  1. 输入”rft567.908kih000000hh890jug678gtff567”这样包含字母和数字的字符串,找到出线次数最多的数字,print “567 shows two time”。

  2. asdbf-12.456.1asdf12.343.232这样的话,应该返回什么呢?

  3. matrix binary search 用两个二分和一个二分分别实现了下,时间复杂度都是O(logm + logn),

  4. 打印树,给一个节点,打印出树的graph

  5. longest increasing subsequence

  6. (2019) Minimum window subsequence

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * @param {string} S
    * @param {string} T
    * @return {string}
    */
    var minWindow = function(S, T) {

    let minLen = S.length + 1
    let minLeft = 0

    let i = 0, j = 0
    while( i < S.length ){
    if( S[i] === T[j] ){
    if( j===T.length-1 ){
    let end = i
    while( j >= 0 ){
    while( S[i] !== T[j] ) i--
    i--
    j--
    }
    i++
    if(minLen > end-i+1){
    minLen = end-i+1
    minLeft = i
    }
    }
    j++
    }
    i++
    }
    return minLen===S.length + 1 ? '' : S.slice(minLeft, minLeft+minLen)
    };
  7. LCA of deepest leaf nodes in a binary tree

  8. 8位String的生成器,要求在A-Z,a-z,0-9,%$&#里挑,每组至少选一个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    /*
    1. which digit use which group of character
    2. a method to randomly get a char from its group
    3. fasher-yate shuffle
    */

    const UPPERCASE = new Array(26).fill(0).map((_,index)=>String.fromCharCode(index+65))
    const LOWERCASE = new Array(26).fill(0).map((_,index)=>String.fromCharCode(index+97))
    const NUMBER = new Array(10).fill(0).map((_,index)=>index)
    const SYMBOL = new Array(14).fill(0).map((_,index)=>String.fromCharCode(index+33))

    const ARRAY = [UPPERCASE, LOWERCASE, NUMBER, SYMBOL]
    function generate (n){
    let res = []
    for(let i = 0; i < ARRAY.length; i++)
    res.push(getOne(ARRAY, i))

    for( let i = 0; i < n-ARRAY.length; i++ ){
    let type = Math.floor( (Math.random() * ARRAY.length) )
    res.push(getOne(ARRAY, type))
    }
    return shuffle(res).join('')
    }

    function getOne(ARRAY, type){
    let len = ARRAY[type].length
    let rand = Math.floor(Math.random()*len)
    return ARRAY[type][rand]
    }

    function shuffle(arr){
    let n = arr.length
    while(n>0){
    let rand = Math.floor(Math.random*n)
    let temp = arr[rand]
    arr[rand] = arr[n-1]
    arr[n-1] = temp
    n--
    }
    return arr
    }

    console.log(generate(8))
  9. (2019)列车时刻表
    A B 1000 1130 (A 到 B,10:00出发, 11:30到达 )
    B C 1200 1230
    A C 1100 1250

    求 A 到 C: 1) 最早到达的时间,输出大概是“A C 1000 1230”。 2)若有多条线路同时到达,求最晚出发的列车schedule,输出格式和第一问一样。

    https://www.1point3acres.com/bbs/thread-460017-1-1.html

  10. 基本上跟 LC 上面 walls and gates 很像,但是有一个agent (起点)和 several goals (终点) 问能不能从一个起点到达所有终点。
    矩阵类似这样:
    0 0 0 1 1 1
    A 1 0 1 G 1
    1 0 G 0 1 1
    1 0 0 1 0 1

    我用recursion写出来然后各种检查typo。最后还是被白人小哥嘲笑worlds打成world。
    还有我更改了矩阵内容0->1,被不允许

  11. (2018-12-6) sqrt(n),最开始我说用brute-force从0到result找,然后刚想说优化用binary search,面试官就表示,如果我的输入是double,怎么办?所以最终的问题变成sqrt(double n, double delta),需要输出一个在[result-delta, result+delta]范围内的任意一个值,然后其实还是binary search来做。最后还有个bug,这里处理得不是很好,感觉面试官有点儿不高兴,唉。。。然后分析了下时间复杂度,问了问问题,就结束了。。

  12. (2018-12-6)题目是,给两个二叉树s和t,需要判断t是否是s的某一个subtree。
    先说了brute-force的方法,然后分析了一下时间复杂度,然后开始优化,当时直觉是用inorder-traverse转化成string来做,但是最开始中间还是有bug,例如
    ​ 1 2
    ​ / \ \
    2 3 和 1, 如果仅仅考虑inorder的情况,显然是不对了。

    幸好大哥给了点提示,然后顺利写出代码,中间有些小bug,在跑了俩test case后就知道问题所在,然后改完就ok了。大哥人真的很nice,面试体验很棒,后面还跟我介绍了很多他们组的情况以及houzz。

  13. top k visited website this day

    this is typical top k . you can create a list of {3, [web1, web2] } , { 2, [web 3 , web 4] } 3 stands for the visited time and web 1 & web 2 stands for the websites . and then you can do the traversal to find the proper results. thanks

  14. 完全背包, 刚开始理解成了01背包,后来发现他想要完全背包, 反正两种都写了

  15. 给出一个arr, 如[0,1,0,1,0],每个1就是一个发电厂,然后给出一个range,比如是2,就是说每个发电厂能cover前后range为2的距离,然后求需要的发电厂的最少数。e.g. [1,1,0], range 为 1 -> [0,1,0]

    给的arr里面有1和0,但是最后输出的arr 要把其中某些1变成0,(不知0能否变成1?)从而达到最少数的发电厂

    http://www.cs.toronto.edu/~jepson/csc373/tutNotes/cellTower.pdf

    Course Schedule III

    https://leetcode.com/problems/heaters/description/

  16. LC 65 Valid Number

    valid number那道题
    “12.3459” -> 12.3459
    “ 12.3459 “ -> 12.3459
    “12.3 459” -> Exception
    “1 2.3459” -> Exception
    “12.34x59” -> Exception
    “12.34.59” -> Exception
    e.g. “+.34” -> 0.34
    e.g. “-1.234” -> -1.234这题感觉之前面经没一个说明白详细的。第一,只能扫一遍,trim都不可以用,第二,不能用valueOf这种函数,parse转化不可以。也就是说,只能硬扫记录硬来,不能用stringbuilder去存去做。这题面之前就写了,但没想到限制的这么硬,虽然和lc65差了个e,但难度基本一样。

  17. design calendar class 有 day year month 实现 add (days) return calendar、、

  18. add(num1, num2) 不能用加号。

  19. Merge k sorted arrays

  20. Design Twitter

  21. find median of two sorted array,

  22. LC 85 Maximal Rectangle,一个变化是要求最后返回一个maximal rectangle的identifier。 (需要给用到stack的那个解法 )

  23. 设计题,要求返回twitter在过去24小时里最popular的10个单词(任何string)

  24. String Permutation I, Follow Up String Permutation II.
    都秒了,

  25. Longest Incresing Subsequence.

  26. 强制用JavaScript写一个方法,把一句话中所有的word转换成第一个字符大写的word

  27. LC 227 但是有括号也要考虑。

  28. print a given source to a destination

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    let adjList = [
    [1,2,3],
    [3],
    [0,1],
    []
    ]

    let s = 2, t = 3
    function getAllPaths(adjList, s, t){
    let res = []
    let visited = new Set()
    dfs(s, [])
    return res

    function dfs(nodeId, prefix){
    if(nodeId === t){
    res.push([...prefix, nodeId])
    return
    }

    let children = adjList[nodeId]
    visited.add(nodeId)
    children.forEach(item=>{
    if(!visited.has(item))
    dfs(item, [...prefix, nodeId])
    })
    visited.delete(nodeId)
    }
    }

    console.log(getAllPaths(adjList, s, t))
  29. traverse the graph

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    let adjList = [
    [1,2,3],
    [3],
    [0,1],
    []
    ]
    let s1 = 0
    // console.log(traverse(adjList, s1))
    let s2 = 2
    // console.log(traverse(adjList, s2))

    function traverse(adjList, s){
    let visited = new Set()
    let res = []
    dfs(s)
    return res

    function dfs(n){
    res.push(n)
    visited.add(n)

    let children = adjList[n]
    children.forEach(item=>{
    if(!visited.has(item)){
    dfs(item)
    }
    })
    }
    }
  30. 数字恩,假设这是一个 恩乘恩 的正方形单元网格,给一个圆,半径以及圆心点坐标,求有多少小格子完全在园内? 不依不饶: 有没有 欧恩的解法?

    根据圆的坐标很容易找到第一个格子,其部分顶点包含在园内,部分在圆外。以这个格子为起始做dfs,找出所有类似的格子,(这些格子全在圆的边界上)。那么所有有相同x坐标的格子,y坐标差减一就是这两个格子之间一定在园内的格子数。这样traverse by x坐标,累积的y坐标差应该就是在园内的格子数。
    欢迎大家补充更优解!!

    圆心,半径都是浮点数。我觉得楼上那位同学说的比我的解法好。扫描经过圆心的直径,根据纵坐标算夹在圆内的格子,这样应该是O(n), 比如,圆心是x = 3.4, y = - 3 半径 5.5, 那么从, Math.max(0, ceilling(x - r)) 一直扫描到 Math.min(n - 1, floor(x + r)). 对每个 x’, x’ + 1 算有多少格子在园内,格子数的和即为最后园内格子数。

    (x - 3.4)^2 + (y + 3)^2 = 5.5^2 带入 x’ 以及 x’ + 1, 得到y的上界yUp y’Up,下界 ydown, y’down, 格子数就是,Floor(Math.min(yUp, y’Up, n)) - ceilling(Math.max(ydown, y’down, 0)),

    Shell_Smith 发表于 2018-1-18 09:01
    不是,如果说n是1的话,那么就有(0,0)(0,1)(1,0)(1,1)四个点围成一个正方形,这个正方形所有 …
    假设圆心是(x0, y0),半径是r的话,把圆分成左右两半分别计算即可:
    对于右边一半,遍历 x = x0 + 1, …, x0 + r,对每个x,计算对应的圆上的y值 yt 和 yb,那么横坐标是x且在圆内的点的个数是 floor(yt) - floor(yb),一旦x到网格外了就停止
    对于左边一半同样计算
    复杂度是 O(N)

  31. quicksort & merge sort

  32. graph是不是bipartite的,bfs解完了,接下来问时间复杂度,可不可以优化。
    https://www.1point3acres.com/bbs/thread-286531-1-1.html

  33. 一个unfair coin,怎么disign一个process可以返回同样几率的0和1.

  34. Calculate the distance of bits between two integers.
    For example: 1 -> 01; 2 -> 10; dist = 2;

  35. Fibonacci, recursive + non recursive;

  36. file path “/home/./../user/file/.././“大概这样 .表示不动,..表示去上层,要求返回简化的路径,”/user”,stack做

  37. calendar add day

    Calendar class(有三个field,year,month,day)和一个数N,实现一个函数,返回N天之后的calendar,譬如说输入是{2017, 3,20}和12, 那么返回的是{2017,4, 1}

  38. excel implementation

  39. Given a DAG, a source, a target, and number n, 找出从source到target的长度为n的路径的个数。

  40. given a undirectd and acyclic graph, 一个source和一个target,找出source到target的最长path

  41. 是之前面经里有过的一个二维数组最后一个格子是个moving block的那题。应该要用a star算法。。我刷面经的时候就想说要是考到这题我就放弃。。因为之前研究了半天还是觉得太复杂,搞懂了感觉一个小时也写不出来。。结果好死不死的考到了=。 =当然我是没有放弃的,强行用DFS解了下,但是空间复杂度会很高,小姐姐说最多只能跑个2*2的试试,DFS的话就是你每走一步就把当前棋盘的状态存到一个hashset里,然后要是走到相同状态就不再走下去。反正最后写了100多行跑了个最简单的用例但是感觉有loop,也没时间debug了。

  42. find max number of points in one line + 系统设计(设计一个topic ranking 系统)

  43. 有向有环图中 找source 到 target 的距离为n的可能性

  44. wordbreak 2 (follow up : NLP problems)

XI. SQL

  1. select count distinct

  2. query要用group by, 然后让我写一个方法,输入income和taxTable可以得出tax. 不是算法题,当时我都震惊了,就是分段函数求和。小哥说就想看下我会不会写code。。估计前面的问答让他对我的能力深感怀疑。。

  3. 给了个实际例子 要我写了个简单的3个entity的table(one to many & many to many)一个USER有很多ALBUM, 很多PHOTO对应很多album
    写SQL: 给一个用户,这个用户有几张照片?
    每个相册分开呢?

最后问了几个SQL的问题:

  1. 有user,user有photo,怎么建表,大概写一下
  2. 写一个SQL查每个user有多少照片

Ember.js Study Notes

Posted on 2019-09-21 | In Full stack | Comments:

I am always curious about how do those libraries/frameworks like React, Angular, Ember work in background. I know it all about design patterns, which appeals me very much.

Now, I have been working on React for about 2 years, and AngularJS for some time. It’s time to start an adventure in Ember world. Items below are my thoughts while I went through the official documentation, and will be kept updating.

  1. Ember framework/ecosystem behaves like a custodian, it

    • takes template, component, service, data, route, etc defined by developers, via interfaces.
    • ditches them together in background

    While React is more like a library, it works together with other library like Redux, React Router, Webpack to build up applications.

  2. Ember CLI can deal with file system, create scaffolds based on convention over configuration

  3. this in hbs:

    1. Each template / component is an object / instance
    2. what does this refer to? / what is the context?
    3. why not using function programming, getting rid of this / context?
  4. .hbs: will be pre-compiled before it converted into valid html?

    1. Compilation workflow?
      • only handler-bar syntax will be scanned
      • other valid html syntax will be omitted
      • valid html generated
  5. Ember’s rendering engine

  6. render lifecycle

  7. almost all functions are injected into Ember, if they are intended to used in another file.

    • eg.: helper functions will be in handlebar file, are injected by helper()
  8. function invokation in hbs: function-name arg1 arg2, will be executed as function-name([arg1, arg2])

  9. Router => Route [modal + template] => template with its model (consist of resuable components)

  10. Ember Data is data management lib, in charge of API call and other data massages

  11. .extend(): use Ember Component class/interface; Meanwhile, add customed function / data

    1
    2
    3
    4
    Component.extend({
    isBig: false,
    productList: []
    })
  12. A component consists of two parts:

    • UI part: .hbs file (app/templates/components/list-filter.hbs)
    • Logic Part: component object (app/components/list-filter.js)
  13. A template can also have its own logic part: controller object

    1. UI part: .hbs file (app/templates/rentals.hbs)
    2. Logic Part: controller object (app/controllers/rentals.js)
  14. Ember.Object

    1. expand properties and methods on original JS object

    2. make it observable (design pattern), used heavily in Ember env system

    3. Issue: create() to instantiate a object with preset properties, using init() at the same time

      1. Problem: preset properties and init properties which one called first?

        1. properties in init beats instance preset properties
        2. the hash passed to the create method is essentially treated as a mixin that is applied before init is run
        3. My early exploration encounterring such issue: seem like this is a good sign, which give me an impression this framework is not with a simple philosophy. But hold on, let me dig on.
      2. How to overwrite init in reopen?

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        const Person = Ember.Object.extend({
        init() {
        this.set("shoppingList", Ember.A(['eggs', 'cheese']));
        },
        addItem(item) {
        this.shoppingList.pushObject(item);
        }
        });

        Person.reopen({
        init() {
        this._super();
        this.set("age", 10);
        }
        })

        let p1 = Person.create({
        name: 'Stefan Penner',
        });

        let p2 = Person.create({
        name: 'Robert Jackson',
        });

        let p3 = Person.create({
        name: "a",
        age: 199,
        kkk: function(){console.log("333")}
        })

        console.log(p1.age);
        console.log(p2.age);
        console.log(p3.age);

        p3.set('age', 3333)
        console.log(p3.age);

Evolve your app to MVC pattern

Posted on 2019-08-31 | In Full stack | Comments:

1. What is it?

MVC is a pattern/framework decouple application into 3 parts: modal, view, and controller.

  • Modal: the single source of truth of your app, responsible for data input and data output;
  • View: the UI part displayed to users, responsible for display;
  • Controller: bind views to modal organically in a desired way, responsible for handling Modal(automatically resulting in View update)

[Note]: MVC is based on Observer Pattern

2. Why use it?

As app grow, the business logic between data and view become complicated. There could be a good chance that several component use same piece of data. It would be painful to call vanilla JS to update every component one by one.

It would be better if there is a way that we can update all those components in a batch automatically when that data piece changes. Here come in the MVC pattern.

  • View is html code snippet actually on frontend;
  • Modal defines all single source of true for their views
  • Controller will
    • binds all Views to their corresponding Modal, so that Views will update automatically when the Modal changes.
    • via callback passed in, it updatesModal, leading to Views get updated

3. How to use it?

The general way:

  1. Declare modal class prototype, and its APIs, which will be called by controller
  2. Specify to-be-bound modal for view components
  3. pass intention callback tocontroller to specify how to update modal

4. Evolve to MVC pattern (eg: Timer)

We will implement a very simple widget to show how to evolve an app to MVC pattern. The widget is to display whatever we get from API or from callback, and show it inside a div.

  1. No Pattern

    1
    2
    3
    4
    5
    6
    7
    // View Part:
    <div id="div1"></div>

    // Logic Part:
    const data = "This is the data"
    const div = document.getElementById('div1');
    div.innerHTML = data;
  1. Using Observer Pattern

    Now we don’t want to update view by ourselves. Instead, we hope data update will trigger view change automatically. Observer Pattern can be used here to let the view observe the modal, so whenever modal changes, view will get updated automatically.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
    }
    Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    // model value change will notify regisered callbacks
    // According to JS running principle, we apply callback asynchronously: WON'T Freeze UI
    // or use requestAnimationFrame, rather than setTimout
    setTimeout(function () {
    self._listeners.forEach(function (listener) {
    listener.call(self, value);
    });
    });
    };
    Model.prototype.watch = function (listener) {
    // register callback
    this._listeners.push(listener);
    };

    // View:
    <div id="div1"></div>

    // Logic:
    (function () {
    var model = new Model();
    var div1 = document.getElementById('div1');
    model.watch(function (value) {
    div1.innerHTML = value;
    });
    model.set('hello, this is a div');
    })();
  1. Bind View and Modal

    There is a drawback in the above approach: we need to make modal to watch each related views manually. But the truth is that all those work are duplicated, and conveys the same functionality: bind views to modal, or let modal watch related views.

    We can encapsulate this feature onto modal actually, as follows:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Model.prototype.bind = function (node) {
    // put `watch` and `common callback` inside bind
    this.watch(function (value) {
    node.innerHTML = value;
    });
    };

    // View:
    <div id="div1"></div>
    <div id="div2"></div>

    // Logic:
    (function () {
    var model = new Model();
    model.bind(document.getElementById('div1'));
    model.bind(document.getElementById('div2'));
    model.set('this is a div');
    })();
  1. MVC Pattern

    Now, it is better. We bind view and modal😄~ And we can bind as many as views to a modal as we want😱.

    But still, we need to manually bind view to modal. Is there a way our code can handle it for us, as long as we tell the code which modal the view want to bind.

    Yeah, You might already get it! We can specify modal piece for the view by using HTML attribute, like <div bind='modal1' />. Thus, the code can know the desired modal current view wants to bind.

    We will create an util object that can

    • firstly, automatically bind views to their modals,
    • secondly, receive a callback to update modal in programmer’s desired way
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function Controller(callback) {
    var models = {};
    // find all views with `bind` attr
    var views = document.querySelectorAll('[bind]');
    // convert views into array
    views = Array.prototype.slice.call(views, 0);
    views.forEach(function (view) {
    var modelName = view.getAttribute('bind');
    // get the modal for this view, or create new one for it
    models[modelName] = models[modelName] || new Model();
    // bind view and its modal
    models[modelName].bind(view);
    });
    // update modals using specified callback
    callback.call(this, models);
    }


    // View:
    <div id="div1" bind="model1"></div>
    <div id="div2" bind="model1"></div>
    // Controller:
    new Controller(function (models) {
    var model1 = models.model1;
    model1.set('this is a div');
    });
  2. The whole MVC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
    }
    Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    setTimeout(function () {
    self._listeners.forEach(function (listener) {
    listener.call(self, value);
    });
    });
    };
    Model.prototype.watch = function (listener) {
    this._listeners.push(listener);
    };
    Model.prototype.bind = function (node) {
    this.watch(function (value) {
    node.innerHTML = value;
    });
    };
    function Controller(callback) {
    var models = {};
    var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0);
    views.forEach(function (view) {
    var modelName = view.getAttribute('bind');
    (models[modelName] = models[modelName] || new Model()).bind(view);
    });
    callback.call(this, models);
    }

5. Test our MVC framework

We will use our MVC framework to implement a Timer as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// View:
<span bind="hour"></span> : <span bind="minute"></span> : <span bind="second"></span>

// Controller:
new Controller(function (models) {
function setTime() {
var date = new Date();
models.hour.set(date.getHours());
models.minute.set(date.getMinutes());
models.second.set(date.getSeconds());
}
setTime();
setInterval(setTime, 1000);
});

6. Thoughts

For almost all frameworks like Redux, Flux. It use similiar ways to handle View and Modal. Here we use Observer Pattern to implement MVC.

Based on my understanding, Redux and Flux use Publisher-Subscriber Pattern will handles events better than Observer Pattern.

7. Reference

  1. 30行代码实现Javascript中的MVC
  2. Design Pattern - JS
  3. Learning JavaScript Design Patterns

HOC & Redux connect()

Posted on 2019-08-29 | In Full stack | Comments:

1. HOC

Frontend people are familiar with HOC(High Order Component) or HOF(Higher Order Function). It can be thought as as an enhanced version of the original Component. Recently, I read an article about mixin pattern drawbacks in React (NOTE: mixing pattern now is obsolete, and HOC are recommended by Facebook React Team).

Personally, I love the way React Doc delivers its concepts, which is very natural and follow the way of regular thought process. Do have a look at this part: Higher-Order Components Explained. Thanks for the efforts from React Team.

2. connect() from Redux

In React-Redux, we see connect()() a lot, actually it is a HOC. Let’s write one! Also note this.context actually passed from <Provider> created by Redux. And it use context in React to pass it to its children.

[NOTE] The return of connect() is: a wrapper function that takes your component and returns a wrapper component with the additional props it injects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object
}

constructor () {
super()
this.state = {
allProps: {}
}
}

componentWillMount () {
const { store } = this.context
this._updateProps()
store.subscribe(() => this._updateProps())
}

_updateProps () {
const { store } = this.context
let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props): {}
let dispatchProps = mapDispatchToProps? mapDispatchToProps(store.dispatch, this.props) : {}
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}

render () {
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}

3. Reference

  1. Use HOCs For Cross-Cutting Concerns

  2. Mixins Considered Harmful

  3. Mixin to HOC to Hook

Front-end Notes

Posted on 2019-08-10 | In Full stack | Comments:

1. Array.prototype.sort(cb)

Always pass in comparison callback, as the default of sort() behavior is:

  • first convert to string type
  • then, sort string array

2 examples are given below:

1
2
3
4
5
6
let array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1); //BAD: Array [1, 100000, 21, 30, 4]

array1.sort((a, b) => a - b);
console.log(array1); //GOOD: Array [1, 4, 21, 30, 100000]

2. NO map or forEach on String

String type is array-like data type, and immutable, no prototype method like map(), forEach(), filter(), reduce(). BUT, like other array-like data type, we can use index to access element: str[i]

1
2
3
4
5
6
7
8
9
10
11
const str = "abcde";

// GOOD
str[k]

// BAD
str.map(c => c)

// GOOD
Array.from(str).map(c => '[' + c + ']');
str.split().map(c => '[' + c + ']');

3. oninput vs onchange

  • oninput will trigger every time there is a modification inside <input>, textarea, etc.
  • onchange won’t necessarily trigger for each modification; but will do when Enter pressed or focus lost
  • inside <input>, <textarea>, holding non-whitespace key without release won’t result in letter repeatition, instead, there will be only one letter types in.

4. Don’t use a number returned comparator directly

Because 0 is falsy, we should use a boolean returned comparator instead, as follow:

1
2
3
4
5
6
7
8
9
10
11
// BAD
const comparator = (a, b) = (a - b);
if (comparator) {
...
}

// GOOD
const comparator = (a, b) = (a - b > 0);
if (comparator) {
...
}

5. Event

  1. Event delegation

    1. GOOD:
      • Less memory to register event handlers, saving memory
      • Dynamically handle element event, eg.: newly added <li> can benefit from its parent <ul> to handle its own event
      • no memory leak: no need to removeEventListener() from a deleted <li>
    2. BAD:
      • Too long time for some event to be handled if the delegation process is too long
      • Some event might be stopped by event.stopPropagation()
      • Some event like onFocus, onBlur don’t support event delegation
    3. NOTE: need to check if event.target === desiredEle to respond.
  2. Default actions

    Event handlers are called before the default behavior happens. Eg.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 
    link tag <a> won't redirect, because
    1. event handler is called
    2. event handler prevent the default behavior of <a>
    -->
    <a href="https://developer.mozilla.org/">MDN</a>
    <script>
    let link = document.querySelector("a");
    link.addEventListener("click", event => {
    console.log("Nope.");
    event.preventDefault();
    });
    </script>
  3. Key events: keydown is enough)

    • cover all keys, while keyup only cover part of keys.

    • has default action and is cancelable, by using preventDefault; While keyup has no default action, and is not cancelable.

      1
      2
      3
      4
      5
      6
      7
      // 1. e.keyCode
      // 2. document.activeElement: the focused Element within the DOM
      document.onkeydown = function(e) {
      if(e.keyCode === 13) { // The Enter/Return key
      document.activeElement.onclick(e);
      }
      };
  • compatible for all browser

  • keep firing when key is held (useevent.repeat to avoid)

    1
    2
    3
    4
    5
    6
    7
    document.addEventListener('keydown', (event) => {
    if(event.repeat) {
    // key is being held down
    } else {
    // key is being pressed
    }
    });

6. How does new work when instantiate an object?

credit_1: https://segmentfault.com/a/1190000017956545

credit_2: https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c

The new operator is used before a function, like new Foo(…). When the code is executed, the following things happen:

  • An empty object is created: obj;

  • Foo is called on obj, so now this refer to obj;

  • obj inherit from Foo.prototype

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function Foo() {        
    this.name = 'Osama';
    this.say = function () {
    return "I am " + this.name;
    };
    }

    let obj = new Foo();

    // The statement above is equal to [1][2][3] below:
    // [1] create a new object using the object literal
    // let obj = {};

    // [2] add instance properties and methods to `this`
    // Foo.call(obj);

    // [3] add proto property to `this`
    // obj._proto_ = Foo.prototype;

    console.log(obj.name); //Osama --- [3]

7. global/window: default one, if no obj set to this

If there is no calling context, this will be bound to global or window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const context = "global";

const obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context, so it will default to `window`
}
};

console.log(obj.method()); //[object Window]:global

8. Foo.prototype.method=... vs Foo.prototype={...}

Use Foo.prototype.method=function () {...}, rather than Foo.prototype={...}.

  • The 1st approach didn’t modify Foo.prototype.constructor, so it is still Foo;
  • However, the 2nd one modify it to be Object, which is not disired.
  • Example below can be
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// [Credit]: https://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor

// 1. define the Person Class
function Person(name) {
this.name = name;
}

Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};

// 2. define the Student class
function Student(name) {
Person.call(this, name);
}

// 3. inherit Person
Student.prototype = Object.create(Person.prototype);

// 4. Now what happens when we create a new Student and copy it?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false

// 5. The copy is not an instance of Student. This is because (without explicit checks), we'd have no way to return a Student copy from the "base" class. We can only return a Person. However, if we had reset the constructor:

// 6. correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true

9. AMD vs. CommonJS vs import/export

credit relavant blog

  1. CommonJS: require & module.exports (exports) relavant blog

    require is a global utility function (obj), with inner cache storing a map <filename, modulePublicAPI> , by firstly executing module functions, and then exporting module API with the help of Closure

    • get the code string of this module;
    • create empty hooks to link potential public interface
    • declare wrapper function
    • call wrapper function to execute module, and expose public interface to module
    • store module public interface in require util obj
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // https://caomingkai.github.io/2018/10/02/Note-of-Eloquent-JavaScript/
    require.cache = Object.create(null);
    function require(fileObj) {
    if (!require.cache[fileObj.name]) {
    let functionStr = fileObj.functionStr; // 1. get the `code string` of this module
    let module = {exports: null}; // 2. create empty hooks to link potential public interface
    const wrapper = Function('require', 'module', functionStr); // 3. declare wrapper function
    wrapper(require, module); // 4. call wrapper function to execute module, and expose public interface to `module`
    require.cache[fileObj.name] = module.exports; // 5. store module public interface in require util obj
    }
    return require.cache[fileObj.name];
    }


    /* TEST:
    `fileObj` simulates a file with name as `a`, and content as `functionStr`
    */
    let fileObj = {
    name: "a",
    functionStr: "let a = 0; function increA() {a += 1;}; function getA(){return a;} module.exports={increA, getA};"
    }

    function test(){
    console.log("require module for 1st time------>");
    let module = require(fileObj);
    console.log(module.getA()); // 0
    module.increA();
    console.log(module.getA()); // 1
    module.increA();
    console.log(module.getA()); // 2
    module.increA();
    module.increA();
    module.increA();
    console.log(module.getA()); // 5


    console.log("require module for 2nd time------>");
    module = require(fileObj);
    console.log(module.getA()); // 5
    module.increA();
    console.log(module.getA()); // 6
    }

    test();
  1. AMD (Asynchronous Module Definition )

    Credit_1

    Credit_2

  2. import & export

    Credit1

10. When to use document.write ?

document.write only works while the page is loading; If you call it after the page is done loading, it will overwrite the whole page.

The following is an legitimate use of document.write comes from the HTML5 Boilerplate index.html example.

1
2
3
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if offline -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.3.min.js"><\/script>')</script>

11. HTML attribute vs DOM property

Attributes are defined by HTML. Properties are defined by the DOM (Document Object Model).

  • A few HTML attributes have 1:1 mapping to properties. id is one example.
  • Some HTML attributes don’t have corresponding properties. colspan is one example.
  • Some DOM properties don’t have corresponding attributes. textContent is one example.
  • Many HTML attributes appear to map to properties … but not in the way you might think!

Attributes initialize DOM properties and then they are done. Property values can change; attribute values can’t.

For example, when the browser renders <input type="text" value="Bob">, it creates a corresponding DOM node with a value property initialized to “Bob”.

When the user enters “Sally” into the input box, the DOM element value property becomes “Sally”. But the HTML value attribute remains unchanged as you discover if you ask the input element about that attribute: input.getAttribute('value') returns “Bob”.

The HTML attribute value specifies the initial value; the DOM value property is the current value.

12. document.load() vs document DOMContentLoaded()

  • DomContentLoaded fires after domcument is created; But css files,images and frame are not yet loaded
  • load fires after all resources are loaded.

13. When to usePromise.resolve() ?

refer this article

Promise.resolve() will immediately change the status and broadcast the value, skipping the execution of executor. The consumers registered in then() will be invoked immediately.

So, we can use promise.resolve() , if we wanted to invoke consumers registered in then(), and don’t care the executor.

14. CSS preprocessor

Why use it?

  • variables:

    font-size: $fontSize;

  • nested selector: clear structure and less redundant selectors

    1
    2
    3
    4
    5
    6
    7
    8
    9
    nav {
    ul {
    margin: 0;
    padding: 0;
    list-style: none;
    }

    li { display: inline-block; }
    }

    will be converted to CSS as follows:

    1
    2
    3
    4
    5
    6
    7
    8
    nav ul {
    margin: 0;
    padding: 0;
    list-style: none;
    }
    nav li {
    display: inline-block;
    }
  • @mixin and @include: make groups of CSS declarations that you want to reuse throughout your site

    [Best Practice:] use a mixin to inject dynamic values into repeated constructs

    1
    2
    3
    4
    5
    6
    @mixin transform($property) {
    -webkit-transform: $property;
    -ms-transform: $property;
    transform: $property;
    }
    .box { @include transform(rotate(30deg)); }

    will be converted to CSS as follows:

    1
    2
    3
    4
    5
    .box {
    -webkit-transform: rotate(30deg);
    -ms-transform: rotate(30deg);
    transform: rotate(30deg);
    }
  • inheritance selector: %name and @extend: share a set of CSS properties from one selector to another;

    [Best Practice:] Only use @extend when the rulesets that you are trying to DRY out are inherently and thematically related

15. HTML Accessibility

[Why need it]:

1
2
3
4
1. not only for <u>people with disability</u> (better experience), 
2. but also for <u>device with disabilities</u> (better rendering)

ARIA: Accessible Rich Internet Applications
  1. semantic HTML. Validating it is a good start, as is using an Auditing tool.
  2. Check that your content makes sense when the CSS is turned off.
  3. keyboard accessible. Test using Tab, Return/Enter, etc.
  4. text alternatives. <img alt="Mingkai's Photo />"
  5. element relationships. <label for="name">Fill in your name:</label> <input type="text" name="name />"
  6. color contrast
  7. hidden content is visible by screenreaders: Use z-index, instead of visibility: hiden;and display: none.
  8. client-side form validation: Make sure that functionality is usable without JavaScript wherever possible.
  9. ARIA: Accessible Rich Internet Applications

16. data-*: HTML syntax vs. JS access vs. CSS access

credit: MDN: Using data attributes

data-* allow us to store extra information on standard, semantic HTML elements.

  • HTML syntax: data-columns

    1
    2
    3
    4
    5
    6
    7
    <article
    id="electric-cars"
    data-columns="3"
    data-index-number="12314"
    data-parent="cars">
    ...
    </article>
  • JS access: dataset (NOTE: dashes are converted to camelCase)

    [Read & Write:] each property is a string and can be read and written. In the above case setting article.dataset.columns = 5 would change that attribute to "5".

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 1st approach: standard (Read & Written)
    const article = document.querySelector('#electric-cars');
    article.dataset.columns // "3"
    article.dataset.indexNumber // "12314"
    article.dataset.parent // "cars"

    // 2nd approach: getAttribute() (ONLY Read)
    article.getAttribute('data-columns') // "3"
    article.getAttribute('data-index-number') // "12314"
    article.getAttribute('data-parent') // "cars"
  • CSS access: attr(name) to get attribute value for name

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // use case 1:
    article::before {
    content: attr(data-parent);
    }

    // use case 2:
    article[data-columns='3'] {
    width: 400px;
    }
    article[data-columns='4'] {
    width: 600px;
    }

17. How does browser render a webpage?

credit1 credit2 credit3

  1. Critical Rendering Path: (DON’T put any other task like img download in CRP)
    • 1.1 HTML => DOM tree
    • 1.2 CSS => CSSOM tree
    • 1.3 JS download & execute
    • 2.0 DOM Tree + CSSOM Tree + JS => Render Tree
    • 3.0 Layout (ReFlow: related to position, layout, height, width)
    • 4.0 Paint (RePain: related to color, etc not related to layout)
  2. CSS is render-blocking source, as it is cascading, has to parse to the end to form a determinated rendering rule; It can also block JS, as JS has to wait complete CSSOM tree to finish, to modify the CSS
  3. JS is HTML parse-blocking source, as it don’t need to wait DOM to finish, rather, it can change the DOM in the fly.
    1. regular script: download & execution will block HTML parsing
    2. script async: download won’t block (async download), execution will block HTML parsing
    3. script defer: download won’t block (async download),, execution won’t block HTML parsing

18. CSS: float set & clear

  • float will put element on its left/right side of its container(box model)

  • floated element is removed from normal flow, but still remaining a part of flow

  • float will force some value of display to be block, like: display: inline, inline-block, table-cell

  • clear will specify an element must be moved below a floating element.

  • If an element contains only floated elements, its height collapses to nothing. Solution clearfix as following:

    1
    2
    3
    4
    5
    #container::after { 
    content: "";
    display: block;
    clear: both;
    }

19. CSS: positioning

blog_1

20. HTML: People you may know

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>

<style>
.class-name {
box-shadow:
}
</style>

<section class="card-people-may-known">
<button class="card-people-may-known__ignore-btn">
X
</button>
<div class="card-people-may-known__profile-info">

<a class="card-people-may-known__name-link">
<img alt="Mingkai's photo" class="card-people-may-known__photo">

<span class="hidden">Person's Name</span>
<div class="tooltip-container">
<span class="card-people-may-known__name">{name}</span>
<span class="tooltip-container__tooltip-text">{name}</span>
</div>

<span class="hidden">Person's Name</span>
<div class="tooltip-container">
<span class="card-people-may-known__position">{position}</span>
<span class="tooltip-container__tooltip-text">{name}</span>
</div>
</a>

<div class="footer">
<a>
<span> connect this person </span>
<button role="connect"> connect </button>
</a>
</div>
</div>

</section>

21. UI: create a tooltip

写一个类似tooltip的widget,当用户把鼠标放在LinkedIn页面上某个联系人的名字上时,会出现一个类似tooltip的预览框,里面有该用户的头像,姓名,degree以及title。要求先用HTML以及CSS mock up下大致的layout,然后用AJAX得到所需要显示的用户信息。?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<html>
<style>
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}

.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
}

.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}

.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}

a {
text-decoration: none;}
</style>
<body style="text-align:center;">

<h2>Tooltip</h2>
<p>Move the mouse over the text below:</p>


<div class="tooltip">
<a href="https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_tooltip">
Hover over me
</a>
<span class="tooltiptext">Tooltip text</span>
</div>


</body>
</html>

22. JS: memorize function

Create a memo function take a function and returns a new one. The returned function can cache the result for corresponding parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Approach 1: store result onto returned function (self-contained)
function cachify_1(fn, resolver) {
// use `!=` instead of `!==`, since
if (typeof fn != 'function' || (resolver != null && typeof resolver != 'function')) {
throw new Error("Not a function");
}

let fn_memoized = function(...args) {
const key = resolver ? resolver.apply(null, args) : args[0];
const cache = fn_memoized.cache;
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(null, args);
cache.set(key, result);
return result;
}
fn_memoized.cache = new Map();

return fn_memoized;
}

// Test
function test1(para) {
let i = 0;
let result = 0;
while (i < 10000) {
result += para;
i += 1;
}
return result;
}

let cachedTest1 = cachify_1(test1);
console.time("test1_1st");
let res1 = cachedTest1(9);
console.timeEnd("test1_1st");
console.log("test1_1st: ", res1);

console.time("test1_2nd");
res1 = cachedTest1(9);
console.timeEnd("test1_2nd");
console.log("test1_2nd: ", res1);

23. UI: Inifinite scroll

24. JS: lazy load

[Credit] web browser renders a webpage by reading it’s markup (HTML). If the markup contains an external JavaScript file, the browser stops loading the markup and starts downloading that external JavaScript – this causes a block in webpage rendering (known as render blocking or parser blocking).

1
2
3
4
5
6
7
8
window.onload = function() {
let s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
let x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}

26. UI: autocomplete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
box-sizing: border-box;
}

body {
font: 16px Arial;
}

/*the container must be positioned relative:*/
.autocomplete {
position: relative;
display: inline-block;
}

input {
border: 1px solid transparent;
background-color: #f1f1f1;
padding: 10px;
font-size: 16px;
}

input[type=text] {
background-color: #f1f1f1;
width: 100%;
}

input[type=submit] {
background-color: DodgerBlue;
color: #fff;
cursor: pointer;
}

.autocomplete-items {
position: absolute;
border: 1px solid #d4d4d4;
border-bottom: none;
border-top: none;
z-index: 99;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
}

.autocomplete-items div {
padding: 10px;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #d4d4d4;
}

/*when hovering an item:*/
.autocomplete-items div:hover {
background-color: #e9e9e9;
}

/*when navigating through the items using the arrow keys:*/
.autocomplete-active {
background-color: DodgerBlue !important;
color: #ffffff;
}
</style>
</head>
<body>

<h2>Autocomplete</h2>

<p>Start typing:</p>

<!--Make sure the form has the autocomplete function switched off:-->
<form autocomplete="off" action="/action_page.php">
<div class="autocomplete" style="width:300px;">
<input id="myInput" type="text" name="myCountry" placeholder="Country">
</div>
<input type="submit">
</form>

<script>
function autocomplete(inp, arr) {
/*the autocomplete function takes two arguments,
the text field element and an array of possible autocompleted values:*/
var currentFocus;
/*execute a function when someone writes in the text field:*/
inp.addEventListener("input", function(e) {
var a, b, i, val = this.value;
/*close any already open lists of autocompleted values*/
closeAllLists();
if (!val) { return false;}
currentFocus = -1;
/*create a DIV element that will contain the items (values):*/
a = document.createElement("DIV");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items");
/*append the DIV element as a child of the autocomplete container:*/
this.parentNode.appendChild(a);
/*for each item in the array...*/
for (i = 0; i < arr.length; i++) {
/*check if the item starts with the same letters as the text field value:*/
if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
/*create a DIV element for each matching element:*/
b = document.createElement("DIV");
/*make the matching letters bold:*/
b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
b.innerHTML += arr[i].substr(val.length);
/*insert a input field that will hold the current array item's value:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/*execute a function when someone clicks on the item value (DIV element):*/
b.addEventListener("click", function(e) {
/*insert the value for the autocomplete text field:*/
inp.value = this.getElementsByTagName("input")[0].value;
/*close the list of autocompleted values,
(or any other open lists of autocompleted values:*/
closeAllLists();
});
a.appendChild(b);
}
}
});
/*execute a function presses a key on the keyboard:*/
inp.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
/*If the arrow DOWN key is pressed,
increase the currentFocus variable:*/
currentFocus++;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 38) { //up
/*If the arrow UP key is pressed,
decrease the currentFocus variable:*/
currentFocus--;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 13) {
/*If the ENTER key is pressed, prevent the form from being submitted,*/
e.preventDefault();
if (currentFocus > -1) {
/*and simulate a click on the "active" item:*/
if (x) x[currentFocus].click();
}
}
});
function addActive(x) {
/*a function to classify an item as "active":*/
if (!x) return false;
/*start by removing the "active" class on all items:*/
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
/*add class "autocomplete-active":*/
x[currentFocus].classList.add("autocomplete-active");
}
function removeActive(x) {
/*a function to remove the "active" class from all autocomplete items:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
/*close all autocomplete lists in the document,
except the one passed as an argument:*/
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
/*execute a function when someone clicks in the document:*/
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}

/*An array containing all the country names in the world:*/
var countries = ["Afghanistan","Albania","Algeria","Andorra","Angola","Anguilla","Antigua & Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia","Bosnia & Herzegovina","Botswana","Brazil","British Virgin Islands","Brunei","Bulgaria","Burkina Faso","Burundi","Cambodia","Cameroon","Canada","Cape Verde","Cayman Islands","Central Arfrican Republic","Chad","Chile","China","Colombia","Congo","Cook Islands","Costa Rica","Cote D Ivoire","Croatia","Cuba","Curacao","Cyprus","Czech Republic","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador","Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Ethiopia","Falkland Islands","Faroe Islands","Fiji","Finland","France","French Polynesia","French West Indies","Gabon","Gambia","Georgia","Germany","Ghana","Gibraltar","Greece","Greenland","Grenada","Guam","Guatemala","Guernsey","Guinea","Guinea Bissau","Guyana","Haiti","Honduras","Hong Kong","Hungary","Iceland","India","Indonesia","Iran","Iraq","Ireland","Isle of Man","Israel","Italy","Jamaica","Japan","Jersey","Jordan","Kazakhstan","Kenya","Kiribati","Kosovo","Kuwait","Kyrgyzstan","Laos","Latvia","Lebanon","Lesotho","Liberia","Libya","Liechtenstein","Lithuania","Luxembourg","Macau","Macedonia","Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Marshall Islands","Mauritania","Mauritius","Mexico","Micronesia","Moldova","Monaco","Mongolia","Montenegro","Montserrat","Morocco","Mozambique","Myanmar","Namibia","Nauro","Nepal","Netherlands","Netherlands Antilles","New Caledonia","New Zealand","Nicaragua","Niger","Nigeria","North Korea","Norway","Oman","Pakistan","Palau","Palestine","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Poland","Portugal","Puerto Rico","Qatar","Reunion","Romania","Russia","Rwanda","Saint Pierre & Miquelon","Samoa","San Marino","Sao Tome and Principe","Saudi Arabia","Senegal","Serbia","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","Solomon Islands","Somalia","South Africa","South Korea","South Sudan","Spain","Sri Lanka","St Kitts & Nevis","St Lucia","St Vincent","Sudan","Suriname","Swaziland","Sweden","Switzerland","Syria","Taiwan","Tajikistan","Tanzania","Thailand","Timor L'Este","Togo","Tonga","Trinidad & Tobago","Tunisia","Turkey","Turkmenistan","Turks & Caicos","Tuvalu","Uganda","Ukraine","United Arab Emirates","United Kingdom","United States of America","Uruguay","Uzbekistan","Vanuatu","Vatican City","Venezuela","Vietnam","Virgin Islands (US)","Yemen","Zambia","Zimbabwe"];

/*initiate the autocomplete function on the "myInput" element, and pass along the countries array as possible autocomplete values:*/
autocomplete(document.getElementById("myInput"), countries);
</script>

</body>
</html>

27. UI: widget

build a widget with 4 lists and 3 nested list, nested can be span/collapse when clicking on parents, highlight txt when hover大概是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}

.active, .accordion:hover {
background-color: #ccc;
}

.panel {
padding: 0 18px;
display: none;
background-color: white;
overflow: hidden;
}
</style>
</head>
<body>

<h2>Accordion</h2>

<button class="accordion">Section 1</button>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

<button class="accordion">Section 2</button>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

<button class="accordion">Section 3</button>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

<script>
var acc = document.getElementsByClassName("accordion");
var i;

for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
</script>

</body>
</html>

28: Browser: GET v.s. POST

29. UI: thumb up

写一个类似于点赞功能的ui,点赞之后旁边的数字+1,取消之后-1写html的时候问了怎么改进accessiblity和symentic?用了一些

之类的标签,加了role属性。写了个js函数来处理button toggle,事件绑定在外层容器上用了事件代理来处理点击操作

30. JS:

  1. 几座小岛那题
  2. Lowest Common Ancestor of a Binary Search Tree 还没不及写完 只是讲一下做法而已
  3. 衣而期
  4. 衣而溜

Debounce & Throttle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
function debounce_leading(fn, period) {
let allowed = true;
let timeOut = null;
return function(...args) {
if (allowed) {
allowed = false;
fn(...args);
}

clearTimeout(timeOut);
timeOut = setTimeout(() => {
allowed = true;
}, period);
};
}



function debounce_trailing(fn, period) {
let timeOut = null;
return function(...args) {
clearTimeout(timeOut);
timeOut = setTimeout(() => {
fn(...args);
}, period);
};
}



/**
* Created by thephpjo on 21.04.14.
*/

/**
* Created by thephpjo on 21.04.14.
*/

var helpers = {
/*
debounce: function(func, wait, scope) {
var timeout;
return function() {
var context = scope || this,
args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
*/

/*

throttle: function(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last, deferTimer;
return function() {
var context = scope || this;

var now = +new Date(),
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function() {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
*/

throttle: function(func, wait, context, options) {
var timeout, args, result;
var previous = 0;
if (!options) options = {};

var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};

var throttled = function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};

throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};

return throttled;
},

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
debounce: function(func, wait, context, immediate) {
var timeout, result;

var later = function(args) {
timeout = null;
if (args) result = func.apply(context, args);
};

var debounced = function(...args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
// timeout = _.delay(later, wait, this, args);
timeout = setTimeout(function() {
return later.apply(this, args);
}, wait);
}

return result;
};

debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};

return debounced;
},

debounce_leading: function(fn, wait, context) {
let allowed = true;
let timeout = null;

let debounced = function(...args) {
if (allowed) {
allowed = false;
fn.apply(context, args);
} else {
clearTimeout(timeout);
timeout = setTimeout(() => {
allowed = true;
}, wait);
}
};
return debounced;
},

debounce_max: function bouncify(fn, wait, context, maxWait) {
let timeout = null;
let previous = null;
let firstTime = true;

const bounced = function(...args) {
clearTimeout(timeout);

if (firstTime) {
firstTime = false;
previous = Date.now();
}
let now = Date.now();
if (now - previous >= maxWait) {
previous = now;
fn.apply(context, args);
// console.log("debouce: ", now - previous)
} else {
timeout = setTimeout(() => {
previous = Date.now();
fn.apply(context, args);
}, wait);
}
}

return bounced;
},

throttle_leading: function (fn, wait, context, options) {
let scheduled = true;
return function(...args) {
if (scheduled) {
scheduled = false;
fn.apply(this, args);
setTimeout(() => {
scheduled = true;
}, wait);
}
}
},

throttle_trailing: function (fn, wait, context, options) {
let scheduled = false;
return function(...args) {
if (!scheduled) {
scheduled = true;
setTimeout(() => {
scheduled = false;
fn.apply(this, args);
}, wait);
}
}
},

throttle_trailing_2: function (fn, wait, context, options) {
let lastInvoke = null;
let timeout = null;

return function(...args) {
let context = this;
clearTimeout(timeout);
if (!lastInvoke) {
timeout = setTimeout(() => {
fn.apply(this, args);
lastInvoke = Date.now();
}, wait);
} else {
let remain = wait - (Date.now - lastInvoke);

timeout = setTimeout(() => {
fn.apply(this, args);
lastInvoke = Date.now();
}, wait);
}

}
},

// same as below
throttle_leading_version2: function (fn, wait, context, options) {
let lastInvoke = null;
let timeout = null;

return function(...args) {
let context = this;
if (!lastInvoke) {
fn.apply(context, args);
lastInvoke = Date.now();
} else {
const remain = wait - (Date.now() - lastInvoke);
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(context, args);
lastInvoke = Date.now();
}, remain)
}
}
},

// same as above
throttle_with_last_call: (func, limit, context) => {
let lastFunc
let lastRan
return function() {
const args = arguments

// 只第一次执行
if (!lastRan) {
func.apply(context, args)
lastRan = Date.now()
// 后续重复执行
} else {
clearTimeout(lastFunc)
let remain = limit - (Date.now() - lastRan);
if (remain <= 0) {
func.apply(context, args);
lastRan = Date.now();
} else {
lastFunc = setTimeout(function() {
// if ((Date.now() - lastRan) >= limit) {
func.apply(context, args)
lastRan = Date.now()
// }
}, remain)
}

}
}
}

};


/**
* Created by thephpjo on 21.04.14.
*/

function NIM_demo() {
this.canvas = document.getElementById("paintonme");
this.context = this.canvas.getContext("2d");

this.movearea = document.getElementById("moveonme");

this.canvasTimeScale = 5 * 1000;

this.paintColors = ["#bbd", "#464", "#d88"];
this.totalLanes = this.paintColors.length;

this.leftMargin = 100;

var self = this;

this.init = function() {
this.canvas.width = window.innerWidth - 250;
this.flush();
this.movearea.addEventListener("mousemove", this.regularHandler);
this.movearea.addEventListener(
"mousemove",
// helpers.debounce(self.debounceHandler, 1000, this)
helpers.debounce_leading(self.debounceHandler, 1000, this)

// helpers.debounce_max(self.debounceHandler, 500, this, 1000)
// helpers.throttle_leading_version2(self.debounceHandler, 1000, this)
// helpers.throttle_trailing_2(self.debounceHandler, 1000, this)

);
this.movearea.addEventListener(
"mousemove",
helpers.debounce(self.throttleHander, 1000, this)

// helpers.throttle(self.throttleHander, 500, this, {trailing: true, leading: true})
// helpers.throttle_leading(self.throttleHander, 1000, this, {trailing: true, leading: false})
// helpers.throttle_trailing(self.throttleHander, 1000, this, {trailing: true, leading: false})
// helpers.throttle_with_last_call(self.throttleHander, 1000, this, {trailing: true, leading: true})
);
};



/**
* painting the rectangle / line
* @param lane
* @param time
*/
this.paintRect = function(lane, time) {
if (time > this.canvasTimeScale) {
this.startTime += time;
time = 0;
this.flush();
}
// console.log(lane,time);
this.context.fillStyle = this.paintColors[lane];

var x =
((this.canvas.width - this.leftMargin) / this.canvasTimeScale) * time +
this.leftMargin;
var y = (this.canvas.height / this.totalLanes) * lane;
var height = this.canvas.height / this.totalLanes;
var width = 1;

this.context.fillRect(x, y, width, height);
};

this.flush = function() {
this.context.fillStyle = "#ffffff";
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);

this.context.font = "200 18px Roboto,Helvetica,Arial";
this.context.fillStyle = this.paintColors[0];
this.context.fillText("Regular", 0, 100);

this.context.fillStyle = this.paintColors[1];
this.context.fillText("debounce", 0, 300);

this.context.fillStyle = this.paintColors[2];
this.context.fillText("throttle", 0, 500);
};
/**
* get the time difference
* @returns {number}
*/
this.getTimeDiff = function() {
var time = new Date().getTime();
if (!this.startTime) {
this.startTime = time;
}
time -= this.startTime;
return time;
};

this.regularHandler = function() {
self.paintRect(0, self.getTimeDiff());
};
this.debounceHandler = function() {
self.paintRect(1, self.getTimeDiff());
};
this.throttleHander = function() {
self.paintRect(2, self.getTimeDiff());
};
}


let params = [];
function startExecutePeriodically() {
if (params.length > 0) {
set
}
}

function queue(fn, wait) {
let params = [];
let context = null;
let firstTime = true;
function startExecutePeriodically() {
if(params.length > 0) {
setTimeout(()=>{
fn.apply(context, params.shift());
startExecutePeriodically();
}, wait);
}
}

let queuedFun = function(...args) {
context = this;
params.push(args);
if (firstTime) {
firstTime = false;
startExecutePeriodically();
}
}

return queuedFun;
}

<html>
<head>
<title>debounce & throttle demo</title>
<style>
body {
font-family: "Roboto","Helvetica",Arial;
font-weight: 200;
color: #777;
}
#moveonme {
width: 200px;
height: 600px;
background: #f5f5f5;
border: 1px solid #aaa;
float: left;
box-sizing: border-box;
padding: 25px;
text-align: center;
font-size: 18px;
}

.backtoblog {
width: 200px;
  padding: 10px;
  background: #f5f5f5;
  border: 1px solid #aaa;
  color: #777;
  display: inline-block;
  text-decoration: none;
  transition: 0.2s all;
  box-sizing: border-box;
  position: relative;
  top: -1px;
  text-align: center;
}

.backtoblog:hover {
  border: 1px solid #aaa;
  background: transparent;
}
</style>
</head>
</html>





<div id="moveonme" >
move your mouse here
</div>
<canvas id="paintonme" height="600" width="600" style="float:right;"></canvas>

<a href="http://blog.nimius.net/2014/04/javascript-debounce-throttle/" class="backtoblog"> < zur&uuml;ck zum Blog</a>
<script src="bbb.js"></script>

<script>
var demo = new NIM_demo();
demo.init();
</script>



// 饿了么
var throttle = function (fn, delay) {
var now, lastExec, timer, context, args;

var execute = function () {
fn.apply(context, args);
lastExec = now;
};

return function () {
context = this;
args = arguments;

now = Date.now();

if (timer) {
clearTimeout(timer);
timer = null;
}

if (lastExec) {
var diff = delay - (now - lastExec);
if (diff < 0) {
execute();
} else {
timer = setTimeout(() => {
execute();
}, diff);
}
} else {
execute();
}
};
};

Autocomplete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
function ele(id) {
return document.getElementById(id);
}

let inputBox = ele("inputBox");
let suggestionList = ele("suggestionList");

inputBox.oninput = debouce(inputBoxHandler, 500);
suggestionList.onclick = suggestionClickHandler;

// 1. call API
// 2. Debounce
// 3. update SuggestionList
function inputBoxHandler(e) {
callAPI(e.target.value).then(function(data) {
updateSuggestionList(data);
});
}

// 1. update inputBox
// 2. hide suggestionList
function suggestionClickHandler(e) {
if (e.target.tagName === "LI") {
inputBox.value = e.target.textContent;
suggestionList.classList.toggle("hide", true);
}
}

// util to debounce an action
function debouce(fn, wait) {
let timeout = null;
let deboucedFun = function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
return deboucedFun;
}

// DOM update
function updateSuggestionList(list) {
console.log(list.length);
if (list.length === 0) {
suggestionList.classList.toggle("hide", true);
return;
}
let htmlText = "";
list.forEach(item => {
htmlText += `<li>${item}</li>
`;
});
suggestionList.innerHTML = htmlText;
suggestionList.classList.toggle("hide", false);
}

// API
function callAPI(prefix) {
return new Promise((resolve, reject) => {
if (prefix.length === 0) {
resolve([]);
return;
}
let res = countries.filter(
country =>
country.toLowerCase().slice(0, prefix.length) === prefix.toLowerCase()
);
resolve(res);
});
}
var countries = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Anguilla",
"Antigua & Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia",
"Bosnia & Herzegovina",
"Botswana",
"Brazil",
"British Virgin Islands",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central Arfrican Republic",
"Chad",
"Chile",
"China",
"Colombia",
"Congo",
"Cook Islands",
"Costa Rica",
"Cote D Ivoire",
"Croatia",
"Cuba",
"Curacao",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Polynesia",
"French West Indies",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea Bissau",
"Guyana",
"Haiti",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Kosovo",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macau",
"Macedonia",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Mauritania",
"Mauritius",
"Mexico",
"Micronesia",
"Moldova",
"Monaco",
"Mongolia",
"Montenegro",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauro",
"Nepal",
"Netherlands",
"Netherlands Antilles",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"North Korea",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestine",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Reunion",
"Romania",
"Russia",
"Rwanda",
"Saint Pierre & Miquelon",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Korea",
"South Sudan",
"Spain",
"Sri Lanka",
"St Kitts & Nevis",
"St Lucia",
"St Vincent",
"Sudan",
"Suriname",
"Swaziland",
"Sweden",
"Switzerland",
"Syria",
"Taiwan",
"Tajikistan",
"Tanzania",
"Thailand",
"Timor L'Este",
"Togo",
"Tonga",
"Trinidad & Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks & Caicos",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States of America",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Vatican City",
"Venezuela",
"Vietnam",
"Virgin Islands (US)",
"Yemen",
"Zambia",
"Zimbabwe"
];


//++++++++++++++++++++++
body {
font-family: sans-serif;
margin: 0;
padding: 0;
}
#container {
margin: 20px;
}

#inputBox {
border: 1px solid green;
}

.list {
padding: 10px;
width: 300px;
height: 100px;
overflow-y: auto;
list-style: none;
border: 1px solid green;
}

.list li:hover {
background-color: lightblue;
}

.hide {
display: none;
}


// ================
<!DOCTYPE html>
<html>

<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<link rel="stylesheet" type="text/css" href="src/styles.css">
</head>

<body>
<div id="container" >
<input name="inputBox" type="text" id="inputBox">
<ul id="suggestionList" class="list hide" >
</ul>
</div>
<script src="src/index.js">
</script>
</body>

</html>

II.On-site

  1. Web & Browser

    1. Progressive web app

    2. Security: XSS,

    3. Http1.0 vs http1.1 vs http2

      1. keep alive: long polling
      2. header compressed
      3. binary stream transmission
    4. web socket vs. server side event(not supported by all) vs. long polling

    5. Service worker vs. Web worker vs. Web socket

    6. PWA:link

      1. Service Worker
      2. Client side storage => Indexed DB
      3. Web push/notification API (service worker)
      4. App shell
    7. short connection & persistent connect vs. long polling & polling reference

    8. HTTP与浏览器缓存
      1. 两方的流程
        1. 图1:server 根据需求决定 cache 的config
        2. 图2:browser根据 server 发来的 cache config,决定是否应用cache,or直接请求
        3. 顺序
          1. Cache-Control —— 请求服务器之前
          2. Expires —— 请求服务器之前
          3. If-None-Match (Etag) —— 请求服务器
          4. If-Modified-Since (Last-Modified) —— 请求服务器
      2. Expires + Cache-Control: (强缓存)
      3. Cache-Control 优先级高于 Expires
      4. 强缓存 优先判断;然后判断弱缓存
      5. Last-Modified + If-Modified-Since (弱缓存)
        1. server response 中带有Last-Modified: time_k, 让browser将资源缓存起来(attaching Last-Modified)
        2. browser request 中带有 If-Modified-Since:time_k(attached Last-Modified),发给server,让其决定是否新鲜
      6. ETag + If-None-Match: (弱缓存)ETag is unique MD5 digest of resource
        1. server response 中带有ETag: val, 让browser将资源缓存起来(attaching ETag)
        2. browser request 中带有 If-None-Match: val(attached Last-Modified),发给server,让其决定是否新鲜
      7. ETag的优先级比Last-Modified更高
      8. cache control: no cache vs no store
        1. no cache: 告诉browser:不能直接用cache,browser必须发请求,server不反悔 content,而是告诉browser: 可以使用cache(304并且会显示一个Not Modified)
        2. no store:告诉browser:请求得到的内容,你不可以缓存起来,每次都必须从server拿资源
    9. Storage

      1. local storage(5M): 永远存在(网页关闭也存在),limited to 域名

        1. storage event:

          1
          2
          3
          4
          5
          window.addEventListener('storage',function(e){
          console.log('key=' + e.key);
          console.log('oldValue=' + e.oldValue);
          console.log('newValue=' + e.newValue);
          })
2. sessin storage(5M):临时存在(网页关闭就消失),limited to 本域名、本网页存在期间

3. cookie(4k): cookie可以设置路径path,所有他要比上面两个多了一层访问限制domain => path

4. session:
  1. JSONP: https://www.w3schools.com/js/js_json_jsonp.asp
    1. vs. CORS
  2. Optimistic UI update: like button

  3. encodeURIComponent vs decodeURIComponent

    1
     
  1. CSS + HTML

    1. Accessibility: ARIA

      1. checklist:

        1. Check ARIA roles here
        2. <img alt="" >
        3. <div tabindex='0' > keyboard nav using tab
        4. <p tabindex='-1'> keyboard focus using arrow (since children will also be tabable if parent is tabindex='0' )
        5. <label for='name input'> <div aria-labelledby='title'>
        6. <div role='menu'>
        7. <div aria-valuemax='100'> <div aria-valuenow='78'>
        8. <span aria-checked='false'>
        9. <input aria-required='true'>
        10. <span role='alert'>
      2. In HTML5, all ARIA attributes validate. The new landmark elements (<main>, <header>, <nav> etc) have built-in ARIA roles, so there is no need to duplicate them.

      3. 温度计

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        <div id="percent-loaded" 
        role="progressbar"
        aria-valuenow="75"
        aria-valuemin="0"
        aria-valuemax="100"
        />

        // OR JS
        <script>
        // Find the progress bar <div> in the DOM.
        var progressBar = document.getElementById("percent-loaded");

        // Set its ARIA roles and states,
        // so that assistive technologies know what kind of widget it is.
        progressBar.setAttribute("role", "progressbar");
        progressBar.setAttribute("aria-valuemin", 0);
        progressBar.setAttribute("aria-valuemax", 100);

        // Create a function that can be called at any time to update
        // the value of the progress bar.
        function updateProgress(percentComplete) {
        progressBar.setAttribute("aria-valuenow", percentComplete);
        }
        </script>
  1. innerText vs innerHtml vS textContent

    1. innerText is aware of CSS styling, it will trigger a reflow, whereas textContent will not.
    2. innerText will not include text that is hidden by CSS
    3. textContent include text no matter the style is hidden or not! GOOD! NO Reflow!
  2. display vs. float vs. position

  3. BFC(Box Formatting Context)

    1. BFC(Block formatting context)直译为”块级格式上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干

    2. BFC布局规则:
      1. 内部的Box会在垂直方向,一个接一个地放置。
      2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
      3. 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
      4. BFC的区域不会与float box重叠。
      5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
      6. 计算BFC的高度时,浮动元素也参与计算
    3. BFC 形成条件:

      1. 根元素
      2. float属性不为none
      3. position为absolute或fixed
      4. display为inline-block, table-cell, table-caption
      5. overflow不为visible
  4. <form>: <fieldset> + <legent> + <input> + <label>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <form>
    <fieldset>
    <legend>Your income is:</legend>
    <p>
    <input type="radio" name="inc" value="0-50K" id="0-50K" />
    <label for="0-50K">$0-50,000 USD</label>
    </p>
    <p>
    <input type="radio" name="inc" value="50-100K" id="50-100K" />
    <label for="50-100K">$50,000-100,000 USD</label>
    </p>
    <p>
    <input type="radio" name="inc" value="100K+" id="100K+" />
    <label for="100K+">$100,000+ USD</label>
    </p>
    </fieldset>

    <fieldset>
    <legend>Your status is:</legend>
    <p>
    <input type="radio" name="status" value="single" id="single" />
    <label for="single">single</label>
    </p>
    <p>
    <input type="radio" name="status" value="married" id="married" />
    <label for="married">married</label>
    </p>
    <p>
    <input type="radio" name="status" value="partner" id="partner" />
    <label for="partner">domestic partner</label>
    </p>
    </fieldset>

    <p>This form goes on with another 97 questions....</p>
    <input type="submit" value="Submit" />
    </form>
  5. How to know which <input type='radio' /> is selected?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <form action="#n" name="theForm">

    <label for="gender">Gender: </label>
    <input type="radio" name="genderS" value="1" checked> Male
    <input type="radio" name="genderS" value="0" > Female<br><br>
    <a href="javascript: submitForm()">Search</A>
    </form>

    <script>
    var radios = document.getElementsByName('genderS');
    for (var i = 0, length = radios.length; i < length; i++)
    {
    if (radios[i].checked)
    {
    // do whatever you want with the checked radio
    alert(radios[i].value);

    // only one radio can be logically checked, don't check the rest
    break;
    }
    }
    </script>
  1. <button type='button'> ??

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    If you include a button in a form element without specifying it's just a regular button, it defaults to a submit button.


    <form>
    <button>I will submit the form when clicked!</button>
    </form>
    vs

    <form>
    <button type='button'>I won't!</button>
    </form>
  2. <input type="button" > vs <button type='button'>

    Unlike <input> tags, <button>‘s can contain other html elements as their labels. <input type="button"> can only accept a string as its label text (css styles not withstanding).

    Additionally, the <button> element accepts a wide range of uncommon but useful attributes regarding multiple forms and click actions. See the MDN page for more details.

  3. HTML5

    1. New elements: all new elements are block-level elements

    2. semantics

      1. article
      2. aside
      3. details
      4. figcaption
      5. figure
      6. footer
      7. header
      8. main
      9. mark
      10. nav
      11. section
      12. summary
      13. time
    3. HTML migration

      1
      2
      3
      4
      5
      <div id="header">			<header>
      <div id="menu"> <nav>
      <div id="content"> <section>
      <div class="article"> <article>
      <div id="footer"> <footer>
4. style guid

   1. `<!doctype html>`
   2. `<html lang="en-US">`
   3. `<head> <meta charset="UTF-8"> </head>`
   4. `<meta name="viewport" content="width=device-width, initial-scale=1.0">`
   5. all lower case
   6. `<img alt="" />`
  1. AJAX

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let xhr = new XMLHttpRequest();
    xhr.onload = function() {
    if (xhr.readyState = 4 && xhr.status == 200) {
    // manipulate DOM
    console.log(xhr.responseText);
    } else {
    console.log(xhr.status);
    }
    };

    xhr.open("GET", url, true);
    xhr.send();
  2. How to lazy load <img> & how to avoid unnecessary traffic?

    1. Browser first download, and then use image in document

    2. new Image(): The Image() constructor creates a new HTMLImageElement instance. It is functionally equivalent to document.createElement('img').

    3. img.onload

    4. data-imgSrc <=> e.dataset.imgSrc

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      (() => {
      // Page is loaded
      const objects = document.getElementsByClassName('asyncImage');
      Array.from(objects).map((item) => {
      // Start loading image
      const img = new Image();
      img.src = item.dataset.src;
      // Once image is loaded replace the src of the HTML element
      img.onload = () => {
      item.classList.remove('asyncImage');
      return item.nodeName === 'IMG' ?
      item.src = item.dataset.src :
      item.style.backgroundImage = `url(${item.dataset.src})`;
      };
      });
      })();
5. `onscroll` lazy load

   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let lazyImages= [...document.querySelectorAll('.lazy-image')];
let inAdvance = 300;

function lazyLoad() {
lazyImages.forEach(image => {
if (image.offsetTop < window.innerHeight + window.scrollY + inAdvance) {
image.src = image.dataset.src;
}
})
}

lazyLoad();
window.addEventListener('scroll', _.throttel(lazyLoad, 50));
window.addEventListener('resize', _.throttel(lazyLoad, 50));
6. NEVER set `<img src="">`: will load this page 2nd time 7.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="https://images.unsplash.com/photo-1524654458049-e36be0721fa2?ixlib=rb-0.3.5&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ&s=1a6782be5cdb94b780ed9d4a79de7041" alt="">


<style>
// These styles keep the aspect ratio of img.
img {
max-width: 100%;
height: auto;
margin: 0 0 1rem;
border: 4px solid orange;
box-sizing: border-box;
}
</style>
<script>
[...document.querySelectorAll('[data-src]')]
.map(img => {
img.addEventListener('click', e => {
img.src = img.dataset.src)
}
})
</script>
  1. JS Lib: (scroll + API)

    1. Write a Promise (then, catch, finally)

    2. Debounce & Throttle & QueuedCall:

      1. ⚠️: this 的处理,传入的fn 有可能内部带有this,那么debounced_fun有可能这样被调用: this.debounced_fn(args)

      2. throttle其实就是 debounce 的“覆盖式”调用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        throttle_with_last_call: (func, limit, context) => {
        let lastFunc
        let lastRan
        return function() {
        const args = arguments
        if (!lastRan) {
        func.apply(context, args)
        lastRan = Date.now()
        } else {
        clearTimeout(lastFunc)
        lastFunc = setTimeout(function() {
        // if ((Date.now() - lastRan) >= limit) {
        func.apply(context, args)
        lastRan = Date.now()
        // }
        }, limit - (Date.now() - lastRan))
        }
        }
        }
      3. QueuedCall: 对每次fn 调用都会执行,不会丢掉;但是是以固定频率执行的

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        function queue(fn, wait) {
        let params = [];
        let context = null;
        let isActive = false;
        function executePeriodically() {
        isActive = true;
        if (params.length > 0) {
        setTimeout(() => {
        fn.apply(context, params.shift());
        executePeriodically();
        }, wait);
        } else {
        isActive = false;
        }
        }

        let queuedFun = function(...args) {
        context = this;
        params.push(args);
        if (!isActive) {
        executePeriodically();
        }
        };

        return queuedFun;
        }

        // Testing
        function loggingIncre(i) {
        console.log(i);
        }

        let queuedFun = queue(loggingIncre, 1000);
        for (let i = 0; i < 2; i++) {
        queuedFun(i);
        }

        setTimeout(() => {
        queuedFun(1111);
        setTimeout(() => {
        queuedFun(2222);
        }, 3000);
        }, 3000);
  1. Memoize: weakMap的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function memoize(func, resolver) {
    if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
    throw new TypeError('Expected a function')
    }
    const memoized = function(...args) {
    const key = resolver ? resolver.apply(this, args) : args[0]
    const cache = memoized.cache

    if (cache.has(key)) {
    return cache.get(key)
    }
    const result = func.apply(this, args)
    memoized.cache = cache.set(key, result) || cache
    return result
    }
    memoized.cache = new (memoize.Cache || Map)
    return memoized
    }

    memoize.Cache = Map

    export default memoize
  2. Infinite scroll

    1. Window.scrollY vs.element.scrollTop: 都表示上侧滑出去多少

    2. Window.scrollX vs.element.scrollLeft 都表示左侧滑出去多少

    3. onscroll要绑定在 镶有滚动条的 div/window 上,而不是内容div上

      1. window上的距离判断: div mustn’t have gap from window

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        <body >
        <div id="scrollContainer">
        <br/><br/><br/><br/><br/><br/><br/><br/><br/> ... <br />
        </div>
        </body>

        <script>
        const scrollContainer = document.getElementById("scrollContainer");
        const rect = scrollContainer.getBoundingClientRect();

        const deltaBottom = rect.bottom - window.innerHeight;
        </script>
2. div上的距离判断: 

   
1
2
3
4
5
6
7
8
9
10
<div id="scrollContainer">
<br/><br/><br/><br/><br/><br/><br/><br/><br/> ... <br />
</div>

<script>
const scrollContainer = document.getElementById("scrollContainer");
const rect = scrollContainer.getBoundingClientRect();

const deltaBottom = scrollContainer.scrollHeight - scrollContainer.clientHeight - scrollContainer.scrollTop;
</script>
  1. LeetCode:
    1. K Closest Points to Origin
      1. quick select
      2. heap
    2. Maximum Subarray
      1. DP 2-d => DP 1-d
      2. accum array
    3. Integer => String
      1. details: ‘’

Product Taste

  1. 问最喜欢的app,linkedin有什么要改进的地方,平时上那些技术网站论坛

    1. LinkedIn:
      • like concise and clean UI, without distraction (compared to FB)
      • like positive and supportive environment
      • like the new action button group, give more accurate react (Thoughts on new buttons)
      • Improvement:
        • Click on link: pop out detail page, have to click close. Can we use touch?
        • Can we share file on home screen?
    2. Stripe

      • harmonious color skeme / animation
      • deliver more info with limited view port
        • debate on this: focus with margin or info with less margin
        • depends on business mode
  2. 比如谈一次失败的经历;如果你和其他人意见不一致时怎么处理等。

    1. That not uncommon at work
    2. Something didn’t handle well, exist ambiguity. Opportunity for improvement.
    3. Talk directly, try to understand other’s point of view. If still cannot come to an agreement. No need to be hostile, as both parties’ goal is to improve sth
      1. time to bring more ideas in, from different perspectives: PM, designer, backend, frontend, CS, even customers
      2. eg: whether we should use PureComponent in Redux project
  3. 谈谈你喜欢和不喜欢的App,以及LinkedIn App。我说我不怎么用LinkedIn App,谈谈网页版的行不行,他说没问题。

  4. 设计一个dashboard,dashboard是一个地图,当有新用户注册账号时,需要实时在dashboard上显示用户位置?
    follow up:前端用什么library?(d3.js),需要向前端发送哪些数据?(经纬度或者geohash),push vs pull?怎么实现push service?(消息队列)?怎么测试?

  5. 用网页写一个带简单 UI 的计算器。有点 Leetcode 224 的意思,但是 UI 的行为要符合简单的计算器,比如屏幕上显示 1 的时候,输入 2,要变成12,但是显示 0 的时候,输入 1,就要变成 1。Edge case 比较多,要好好跟考官交流。一开始只让写有 + 跟 = 就行了。写的不太好,这里又我面试完写的完全版: https://gist.github.com/d6u/2e982bdc965e14ce12a545e6996f0af4 大家可以一起交流。带 +、-、*、/。面试的时候提了 reverse polish notation 但是没用。-baidu 1point3acres

  6. 11:00-12:00,一位面试官。题目是给了个地图,上面有很多点, 每个点是个object, x, y 坐标都给了, 比如{x: 5, y: 5}已知 包含所有点的 array, 某一个点,和半径d, 写一个function 返回到这个点的距离小于或等于d 的所有点。怎么优化。我当时答得是把整个地图分成小的区域,每一小块2d的正方形。然后找这个点所在的方块和周围的几个方块。这样就不用遍历地图里所有的点了。

  7. 这一轮和manager聊,学习经历和工作经历,全程聊天穿插一些behavior question。你想给LinkedIn增加哪些新功能。

  8. product and cultule fit。 说一个你最喜欢的应用, 为什么喜欢,还有哪些需要改进的地方。你想给LinkedIn增加哪些新功能(这一轮又问到了)。

  9. 1:00-2:00。还是两位面试官。 系统设计。linkedin新加的功能, 页面的右下角可以发messenger. 对于messenger这个feature, 先列一下有哪些功能要实现。然后对每个功能讲讲怎么实现。

  10. 题目是写一个function实现计算器里的undo以及redo功能。其实就是考数据结构里的stack。最后的拓展题感觉在问system design,问我如果这种undo以及redo操作是用在占据很大存储空间以及用户量很大的data上该怎么办,我说那就给每个数据加上index,undo以及redo只用在index上,最后再用index去取数据。。。

  11. 13:30-14:15,product and cultule fit。这轮主要就是扯淡。。。问到好多UI以及设计方面的问题,比如谈谈你对client side rendering以及server side rendering的理解,说一个你最喜欢的应用并列举下优缺点,LinkedIn还有哪些需要改进的地方等等等等。。。

  12. 主要根据简历的情况,很随机地问了很多behaviour的问题,所以简历里写的东西自己一定要熟悉,manager人好好呀,感觉特别亲和,有活力。

  13. 设计一个系统或者service,包括分析QPS, concurrent user那些,到后台用哪种database存,push or pull,这些,结果完全又不按套路出牌,让设计一个日历,要写detail的HTML和js代码,这一轮的面试小哥非常严肃,提示也比较confusing….楼主表示全程很懵逼,他问的问题也比较随机,看到你写到某个地方的时候,会突然根据你写的提出一个问题,感觉他有点想到什么问什么。

  14. 高频题:地图上显示新注册用户的位置。 很多很多follow up, 比如:更新频率如何设定,短时间大量用户注册怎么处理,后端api返回的数据结构,polling vs socket,是否需要实时更新等等。

  15. 这个是一个小时。主要考验你的是宏观的设计。例如后端该有什么 endpoint,前端什么时候要用这些 endpoint。前端每个 page 该怎么和对方交流。一般会叫你设计一个 App。面试我的时候是给设计 linkedin 的 search。这轮之需要画一些 diagram 和 flowchart 就好,不需要写 code。

  16. 这一轮比较奇葩。要求你找一个你最喜欢的网站,然后讲你为什么喜欢。然后如果你是PM,你会怎么改进那个网站。考的更多的是你对 human computer interaction 的理解。大多数是学校里学 UI 时候学的东西。

  17. Word ladder 1 / 2

  18. 简历

    1.

Do we need PureComponent in Redux project?

Posted on 2019-08-10 | In Full stack | Comments:

I. What

I had such question when I first join my current company. We use react-redux lib, and let redux to manage nearly all data in frontend.

  1. [Fact 1]: We all know that the parent re-render will trigger children component re-render by default (if no shouldComponentUpdate provided in child component, it will always default to true). This way, it might introduce unnecessary re-render for children component even though it should NOT.
  2. [Fact 2]: Redux creating HoC Pure component for its corresponding UI components. That is, container components actually inherit PureComponent (by default, it implements shouldComponentUpdate and return false if props and state stay the same). Thus, there is no need we use pure components for the UI components, as its container components already handle it

  3. [Question]: Does that means we don’t need PureComponent in Redux project? ——>NO

II. Why?

  1. Actually, Redux uses connect() to create HoC Pure component, which actually inherits PureComponent (by default, it implements shouldComponentUpdate and return false if props and state stay the same). All UI components now hide behind the Container components, which are Pure.Components
  2. But, we cannot guarantee all the components have a corresponding container components created by redux. Still, there exist react component not plugging into redux store. In this case, it will trigger child component re-render if the parent component re-render
  3. So, we still need to inherit PureComponent in some case in redux project

III. Code

Play with part_1 and part_2, to see when:

  • child plugging into redux;

  • child not plugging into redux;

  • child not plugging into redux, but with shouldComponentUpdate()

  1. App.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import { Provider } from "react-redux";
    import React from "react";
    import ReactDOM from "react-dom";

    import Parent from "./Parent";
    import store from "./store";

    class App extends React.Component {
    render() {
    return (
    <Provider store={store}>
    <div className="App">
    <Parent />
    </div>
    </Provider>
    );
    }
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
  2. Parent.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import { connect } from "react-redux";
    import React from "react";
    import Child from "./Child";

    let cnt_parent = 0;
    class Parent extends React.Component {
    constructor() {
    super();
    this.state = {
    parentStr: "This is parent String"
    };
    }

    clickHandler = () => {
    this.setState({
    parentStr: "Parent String changed!"
    });
    };

    render() {
    console.log("parent cnt: ", cnt_parent++);
    return (
    <div
    onClick={() => {
    this.clickHandler();
    }}
    >
    {this.state.parentStr}
    <Child />
    </div>
    );
    }
    }

    export default Parent;
  3. Child.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import React from "react";
    import { connect } from "react-redux";
    let cnt_child = 0;

    class Child extends React.Component {
    static defaultProps = { childStr: "This is child String" };
    /*
    // Part_1
    shouldComponentUpdate() {
    return false;
    }
    */
    render() {
    console.log("child cnt: ", cnt_child++);
    return <div>{this.props.childStr}</div>;
    }
    }

    /*
    // Part_2
    const mapStateToProps = state => ({
    childStr: state.child
    });

    const mapDispatchToProps = dispatch => {};

    export default connect(
    mapStateToProps,
    null
    )(Child);
    */

    export default Child;

Backtrack: Find target using '+', '-', '*', '/'

Posted on 2019-08-03 | In categories | Comments:

I. What

Given an list of number (no ‘0’) and a target, combined with any operator from +, -, *, /, check if they can form into an formular which evaluated to be target.

example:

  1. Array: [4, 2, 3, 1, 5, 6]
  2. target: 10
  3. should return true, as [1, "+", 5, "+", 6, "+", 4, "-", 2, "*", 3] = 10

II. Analysis

try to put each element to be the n-th operand, followed by each operator as a potential prefix as follows:

1
2
3
4
							1						2						3						4						5
+ - * /
2 3 4 5
+ - * /

III. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* @param {number[]} nums
* @param {number} S
* @return {number}
*/
var findTargetSumWays = function(nums, t) {
let visited = new Set();
let len = nums.length;
let prefix = [];
let symbols = ['+', '-', '*', '/'];

function helper(prefix) {
if (prefix.length === 2 * len) {
let res = evaluate(prefix, t)
console.log(prefix);
console.log(res);
return res === t;
}

for (let i = 0; i < len; i++) {
if (!visited.has(i)) {
visited.add(i);
prefix.push(nums[i]);
for (let j = 0; j < 4; j++) {
prefix.push(symbols[j]);
if (helper(prefix)) {
return true;
}
prefix.pop()
}
prefix.pop();
visited.delete(i);
}
}
return false;
}

return helper(prefix);

// evaluate such arr [1, "+", 5, "+", 6, "+", 4, "-", 2, "*", 3, "+"]
function evaluate(prefix) {
prefix[prefix.length - 1] = '+';
let operator = [];
let operand = [];

operand.push(0);
operator.push('+');
operand.push(prefix[0]);

for (let i = 1; i < prefix.length; i += 2) {
let symbol = prefix[i]
let digit = prefix[i + 1];
let lastSymbol = operator[operator.length - 1];
if (compare(symbol, lastSymbol) > 0) {
let digitPre = operand.pop();
operand.push(evaluate(digitPre, symbol, digit));
} else {
let symbolPre = operator.pop();
let digit2 = operand.pop();
let digit1 = operand.pop();
operand.push(evaluate(digit1, symbolPre, digit2));
operand.push(digit);
operator.push(symbol);
}
}
operand.pop()
return operand.pop();

function evaluate(d1, s, d2) {
switch (s) {
case '+': return d1 + d2;
case '-': return d1 - d2;
case '*': return d1 * d2;
case '/': return d1 / d2;
default: return null;
}
}
}


// compare operator priority
function compare(s1, s2) {
return evaluate(s1) - evaluate(s2);
function evaluate(s) {
if (s === '*' || s === '/') {
return 1;
}
return 0;
}
}
};

let nums = [1, 5, 6, 2, 3, 4];
let target = 10;
let res = findTargetSumWays(nums, target);

console.log(res);

Priority Queue/Binary Heap Implementation in JS

Posted on 2019-07-29 | In Algorithm | Comments:

I. Why?

Because in JS, there is NO such useful build-in data structure🤣…….

[Great Links]: https://www.cs.usfca.edu/~galles/visualization/Heap.html

II. Time Complexity

  1. construct: O(n)
  2. peek: O(1)
  3. offer: O(lgn)
  4. poll: O(lgn)

III. Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

/**
* Priority Queue
*
* Binary Heap implementation
*
* clear: Removes all of the elements from this priority queue.
* offer: Inserts the specified element into this priority queue.
* peek: Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
* poll: Retrieves and removes the head of this queue, or returns null if this queue is empty.
* size: Returns the number of elements in this collection.
* toArray: Returns an array containing all of the elements in this queue.
*/

class BinomialHeap {
constructor({
comparator = (a, b) => a - b,
initialValues = []
} = {}) {
this.comparator = comparator;
this.data = initialValues;
this.heapify();
}


heapify() {
if (this.isEmpty()) {
return;
}
for (let i = 1; i < this.size(); i++) {
this.bubbleUp(i);
}
}

offer(newNode) {
this.data.push(newNode);
this.bubbleUp(this.size() - 1);
}

poll() {
if (this.isEmpty()) {
return null;
}

let res = this.data[0];
let last = this.data.pop();

if (this.size() > 0) {
this.data[0] = last;
this.sinkDown(0);
}
return res;
}

peek() {
if (this.isEmpty()) {
return null;
}
return this.data[0];
}

isEmpty() {
return this.size() === 0;
}

size() {
return this.data.length;
}

toArray() {
return this.data.slice();
}

clear() {
this.data = [];
}

bubbleUp(pos) {
while (pos > 0) {
let parentIndex = (pos - 1) >>> 1;
if (this.comparator(this.data[parentIndex], this.data[pos]) > 0) {
[this.data[parentIndex], this.data[pos]] = [this.data[pos], this.data[parentIndex]];
pos = parentIndex;
} else {
break;
}
}
}

sinkDown(pos) {
let size = this.size();
while (true) {
let left = (pos << 1) + 1;
let right = left + 1;
let minIndex = pos;

if (left < size && this.comparator(this.data[left], this.data[minIndex]) < 0) {
minIndex = left;
}

if (right < size && this.comparator(this.data[right], this.data[minIndex]) < 0) {
minIndex = right;
}

if (minIndex !== pos) {
[this.data[minIndex], this.data[pos]] = [this.data[pos], this.data[minIndex]];
pos = minIndex;
} else {
break;
}
}
}
}



// TEST cases:
class Node{
constructor(k, v) {
this.k = k;
this.v = v;
}
}

let pq = new BinomialHeap({
comparator: (n1, n2) => n1.v - n2.v,
initialValues: [
new Node('c', 3),
new Node('f', 6),
new Node('b', 2),
new Node('d', 4),
new Node('a', 1),
new Node('e', 5),
new Node('g', 7),
]
});

console.log("toArray(): should be [1, 2, 3, 6, 4, 5, 7] ", pq.toArray());
console.log("smallest should be: Node {k: 'a', v: 1} ", pq.peek());
console.log("size should be 7 ", pq.size());

let smallest = pq.poll();
console.log("smallest should be: Node {k: 'a', v: 1} ", smallest);
console.log("size should be 6 ", pq.size());
console.log("toArray(): should be [2, 4, 3, 6, 7, 5] ", pq.toArray());

pq.offer(new Node('aa', 1));
console.log("toArray(): should be [1, 4, 2, 6, 7, 5, 3] ", pq.toArray());
console.log("size should be 7 ", pq.size());

JS Inheritance Insights

Posted on 2019-04-27 | In Full stack | Comments:

I. Best Practice for Object Inheritance

What a “class” is made of in JavaScript:

  1. [private stuff] The constructor function. This function contains all the logic to create an instance of the “class”, i.e. instance specific code .
  2. [public stuff] The prototype object. This is the object the instance inherits from. It contains all methods (and other properties) that should be shared among all instances.

The constructor corresponds to private, and prototype corresponds to public in Java

Steps to inherit from another object:

  1. consturctor inherit

  2. prototype inherit

    • Benefits of using Object.create(Person) for inheritance rather than new Person()?

    • Actully this works because an Person instance inherits from the Person prototype object. But it also implies that every dog inherits from one specific Person instance. That seems to be a bit strange. Shouldn’t instance specific code only be run in the constructor function? Suddenly instance specific code and prototype methods seem to be mixed.

      We don’t actually want to run Person instance specific code at that moment, we only want all the methods from the Person prototype object. That is what Object.create lets us do

  3. prototype constructor correct

    • Why is it necessary to set the prototype constructor?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* parent object: Person */
// 1.1 constructor
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
};
// 1.2 prototype
Person.prototype.greeting = function() {
alert('Hi! I\'m ' + this.name.first + '.');
};

const John = new Person('John', 'Doe', 12, 'male', 'pingpong');

/* child object: Teacher */
// 2.1 constructor inherit
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
// 2.2 prototype inherit
Teacher.prototype = Object.create(Person.prototype);
// 2.3 prototype constructor correct
Teacher.prototype.constructor = Teacher;
const TeacherSam = new Teacher('Teacher', 'Sam', 43, 'male', 'teaching', 'math');

II. prototype vs __proto__

  1. in JS, everything is an object except primitive value. objects can serve 2 goals:

    • prototype / class for other sub-object / sub-class. In this case, we can check prototype of a constructor‘s shared parent object [ie. prototype]
    • sub-object / sub-class of other prototype / class
    • NOTE:
      • an object created by new (constructor) doesn’t has prototype property, since it’s created with the hope to get an pure object!
      • If this object wants to have prototype property, then it must be the case we want this object to be a ‘super-class’, and inherited by ‘sub-class’. This way, we will need to attach prototype to this object. Also, we need to create shared field or method onto prototype (otherwise, if nothing inside prototype, are we dumb to create all fields in memory without putting shared stuff inprototype??? )
      • On the other hands, an object created by new (constructor) always has __proto__ property, in which we can know:
        1. the constructor of this object
        2. other shared methods or fields of its prototype
      • An prototype will always have a constructor property, to tell its corresponding U associated to this ‘class’ prototype
  2. prototype is the object that is used to build __proto__ when you create an object with new. It is used by constructor() functions. It should’ve really been called something like, “prototypeToInstall”, since that’s what it is.

  3. __proto__ is the actual object that is used in the lookup chain to resolve methods, pointing to its prototype. It is that “installed prototype” on an object (that was created/installed upon the object from said constructor() function)

    1
    2
    ( new Foo ).__proto__ === Foo.prototype;
    ( new Foo ).prototype === undefined;

Reference

  1. https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
  2. https://stackoverflow.com/questions/17392857/benefits-of-using-object-create-for-inheritance
  3. https://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor
  4. https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript

Diagonally Print a Square Array

Posted on 2019-01-25 | In Algorithm | Comments:

Description

Given an sqaure array (n*n) of integers, print elements diagonally .

Example

1
2
3
4
5
6
7
8
9
10
11
12
let nums = [
[1,2,3],
[4,5,6],
[7,8,9]
]

should print the following:
1
2 4
3 5 7
6 8
9

Solution: O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function printDiagnally(input){
let n = input.length // 3
let lines = 2*(n-1) + 1 // 5

// determine how many diagonal lines
for( let sum = 0; sum < lines; sum++ ){

// determine the starting x-coord, and starting y-coord
let x0 = sum < n ? sum : n-1
let y0 = sum - x0
let num = Math.abs(x0 - y0) +1 // how many elements in this line
let str = ""

// concat all elements in this line
for( let x = x0; x > x0-num; x-- ){
let y = sum - x
str = input[x][y] + " "+str
}
console.log(str)
}
}

// ---------- Testing: ------------
console.log("Test1: ")
let input = [[1]]
printDiagnally(input)


console.log("Test2: ")
input = [
[1, 2],
[3, 4]
]
printDiagnally(input)


console.log("Test3: ")
input = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
printDiagnally(input)

console.log("Test4: ")
input = [
[1, 2, 3, 4 ],
[5, 6, 7, 8 ],
[9, 10, 11, 12],
[13, 14, 15, 16],
]
printDiagnally(input)
12…9
Mingkai Cao

Mingkai Cao

All-trade Jack, loves full-stack

85 posts
5 categories
136 tags
GitHub E-Mail
© 2020 Mingkai Cao
Powered by Hexo
|
Theme — NexT.Mist v6.0.6