Front-end Notes

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.