CMK

Let's create something!


  • Home

  • Categories

  • Tags

  • Archives

  • About

A Way to Make an Efficient React Timer

Posted on 2018-10-05 | In Full stack | Comments:

I. The Problem

It is very common we need to implement a Timer in our daily projects. Of course, there are several ways to make one.

Most online resource only introduces how to make a Timer as the only one component itself, while in reality we want our Timer to be an sub-component of some parent component

We don’t want to put the time_elapsed state in the state of its parent component. That way, every one seconds, the parent component will rerender over and over, even though all other state field stay the same.

II. We Want Efficiency

Here, the basic idea is:

  1. we put the time_elapsed state in a single component with no other state field in it.
    • start hanler = setInterval() to update the time_elapsed , in componentDidMount() stage
    • pass out the handler to its parent component
  2. we also want the parent component can startTimer and stopTimer
    • we need to get the handler of the setInterval(), in order to stop it in outside
    • BUT, how to get the handler??? ==> we pass in an object as props to timer component

III. Implementation

  1. 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
    <html>
    <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
    </head>
    <body>
    <div id="root"></div>
    <script type="text/jsx">

    class Timer extends React.Component{
    constructor(props){
    super(props)
    this.state={ currTime: 0 }
    }

    componentDidMount (){
    this.props.timerObj.handler = setInterval(()=>{
    this.setState({ currTime: this.state.currTime+1000 })
    }, 1000)
    }

    render(){
    console.log("Inside Timer Component: ", this.state)
    const timePassed = this.state.currTime / 1000
    return <span>{timePassed}</span>
    }
    }


    class App extends React.Component{
    constructor(props){
    super(props)
    this.state = { showTimer: false }
    this.timerObj = { handler: null }
    }

    startTimer = ()=>{
    this.setState({ showTimer: true })
    }

    stopTimer = ()=>{
    clearInterval(this.timerObj.handler)
    this.setState({showTimer: false})
    }


    render(){
    console.log("App Component: ", this.state)
    const TimerPlaceholder = this.state.showTimer ? <Timer timerObj={this.timerObj} /> : null
    return (
    <div>
    <h1> React Inner Timer Component</h1>
    <button onClick={this.startTimer}> startTimer </button>
    <br /><br />

    Time Passed: {TimerPlaceholder }

    <br /><br />
    <button onClick={this.stopTimer}> stopTimer </button>
    </div>
    )
    }
    }


    ReactDOM.render(
    <App />,
    document.getElementById('root')
    );
    </script>
    </body>
    </html>

Flux & Redux

Posted on 2018-10-04 | In Full stack | Comments:

I. Flux

  1. 4 parts of Flux architecture:

    • View
    • Actions
    • Dispatcher
    • Store
  2. Relationship

    • Store: model object managing all data; has updating methods as well
      • provide data to View
      • provide updating methods to Actions
    • Dispatcher: extends Event Emitter object
      • used by component to register event & callbacks
      • used by component to emit actions to call store updating methods
    • View: receive data from store
    • Actions: used by component to let dispather emit actions
    • Great article: http://www.ruanyifeng.com/blog/2016/01/flux.html

II. Redux

  1. store

    1. getState(): source of data
    2. dispath(): emit actions, execute reducer to update state
    3. subscribe(): component subscribe to receive updated state
  2. reducer

    • likeemitter.on, used to register all the updating functions for related actions
    • pure function
    • combineReducers(): used to make logic clean
  3. Store implementation

    1. How to use the store object

    2. 1
      2
      3
      4
      5
      6
      7
      import { createStore } from 'redux';
      let { subscribe, dispatch, getState } = createStore(reducer);
      // or
      let store = createStore(todoApp, window.STATE_FROM_SERVER)
      store.getState()
      store.dispatch()
      store.subscribe()
    3. redux provide an interface createStore() to create the store object

    4. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      const createStore = (reducer) => {
      let state;
      let listeners = [];

      const getState = () => state;

      const dispatch = (action) => {
      state = reducer(state, action);
      listeners.forEach(listener => listener());
      };

      const subscribe = (listener) => {
      listeners.push(listener);
      return () => {
      listeners = listeners.filter(l => l !== listener);
      }
      };

      dispatch({});

      return { getState, dispatch, subscribe };
      };
  4. An example using Redux

  5. 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
    const Counter = ({ value, onIncrement, onDecrement }) => (
    <div>
    <h1>{value}</h1>
    <button onClick={onIncrement}>+</button>
    <button onClick={onDecrement}>-</button>
    </div>
    );

    const reducer = (state = 0, action) => {
    switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
    }
    };

    const store = createStore(reducer);

    const render = () => {
    ReactDOM.render(
    <Counter
    value={store.getState()}
    onIncrement={() => store.dispatch({type: 'INCREMENT'})}
    onDecrement={() => store.dispatch({type: 'DECREMENT'})}
    />,
    document.getElementById('root')
    );
    };

    render();
    store.subscribe(render);

III. React-Redux package

  1. connect():
    • connect UI component with it outer Controller component
    • usage: const Contrainer = connect(mapStateToprops, mapDispatchToProps)(UIComponent)
  2. mapStateToProps()
    • map state in Redux store to UI
    • like eventEmitter.subscribe(), will always get the updated state from Redux
  3. mapDispatchToProps

    • map actions dispatch() in Redux to UI
    • like eventEmitter.emit(), will trigger actions when UI event is fired, to execute reducer to update the state
  4. <Provider> component

    • an outer container, used to pass the state to inner components
    • using context attribute

Create new Objects with Inheritance

Posted on 2018-10-04 | In Full stack | Comments:

I. { }: Literal Constructor

  1. 1
    2
    3
    4
    5
    6
    var person = {
    name = "Anand",
    getName = function(){
    return this.name
    }
    }

II. Object.create(): can be used on inheritance

  • This can create a new object from another object as it prototype

  • More efficient to use a prototype if you are going to have multiple objects sharing properties or methods.

  • 1
    2
    3
    4
    5
    let proto = {name: "default", foo: function() {}}
    let obj1 = Object.create(proto); obj1.name = "a"
    let obj2 = Object.create(proto); obj1.name = "b"
    let obj3 = Object.create(proto); obj1.name = "c"
    let obj4 = Object.create(proto); obj1.name = "d"

III. Function Constructor with this & new

  • Bad thing: every time, when we create an new object, it will occupy space for all attributes of Person
  • Using Prototype

  • 1
    2
    3
    4
    5
    6
    7
    8
    function Person(name){
    this.name = name
    this.getName = function(){
    return this.name
    }
    }

    let mycar = new Person('Eagle');

IV. Function Constructor & Prototype

  • prototype is a shared object, where all its inheriting objects will share this same space, won’t create extra space for attributes inherited from prototype

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function Graph() {
    this.vertices = [];
    this.edges = [];
    }

    Graph.prototype = {
    addVertex: function(v) {
    this.vertices.push(v);
    }
    };

    var g = new Graph();
    // g is an object with own properties 'vertices' and 'edges'.
    // g.[[Prototype]] is the value of Graph.prototype when new Graph() is executed.

V. ES6 class

  1. introduced a new set of keywords implementing classes. The new keywords include class, constructor, static, extends, and super.

  2. all functions except constructor are put into the prototype of that object (Square)

  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Polygon {
    constructor(height, width) {
    this.height = height;
    this.width = width;
    }
    }

    // inherit from another class
    class Square extends Polygon {
    constructor(sideLength) {
    super(sideLength, sideLength);
    }
    get area() {
    return this.height * this.width;
    }
    set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
    }
    }

    // Use new to create object from class
    var square = new Square(2);

VI. Singleton: one object of a kind

  • Singleton is used on class-based language

  • In jS, All objects created using literal constructor are singletons

    • 1
      2
      3
      4
      5
      6
      7
      8
      var Robot = {
      metal: "Titanium",
      killAllHumans: function(){
      alert("Exterminate!");
      }
      };

      Robot.killAllHumans();

Note of Eloquent JavaScript - [Browser]

Posted on 2018-10-03 | In Full stack | Comments:

I. DOM

  1. what: document object model

  2. who&why:

    • the browser build the DOM
    • used to draw the page on screen
    • provide interface to JS to dynamically manipulate elements on the page
  3. How to access it: document object

    • document.documentElement => ‘ …‘
  4. moving throught the DOM tree

    • traverse only element nodes
      • children
    • traverse all node types: element node, text node, comment node, …
      • childNodes
      • parentNode
      • firstChild
      • lastChild
      • previousSibling
      • nextSibling
  5. traverse tree e.g.:

    • recursive functions are often useful. The following function scans a document for text nodes containing a given string and returns true when it has found one:

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        function talksAbout(node, string) {
        if (node.nodeType == Node.ELEMENT_NODE) {
        for (let i = 0; i < node.childNodes.length; i++) {
        if (talksAbout(node.childNodes[i], string)) {
        return true;
        }
        }
        return false;
        } else if (node.nodeType == Node.TEXT_NODE) {
        return node.nodeValue.indexOf(string) > -1;
        }
        }

        console.log(talksAbout(document.body, "book"));
        // → true
  6. find element

    1. document.querySelector("idName");

      • first Element within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
      • var el = document.querySelector(".myclass");
      • var el = document.querySelector("div.user-panel.main input[name='login']");
        • Here, the first <input> element with the name “login” (<input name="login"/>) located inside a <div> whose class is “user-panel main” (<div class="user-panel main">) in the document is returned.
    2. document.getElementById("idName");

    3. document.getElementsByTagName("idName");

    4. document.getElementsByClassName("idName");

    5. document.getElementsByName() when inside html we have <input name="down">

  7. change document

    1. 1
      2
      3
      4
      <script>
      let paragraphs = document.body.getElementsByTagName("p");
      document.body.insertBefore(paragraphs[2], paragraphs[0]);
      </script>
  8. create nodes

    1. document.createTextNode(image.alt);

    2. image.parentNode.replaceChild(text, image);

    3. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <p>The <img src="img/cat.png" alt="Cat"> in the
      <img src="img/hat.png" alt="Hat">.</p>

      <p><button onclick="replaceImages()">Replace</button></p>

      <script>
      function replaceImages() {
      let images = document.body.getElementsByTagName("img");
      for (let i = images.length - 1; i >= 0; i--) {
      let image = images[i];
      if (image.alt) {
      let text = document.createTextNode(image.alt);
      image.parentNode.replaceChild(text, image);
      }
      }
      }
      </script>
  9. attributes

    • HTML allows setting any attribute you want on nodes. <p data="52">
    • setAttribute & getAttribute

    • original attributes: href, class, style

    • custom attributes: data, myown

II. CSS

  1. CSS specificity

    • inline_style=1000

    • id=100

    • class=10

    • element=1

    • e.g.

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        A: h1
        B: #content h1
        C: <div id="content"><h1 style="color: #ffffff">Heading</h1></div>

        The specificity of A is 1 (one element)
        The specificity of B is 101 (one ID reference and one element)
        The specificity of C is 1000 (inline styling)

        Since 1 < 101 < 1000, the third rule (C) has a greater level of specificity, and therefore will be applied.
  2. multiple class name for element: <p class="leftAlign title"> content </p>

  3. e.g.:

    1. 1
      2
      3
      4
      /* p elements with id 'main' and with classes a and b */
      p#main.a.b {
      margin-bottom: 20px;
      }
    2. p > a {…} applies the given styles to all <a> tags that are direct children of <p> tags.

    3. p a {…} applies to all <a> tags inside <p>tags, whether they are direct or indirect children.

  4. position & animation

    1. position: transform: used for rotation, move, skew

      • translate(x, y): horizontally and vertically move
      • translateX(x): horizontally move
      • translateY(y): vertically move
      • scale(x,y): zoom in & out according to x-axis/ y-axis
      • rotate(90deg): num&deg: the angle is specified in the parameter
      • skew(x-angle,y-angle): num&deg: the angle is specified in the parameter
    2. animation:

      • animation-name: mymove;, mymove is created using @keyframes rule specifies the animation code

      • animation-duration:

      • animation-delay:

      • animation-iteration-count: 2:

      • e.g.

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        div {
        width: 100px;
        height: 100px;
        background: red;
        position: relative;
        -webkit-animation-name: mymove; /* Safari 4.0 - 8.0 */
        -webkit-animation-duration: 5s; /* Safari 4.0 - 8.0 */
        animation-name: mymove;
        animation-duration: 5s;
        }

        /* Safari 4.0 - 8.0 */
        @-webkit-keyframes mymove {
        from {left: 0px;}
        to {left: 200px;}
        }

        @keyframes mymove {
        from {left: 0px;}
        to {left: 200px;}
        }

III. event handler

  1. event object

    • event handler functions are passed an argument: event object
    • event.target.innerHTML or event.target.value
  2. two ways to handle event

    • onclick attribute: But can only add one handler, since a node can have only one onclick attr

    • addEventListener(event, callback): can add many handlers

    • removeEventListener(event, callback): will remove handler on this event

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        <button>Act-once button</button>
        <script>
        let button = document.querySelector("button");
        function once() {
        console.log("Done.");
        button.removeEventListener("click", once);
        }
        button.addEventListener("click", once);
        </script>
  3. event loop

    • Never put too much synchronous work inside asynchronous callback! It will make page freezing!
    • If have to, use the web worker: is a JS process that runs alongside the main script, on its own timeline
  4. propagation

    • the event will like ‘ bubble‘, propagation upward to elements who also registered for this event

      • 1
        2
        3
        4
        5
        6
        // when inner div clicked, both "fun_2", "fun_1" will be called!!
        <div id='outer' onclick="fun_1()">
        <div id='inner' onclick="fun_2()" >

        <div>
        </div>
    • stopPropagation can prevent bubbling, prevent handlers further up from receiving the event

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        <p>A paragraph with a <button>button</button>.</p>
        <script>
        let para = document.querySelector("p");
        let button = document.querySelector("button");
        para.addEventListener("mousedown", () => {
        console.log("Handler for paragraph.");
        });
        button.addEventListener("mousedown", event => {
        console.log("Handler for button.");
        event.stopPropagation();
        });
        </script>
  5. Default action

    • [Important]: JavaScript event handlers are called before the default behavior takes place.

    • If you click a link, you will be taken to the link’s target.

    • If you press the down arrow, the browser will scroll the page down.

    • If you right-click, you’ll get a context menu.

    • 1
      2
      3
      4
      5
      6
      7
      8
      <a href="https://developer.mozilla.org/">MDN</a>
      <script>
      let link = document.querySelector("a");
      link.addEventListener("click", event => {
      console.log("Nope.");
      event.preventDefault();
      });
      </script>
  6. event type:

    1. key

    2. mouse

    3. touchscreen

    4. scroll

      • Whenever an element is scrolled, a "scroll" event is fired on it

      • e.g.: draws a progress bar above the document and updates it to fill up as you scroll down

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          <style>
          #progress {
          border-bottom: 2px solid blue;
          width: 0;
          position: fixed;
          top: 0; left: 0;
          }
          </style>
          <div id="progress"></div>
          <script>
          // Create some content
          document.body.appendChild(document.createTextNode(
          "supercalifragilisticexpialidocious ".repeat(1000)));

          let bar = document.querySelector("#progress");
          window.addEventListener("scroll", () => {
          let max = document.body.scrollHeight - innerHeight;
          bar.style.width = `${(pageYOffset / max) * 100}%`;
          });
          </script>
      • scrollHeight: ENTIRE content & padding (visible or not)

      • offsetHeight: VISIBLE content & padding + border + scrollbar

      • clientHeight: VISIBLE content & padding

      • https://stackoverflow.com/questions/22675126/what-is-offsetheight-clientheight-scrollheight

      • outerHeight: height in pixels of the whole browser window, browser’s out-most height

      • innerHeight: Height in pixels of the browser window viewport including the horizontal scrollbar

      • https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight

    5. focus event

      1. focus & blur

      2. e.g.: prompt

        1. 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          <p>Name: <input type="text" data-help="Your full name"></p>
          <p>Age: <input type="text" data-help="Your age in years"></p>
          <p id="help"></p>

          <script>
          let help = document.querySelector("#help");
          let fields = document.querySelectorAll("input");
          for (let field of Array.from(fields)) {
          field.addEventListener("focus", event => {
          let text = event.target.getAttribute("data-help");
          help.textContent = text;
          });
          field.addEventListener("blur", event => {
          help.textContent = "";
          });
          }
          </script>

IV. Timers

  1. handler = setTimeout(callback, time)
  2. clearTimeout(handler)
  3. handler = setInterval(callback, time)
  4. clearInterval(handler)

  5. e.g.:

    1. make a bomb: cancel a function you have scheduled

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        // 1. make it explods after 500ms
        let bombTimer = setTimeout(() => {
        console.log("BOOM!");
        }, 500);

        // 2. since synchronous, can clear timer before it explods
        if (Math.random() < 0.5) {
        console.log("Defused.");
        clearTimeout(bombTimer);
        }
    2. make a ticker:

      1. 1
        2
        3
        4
        5
        6
        7
        8
        let ticks = 0;
        let clock = setInterval(() => {
        console.log("tick", ticks++);
        if (ticks == 10) {
        clearInterval(clock); // can access outter var: clock
        console.log("stop.");
        }
        }, 200);

V. Debounce / Throttle

  1. why Debounce & Throttling?

    • Debounce: For events like keydown, scroll, we don’t want to trigger event in the middle of it, but only want to trigger after user pause! (ie. we only care the final result)
    • Throttling: For events like mouseover, we don’t want to trigger event every for every single move, but only want to trigger it every 200ms if such event happens (ie. we only care sample results)
  2. Debounce e.g. : debouce keydown [ setTimeout() ]

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 1. clear last timer for each keydown event, 
      // 2. execute callback function after setting time

      // This way, if two keydown events happen less than 500ms,
      // the first one will be cleard, won't execute the callback function
      <textarea>Type something here...</textarea>
      <script>
      let textarea = document.querySelector("textarea");
      let timeout;
      textarea.addEventListener("input", () => {
      clearTimeout(timeout);
      timeout = setTimeout(() => {console.log("Typed!"); }, 500);
      });
      </script>
  3. Throttling e.g.: throttle mouseover[ setTimeout() ]

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 1. scheduled first sampling
      // 2. only when scheduled, we execute function;
      // - set scheduled to be false
      // - after 1000ms, set scheduled to be true
      <script>
      let scheduled = true;
      window.addEventListener("mousemove", event => {
      if (scheduled) {
      document.body.textContent = `Mouse at ${event.pageX}, ${event.pageY}`;
      scheduled = false;
      setTimeout(() => { scheduled = true; }, 1000);
      }
      });
      </script>
  4. Debouncify a function

    • Given a function and a delay period, we want return a debouncified function:

      • whenever called, will only be executed after the specified delay.
      • Ignore calls in the middle of the delay, and reschedule the starting time of delay
      • must receive variant number of arguments
    • code implementation

      • ‘closure‘ is used here: the returned function have access to its outer function var: timeout_handler, and modify it by the returned functions

      • 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
        /* Each time the returned function is called,
        * First clear the previous scheduled 'func' call
        * Then, reschedule a new one
        * <NOTE> we used 'closure' here: the returned function have
        * access to its outer function var: timeout_handler, and modify
        * it
        */
        function debouncify( func, delay ){
        let timeout_handler
        return function(){ // Closure is used here
        clearTimeout(timeout_handler)
        timeout_handler = setTimeout(function(arguments){
        func(arguments)
        }, delay)
        }
        }

        // test
        function printTime(){
        console.log(new Date().toLocaleTimeString());
        }

        let printTimeAfterFiveSecs = debouncify(printTime, 5000)
        // need to call
        console.log(new Date().toLocaleTimeString()) // get the start time
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs()
        printTimeAfterFiveSecs() // should be 5sec after the start time

Note of Eloquent JavaScript - [Pure JS]

Posted on 2018-10-02 | In Full stack | Comments:

I. Value, Type, Operators

  1. Object, Number, String, Boolean, null, undefined, Symbol

    • Symbol: used for global unique value in Map: https://zhuanlan.zhihu.com/p/22652486
  2. typeof will return lowercase string type

    • typeof null => "object"
    • typeof undefined => "undefined"
    • typeof 5 => "number"
    • typeof Number("4") => "number"
    • typeof NaN => "number"
    • typeof Infinity => "number"
    • typeof [1,2] => "object"
  3. comparsion

    There is only one value in JavaScript that is not equal to itself, and that is NaN(“not a number”).

    1
    console.log(NaN == NaN)  // false !!!!
  4. operator used on string / null / number

    1
    2
    3
    4
    5
    6
    console.log(8 * null)   // → 0
    console.log("5" - 1) // → 4
    console.log("5" + 1) // → "51" !!!
    console.log("5" * 1) // → 5
    console.log("five" * 2) // → NaN
    console.log(false == 0) // → true
  5. when null or undefined occurs on either side of the operator, it produces true only if both sides are one of null or undefined.

    1
    2
    3
    4
    console.log(null == undefined);
    // → true
    console.log(null == 0);
    // → falseProgram Structure

II. Program Structure

  1. scope: Each variable has a scope, which is the part of the program in which the binding is visible.

    • all variables in JS have its own scope, we can use this to refer it
    • useful in Closure
  2. variable :are reference, think it as tentscles, not boxes

  3. function (Important List Inside):

    • optional arguments:

      • If you pass too many, the extra ones are ignored.
      • If you pass too few, the missing parameters get assigned the value undefined.
        • use default value like this: function power(base, exponent = 2) {...}
      • How to call a pass a function with variant num of parameters?: funName(...arg)
    • function expression

      1
      2
      3
      4
      // Define f to hold a function value
      const f = function(a) {
      console.log(a + 2);
      };
    • function declaration

      1
      2
      3
      4
      // Declare g to be a function
      function g(a, b) {
      return a * b * 3.5;
      }
    • arrow function: automatically bind its scope to this inside the function (variables with implicit this)

      1
      2
      // A less verbose function value
      let h = a => a % 3;
    • Closure

      • What happens to local bindings when the function call that created them is no longer active?
      • This feature—being able to reference a specific instance of a local variable in an enclosing scope—is called [closure]
      • Local variables inside the outer function are created again for every call
      • Different calls can’t trample on one another’s local bindings.
    • built-in functions

      • prompt() can get user’s input using a popup box: let inputName = prompt("please input your name")

      • console.log() can also print object, console.log("this obj is", obj), instead of console.log("this obj is" + obj)

III. Data Structures: Objects

  1. Property of Object** :

    • Almost all JavaScript values have properties. The exceptions are null and undefined.
    • 2 ways to access properties:
      • obj.x
      • obj[a] // a = “x”
  2. Method of Object:

    • 2 ways to call methods:
      • obj.x
      • obj[a] (val) // a = “x”
  3. Mutability:

    • Tip1: Regardless of whether you use regular or strict equality, object comparisons only evaluate to true if you compare the same exact object.

    • Why we need Mutability? - since we want want one obj affect others when we change one of them

      1
      2
      3
      4
      5
      let object1 = {value: 10};
      let object2 = object1;

      object1.value = 15; // we update the value of object1
      console.log(object2.value); // the value of object2 also changed, which is not what we want!
    • How to keep Mutability?

      1
      2
      3
      4
      5
      let object1 = {value: 10};
      let object2 = {...object1};

      object1.value = 15; // we update the value of object1
      console.log(object2.value); // the value of object2 also changed, which is not what we want!

IV. Data Structures: Array

  1. for...of vs for...in vs forEach() :

    • for..in operates on any object; it serves as a way to inspect enumerable properties on this object

    • for...of : mainly used to iterate only iterable object, it serves as a way to inspect all iterable entries of this object; It cannot used on Object, can be only used on Map, Set, Array, String

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      //  The e.g.

      Object.prototype.objCustom = function() {};
      Array.prototype.arrCustom = function() {};

      let iterable = [3, 5, 7];
      iterable.foo = 'hello';

      for (let i in iterable) {
      console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
      }

      for (let i in iterable) {
      if (iterable.hasOwnProperty(i)) {
      console.log(i); // logs 0, 1, 2, "foo"
      }
      }

      for (let i of iterable) {
      console.log(i); // logs 3, 5, 7
      }
    • forEach(): is a method only belonging to the royal family of Arrays, including Map, Set (since Map, Set are implemented using Array). It iterate iterable elements in an array. It mainly depends on its callback function

      • map.forEach( callback(val, key, map) )
      • array.forEach( callback(val, index, array) )
      • set.forEach( callback(val, key, set) )
  2. common methods

    • includes(): determines whether an array includes a certain element, returning true or false as appropriate.

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        var array1 = [1, 2, 3];

        console.log(array1.includes(2));
        // expected output: true

        var pets = ['cat', 'dog', 'bat'];

        console.log(pets.includes('cat'));
        // expected output: true

        console.log(pets.includes('at'));
        // expected output: false
    • forEach()

    • map()

    • reduce()

    • filter()

    • push(): push on end

    • pop(): pop from end

    • shift(): remove from front

    • unshift(): add to front

    • sort()

    • reverse()

    • slice(): return a new array (sub-array)

    • splice(): modify current array (current array)

    • join(): returns a new string by concatenating all of the elements in an array

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        var elements = ['Fire', 'Wind', 'Rain'];

        console.log(elements.join());
        // expected output: Fire,Wind,Rain

        console.log(elements.join(''));
        // expected output: FireWindRain

        console.log(elements.join('-'));
        // expected output: Fire-Wind-Rain
    • concat()

    • indexOf()

    • lastIndexOf()

    • find(): returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.

      • 1
        2
        3
        4
        5
        6
        7
        8
        var array1 = [5, 12, 8, 130, 44];

        var found = array1.find(function(element) {
        return element > 10;
        });

        console.log(found);
        // expected output: 12
    • findIndex(): returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1

      • 1
        2
        3
        4
        5
        6
        7
        8
        var array1 = [5, 12, 8, 130, 44];

        function findFirstLargeNumber(element) {
        return element > 13;
        }

        console.log(array1.findIndex(findFirstLargeNumber));
        // expected output: 3
    • values(): returns a new Array Iterator object that contains the values for each index in the array

      • 1
        2
        3
        4
        5
        6
        const array1 = ['a', 'b', 'c'];
        const iterator = array1.values();

        for (const value of iterator) {
        console.log(value); // expected output: "a" "b" "c"
        }
    • fill(): fills all the elements of an array from a start index to an end index with a static value. The end index is not included.

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        var array1 = [1, 2, 3, 4];

        // fill with 0 from position 2 until position 4
        console.log(array1.fill(0, 2, 4));
        // expected output: [1, 2, 0, 0]

        // fill with 5 from position 1
        console.log(array1.fill(5, 1));
        // expected output: [1, 5, 5, 5]

        console.log(array1.fill(6));
        // expected output: [6, 6, 6, 6]
    • flat(): creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        var arr1 = [1, 2, [3, 4]];
        arr1.flat();
        // [1, 2, 3, 4]

        var arr2 = [1, 2, [3, 4, [5, 6]]];
        arr2.flat();
        // [1, 2, 3, 4, [5, 6]]

        var arr3 = [1, 2, [3, 4, [5, 6]]];
        arr3.flat(2);
        // [1, 2, 3, 4, 5, 6]

IV. Data Structures: Map

  1. internal structure
    • new Map([iterable])
    • iterable: key-value pairs (arrays with two elements, e.g. [[ 1, 'one' ],[ 2, 'two' ]])
    • new Map( [ ["a", 1], ["b", 2], ["c", 3], ["d", 4] ] )
  2. Map vs Object
    • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.
    • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.
    • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.
    • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion and iterating over them.
    • An Object has a prototype, so there are default keys in the map that could collide with your keys if you’re not careful. As of ES5 this can be bypassed by using map = Object.create(null), but this is seldom done.
    • A Map may perform better in scenarios involving frequent addition and removal of key pairs.

V. Higher-Order Functions

  1. what?

    • Functions that operate on other functions, either by taking them as arguments or as return value, are called higher-order functions
  2. why? => handle actions, not just values

    • eg1 : use function as arguments

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      function add(obj){ obj += 1 };
      function substract(obj){ obj -= 1};

      function repeatAction(times, action) {
      for (let i = 0; i < times; i++) {
      action(i);
      }
      }

      repeatAction(5, add );
      repeatAction(5, substract );
    • eg2 : use function as return value

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // utility function: returns another function,
      // can check if a number > certain number

      function greaterThan(n) {
      return m => m > n;
      }

      let greaterThan10 = greaterThan(10);
      console.log(greaterThan10(11));
      // → true
    • eg3:

    • 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
      // ----------- 1. filter() -----------
      function filter(array, test) {
      let passed = [];
      for (let element of array) {
      if (test(element)) {
      passed.push(element);
      }
      }
      return passed;
      }

      console.log(filter(SCRIPTS, script => script.living));
      // → [{name: "Adlam", …}, …]


      // ----------- 2. map() -----------
      function map(array, transform) {
      let mapped = [];
      for (let element of array) {
      mapped.push(transform(element));
      }
      return mapped;
      }

      let rtlScripts = SCRIPTS.filter(s => s.direction == "rtl");
      console.log(map(rtlScripts, s => s.name));
      // → ["Adlam", "Arabic", "Imperial Aramaic", …]


      // ----------- 3. reduce() -----------
      function reduce(array, combine, start) {
      let current = start;
      for (let element of array) {
      current = combine(current, element);
      }
      return current;
      }

      console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));
      // → 10
  3. Composability [pipeline]

    • what: we want to compose operations, to make meaningful & readable code

    • How: create functions, to make a series action into a pipeline

    • e.g.:

      1. we start with all scripts,
      2. filter out the living (or dead) ones,
      3. take the years from those,
      4. average them,
      5. and round the result.
    • 1
      2
      3
      4
      5
      6
      function average(array) {
      return array.reduce((a, b) => a + b) / array.length;
      }

      console.log(Math.round(average(
      SCRIPTS.filter(s => s.living).map(s => s.year))));

VI. The life of Object

  1. Encapsulation / 封装

    1. What?:

      • Internal details of one object cannot be access from outside
      • objects communicate via their interface
    2. How?

      • private: put an underscore (_) character at the start of property names to indicate that those properties are private.
    3. property

      • belongs to that object itself
      • or can be traced back up to its prototype
    4. Method

      • each function has its own this property
      • arrow function: they do not bind their own this but can see the this binding of the scope around them

      • this: every method have this implicit parameter passed into the function. The following are the same

        1
        2
        3
        4
        5
        6
        7
        function speak(line) {
        console.log(`The ${this.type} rabbit says '${line}'`);
        }
        let whiteRabbit = {type: "white", speak};

        whiteRabbit.speak("Burp"); // same effect
        speak.call(whiteRabbit, "Burp!"); // same effect
  2. Polymorphism

    1. overriding derived property

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        Rabbit.prototype.teeth = "small";
        console.log(killerRabbit.teeth);
        // → small
        killerRabbit.teeth = "long, sharp, and bloody";
        console.log(killerRabbit.teeth);
        // → long, sharp, and bloody
        console.log(blackRabbit.teeth);
        // → small
        console.log(Rabbit.prototype.teeth);
        // → small
    2. override derived method

      1. 1
        2
        3
        4
        5
        6
        Rabbit.prototype.toString = function() {
        return `a ${this.type} rabbit`;
        };

        console.log(String(blackRabbit));
        // → a black rabbit
    3. Why use Map instead of pure Object?

      • We certainly didn’t list anybody named toString in our map.

      • Yet, because plain objects derive from Object.prototype, it looks like the property is there.

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        let ages = {
        Boris: 39,
        Liang: 22,
        Júlia: 62
        };

        console.log("Is Jack's age known?", "Jack" in ages);
        // → Is Jack's age known? true
        console.log("Is Jack's age known?", "Jack" in ages);
        // → Is Jack's age known? false
        console.log("Is toString's age known?", "toString" in ages);
        // → Is toString's age known? true
        // ====>【what we don't want!!!】
    4. getters, setters, and static

      1. get: The get syntax binds an object property to a function that will be called when that property is looked up.

        1. 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          var obj = {
          log: ['a', 'b', 'c'],
          get latest() {
          if (this.log.length == 0) {
          return undefined;
          }
          return this.log[this.log.length - 1];
          }
          }

          console.log(obj.latest);
      2. set: The set syntax binds an object property to a function to be called when there is an attempt to set that property.

        1. 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          var language = {
          set current(name) {
          this.log.push(name);
          },
          log: []
          }

          language.current = 'EN';
          language.current = 'FA';

          console.log(language.log);
          // expected output: Array ["EN", "FA"]
      3. static: methods that have static written before their name are stored on the constructor.

        1. 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
          class Temperature {
          constructor(celsius) {
          this.celsius = celsius;
          }
          get fahrenheit() {
          return this.celsius * 1.8 + 32;
          }
          set fahrenheit(value) {
          this.celsius = (value - 32) / 1.8;
          }

          static fromFahrenheit(value) {
          return new Temperature((value - 32) / 1.8);
          }
          }

          let temp = new Temperature(22);
          console.log(temp.fahrenheit);
          // → 71.6
          temp.fahrenheit = 86;
          console.log(temp.celsius);
          // → 30

          Temperature.fromFahrenheit(100)
          // -> Temperature {celsius: 37.77777777777778}
  3. Inheritance

    1. extends operator

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        class SymmetricMatrix extends Matrix {
        constructor(size, element = (x, y) => undefined) {
        super(size, size, (x, y) => { // 'super' call its parents constructor
        if (x < y) return element(y, x);
        else return element(x, y);
        });
        }

        set(x, y, value) {
        super.set(x, y, value); // 'super' call its parents methods
        if (x != y) {
        super.set(y, x, value); // 'super' call its parents methods
        }
        }
        }

        let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);
        console.log(matrix.get(2, 3));
        // → 3,2
    2. instanceof operator

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        console.log(
        new SymmetricMatrix(2) instanceof SymmetricMatrix);
        // → true
        console.log(new SymmetricMatrix(2) instanceof Matrix);
        // → true
        console.log(new Matrix(2, 2) instanceof SymmetricMatrix);
        // → false
        console.log([1] instanceof Array);
        // → true
    3. prototype: 也是一个object, 用来表明该object中的哪些method用来 inherite

      • prototype vs __proto__

        • prototype: an shared object contains all methods to be inherited by child objects
        • __proto__: indicating the object it’s inheriting, ie, obj.prototype
      • what: can be treated as parent class in java. A prototype is another object that is used as a fallback source of properties.

      • object.prototype === null

      • Object.create(): create an object using anther object as the prototype. like constructor()

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        let protoRabbit = {
        speak(line) {
        console.log(`The ${this.type} rabbit says '${line}'`);
        }
        };
        let killerRabbit = Object.create(protoRabbit);
        killerRabbit.type = "killer";
        killerRabbit.speak("SKREEEE!");
        // → The killer rabbit says 'SKREEEE!'
    4. class: clearer way to create prototype

      • What & Details

        • The one named constructor is treated specially. It provides the actual constructor function, which will be bound to the name Rabbit.
        • The others are packaged into that constructor’s prototype. Thus, the earlier class declaration is equivalent to the constructor definition from the previous section. It just looks nicer.
      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        class Rabbit {
        constructor(type) {
        this.type = type;
        }
        speak(line) {
        console.log(`The ${this.type} rabbit says '${line}'`);
        }
        }

        let killerRabbit = new Rabbit("killer");
        let blackRabbit = new Rabbit("black");
      • NOTE: the constructor are capitalized

      • 1
        2
        3
        4
        5
        6
        7
        8
        function Rabbit(type) {   // note: name are capitalized
        this.type = type;
        }
        Rabbit.prototype.speak = function(line) {
        console.log(`The ${this.type} rabbit says '${line}'`);
        };

        let weirdRabbit = new Rabbit("weird");

VII. Bugs

  1. strict mode

    1. why? => we don’t want to write bugs!

    2. how?

      • use strict at top of a file

      • use strict in body of function

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        function canYouSpotTheProblem() {
        "use strict";
        for (counter = 0; counter < 10; counter++) {
        console.log("Happy happy");
        }
        }

        canYouSpotTheProblem();
        // → ReferenceError: counter is not defined
  2. regular express

    1. A regular expression is a type of object

      • with the RegExp constructor
      • as a literal value by enclosing a pattern in forward slash (/) characters
    2. 1
      2
      let re1 = new RegExp("abc");
      let re2 = /abc/;

VIII. Modules

  1. what & why

    • Modules are code snippet can be reused repeatedly
    • We want do DRY
  2. Inner implementation: [ important ]

    • we need JS has the ability to execute string of code, because what imported in are strings!

    • 2 ways to execute strings as code in JS

      • eval(): drawback => change scope!

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          const x = 1;
          function evalAndReturnX(code) {
          eval(code);
          return x;
          }

          console.log(evalAndReturnX("var x = 2"));
          // → 2
          console.log(x);
          // → 1
      • Function ( params_string, body_string ): self-contained module scope

        • 1
          2
          3
          let plusOne = Function("n", "return n + 1;");
          console.log(plusOne(4));
          // → 5
  3. CommonJS: require & exports

    • How to use

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        const ordinal = require("ordinal");
        const {days, months} = require("date-names");

        exports.formatDate = function(date, format) {
        return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => {
        if (tag == "YYYY") return date.getFullYear();
        if (tag == "M") return date.getMonth();
        if (tag == "MMMM") return months[date.getMonth()];
        if (tag == "D") return date.getDate();
        if (tag == "Do") return ordinal(date.getDate());
        if (tag == "dddd") return days[date.getDay()];
        });
        };
    • require: a function mainly used by CommonJS

      • calling a function to access a dependency
      • When you call this with the module name of a dependency, it makes sure the module is loaded and returns its interface.
      • The inner implementation of require()
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      require.cache = Object.create(null);    // a running-time  global obj

      function require(name) {
      if (!(name in require.cache)) { // check if alreay has the module in cache
      let code = readFile(name); // read [code STRING] from file
      let module = {exports: {}}; // an callback container to fetch real one
      require.cache[name] = module; // bind this container to cache
      let wrapper = Function("require, exports, module", code);// 'eval' code String in <local function scope>
      wrapper(require, module.exports, module); // put all the code to module,
      } // while tackle module of the required one
      return require.cache[name].exports; // output the
      }
  4. ES6 Module: export & import

    1. what & why [imported module are reference, not value]

      • Same: try to tackle dependency, but
      • Difference:
        • imported module using ES6 are reference, not value; So local scope can access its value when there is update inside the imported module
        • exporting module may change the value of the binding at any time, and the modules that import it will see its new value.
    2. How to use

      1. 1
        2
        3
        4
        import ordinal from "ordinal";
        import {days, months} from "date-names";

        export function formatDate(date, format) { /* ... */ }

IX. Asynchronous Programming

  1. callback function

    • Asynchronous behavior happens on its own empty function call stack

    • callbacks are called by others, not directly called by the code that scheduled them.

    • If I call setTimeout from within a function, that function will have returned by the time the callback function is called.
    • And when the callback returns, control does not go back to the function that scheduled it.
  2. Promise

  3. async & await

  4. generator

    • What: functions can be paused and then resumed again
    • how: function* funName(){ ... }
  5. event loop

    • all asynchronous events are put onto a Queue
    • all synchronous function are executed in Stack

Memory Game

Posted on 2018-10-02 | In Portfolios | Comments:

Memory-Game

What?

This is a memory game, developed using React, Ant Design, webpack, and CSS grid, etc.

memory_game.png

How to run?

  1. git clone
  2. npm install
  3. npm start

Features

  • Notify the user if they win or lose
  • Hints assistance
  • Allow the user to reset at any time
  • Randomize the ‘cards’ layout every game
  • Multi Player mode
  • Tracking Scores
  • Tracking Best Score
  • Tracking time
  • Adjustable number of cards in the game (difficulty levels)

To-do

  • Options & Start UI substitute the game board UI at the beginning
  • Hint feature: add keyboard press event

Layout & Logic

workflow.png

Event Emitter

Posted on 2018-10-01 | In Full stack | Comments:

I. What is an Event Emitter?

An event emitter is a utility object, which can be used to emit event, which can be subscribed by as many functions as we can.

  1. It is an event manager, manages differents event and its subscribers.

  2. Also, we can see it as the Redux, which also dispatches(emits) actions, and handled by its reducers.

  3. It widely used at Node.js ( asynchronous event-driven architecture)
    • certain kind of objects call emitters periodically to emit events that cause listener objects to be called.
    • when a request comes requesting some resource, Node.js will register the subscriber: responseToThisRequest() on event: thisTypeDataDone. When data is obtained, will call emitter.emit( thisTypeDataDone ), to call the responseToThisRequest() corresponding all the requests.

II. Why and when do we need it?

Whenever it makes sense for code to SUBSCRIBE to something rather than get a callback from something

https://stackoverflow.com/questions/38881170/when-should-i-use-eventemitter

If when a certain event happens, we have to deal many type of manipulation for that event’s result, then it is better to use the event emitter pattern.

Instead put all functions inside an event callback, we subscribe all the function to that event, when the event happens, we emit the event!

III. How to use it?

[Good Link]: https://netbasal.com/javascript-the-magic-behind-event-emitter-cce3abcbcef9

[Note]: when pass in the callback function, we use arrow function, which already bind ‘this’ to the function, so that when the function be called, it knows what value it will update

1
2
3
4
5
6
7
8
9
10
11
12
13

let input = document.querySelector('input[type="text"]');
let button = document.querySelector('button');
let h1 = document.querySelector('h1');

button.addEventListener('click', () => {
emitter.emit('event:name-changed', {name: input.value});
});

let emitter = new EventEmitter();
emitter.subscribe('event:name-changed', data => {
h1.innerHTML = `Your name is: ${data.name}`;
});
  1. create an emitter util object

  2. subscribe the desired callback function, using emitter.subscribe(event, function.bind(this))

  3. call emitter.emit() when the event happen

IV. Write one!

  1. object: we need an object (utility object)
  2. data structure: inside it, we need a data structure to store the event and its subscribers(callbacks)
  3. methods: all the methods
    • subscribe(event, callback): we need the return value to be an unsubscribe(), so we won’t result in the memory leak!
    • emit()
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
class EventEmitter {
constructor() {
this.events = {};
}

on(event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
this.events[event].push(listener);
return () => this.removeListener(event, listener);
}

removeListener(event, listener) {
if (typeof this.events[event] === 'object') {
const idx = this.events[event].indexOf(listener);
if (idx > -1) {
this.events[event].splice(idx, 1);
}
}
}

emit(event, ...args) {
if (typeof this.events[event] === 'object') {
this.events[event].forEach(
listener => {if(typeof listener === 'function') listener.apply(null, args)}
);
}
}

// register a wrapper function containing the listener on this event,
// when get executed,
// - first remove it on this event;
// - then execute real listener
once(event, listener) {
const remove = this.on(event, (...args) => {
remove();
listener.apply(null, args);
});
}
};


let emitter = new EventEmitter()
// Test 1: invalid callback function
console.log("Test 1: Nothing should be print out")
emitter.on("a", 1);
emitter.emit("a");
console.log()

// Test 2: add mutilple callback function to an event
console.log("Test 2: callback 1, 2, 3")
emitter.on("b", ()=>{console.log("event b callback 1")});
emitter.on("b", ()=>{console.log("event b callback 2")});
let off1 = emitter.on("b", ()=>{console.log("event b callback 3")});
emitter.emit("b");
console.log()


// Test 3: unsubscribe one callback from an event queue
console.log("Test 3: callback 1, 2")
off1();
emitter.emit("b");
console.log()


// Test 4: subscribe other event & callbacks at the same time
let data = {digit: 1}
console.log("Test 4: digit should be 11 & 14 ")
emitter.on("c", function(data){ data.digit+=1 });
emitter.on("c", function(data){ data.digit+=2 });
emitter.on("d", function(data){ data.digit+=3 });;
let off2 = emitter.on("d", function(data){ data.digit+=4 });

emitter.emit("c", data);
emitter.emit("d", data);
console.log(data.digit)

off2()
emitter.emit("d", data);
console.log(data.digit)
console.log()



// Test 5: subscribe other event & callbacks
let data2 = {digit: 5}
console.log("Test 5: digit should be 11")
emitter.emit("d", data2 );
emitter.emit("c", data2 );
console.log(data2.digit)
console.log()

// Test 6: test once
console.log("Test 6: should only print once")
emitter.once("test 6", ()=>{console.log("should only print once")} );
emitter.emit("test 6" );
emitter.emit("test 6" );
emitter.emit("test 6" );
console.log()

FaceBook interview version
https://gist.github.com/kutyel/7d8f204a347840a6ee7220743957e504

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
/*
* Create an event emitter that goes like this
* emitter = new Emitter();
*
* Allows you to subscribe to some event
* sub1 = emitter.subscribe('function_name', callback1);
* (you can have multiple callbacks to the same event)
* sub2 = emitter.subscribe('function_name', callback2);
*
* You can emit the event you want with this api
* (you can receive 'n' number of arguments)
* sub1.emit('function_name', foo, bar);
*
* And allows you to release the subscription like this
* (but you should be able to still emit from sub2)
* sub1.release();
*/

// facebook.js

class Emitter {
constructor(events = {}) {
this.events = events
}

subscribe(name, cb) {
(this.events[name] || (this.events[name] = [])).push(cb)

return {
release: () => this.events[name] &&
this.events[name].splice(this.events[name].indexOf(cb) >>> 0, 1)
}
}

emit(name, ...args) {
return (this.events[name] || []).map(fn => fn(...args))
}
}

export default Emitter





// test.js

const expect = require('expect')

const Emitter = require('./facebook')

// Step 1

let test = 0

const emitter = new Emitter()

emitter.emit('sum', 1)

expect(test).toBe(0) // should trigger nothing

const sub1 = emitter.subscribe('sum', num => (test = test + num))

emitter.emit('sum', 1)

expect(test).toBe(1) // should trigger 1 callback

const sub2 = emitter.subscribe('sum', num => (test = test * num))

emitter.emit('sum', 2)

expect(test).toBe(6) // should trigger 2 callbacks

// Step 2

sub1.release()

emitter.emit('sum', 3)

expect(test).toBe(18) // should trigger 1 callback

// Step 3

const myEvent1 = emitter.subscribe('myEvent', () => 1)
const myEvent2 = emitter.subscribe('myEvent', () => 2)
const myEvent3 = emitter.subscribe('myEvent', () => true)
const result = emitter.emit('myEvent')

expect(result).toEqual([1, 2, true])

console.info('You passed the test!!! 👏🏼 👏🏼 👏🏼')

CSS: element positioning

Posted on 2018-09-20 | In Full stack | Comments:

0. Credit to:

  1. http://javascript.info/coordinates

  2. https://msdn.microsoft.com/en-us/library/hh781509(VS.85).aspx

I. mouse event positon

  1. https://www.w3schools.com/jsref/event_clientx.asp
    • event.clientX: distance to the left border of viewport (position:fixed)
    • event.screenX: distance to the left border of browser
    • event.pageX: distance to the left border of document area(position: absolute)
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
<!DOCTYPE html>
<html>
<head>
<style>
div {
width: 500px;
height: 300px;
border: 1px solid black;
text-align: center;
}
#demo2{width: 500px;
height: 300px;
border: 1px solid red;
text-align: center;}
</style>
</head>
<body onclick="showCoords(event)">

<p id="pp">Click in the div element below to get the x (horizontal) and y (vertical) coordinates of the mouse pointer, when it is clicked.</p>

<div ><p id="demo"></p></div>

<p id="demo2"></p>

<p><strong>Tip:</strong> Try to click different places in the div.</p>

<script>
function showCoords(event) {
var cX = event.clientX;
var cY = event.clientY;
var sX = event.screenX;
var sY = event.screenY;
var pX = event.pageX;
var pY = event.pageY;

var slX = scrollX;
var slY = scrollY;
var pOffX = pageXOffset;
var pOffY = pageYOffset;

var coords1 = "client - X: " + cX + ", Y coords: " + cY;
var coords2 = "screen - X: " + sX + ", Y coords: " + sY;
var coords3 = "page - X: " + pX + ", Y coords: " + pY;
var coords4 = "sroll - X: " + slX + ", Y coords: " + slY;
var coords5 = "pageOffset - X: " + pOffX + ", Y coords: " + pOffY;
document.getElementById("demo2").innerHTML = coords1 + "<br>" + coords2 + "<br>" + coords3 + "<br>" + coords4 + "<br>" + coords5 + "<br>";
}

</script>

</body>
</html>

II. element position

  1. getBoundingClientRect():
    • top – Y-coordinate for the top element edge,
    • left – X-coordinate for the left element edge,
    • right – X-coordinate for the right element edge,
    • bottom – Y-coordinate for the bottom element edge.
  2. clientHeight: the height of the element
  3. offsetTop: returns the top position (in pixels) relative to the top of the offsetParent element. The returned value includes:
    • the top position, and margin of the element
    • the top padding, scrollbar and border of the offsetParent element
    • The offsetParent element is the nearest ancestor that has a position other than static.
  4. (clientX,clientY): window coordinates, correspond to position:fixed,
  5. (pageX,pageY): document coordinates , correspond to position:absolute

  6. formular:

    • pageY = clientY + height of the scrolled-out vertical part of the document.
    • pageX = clientX + width of the scrolled-out horizontal part of the document.
1
2
3
4
5
6
7
8
9
// get document coordinates of the element
function getCoords(elem) {
let box = elem.getBoundingClientRect();

return {
top: box.top + pageYOffset,
left: box.left + pageXOffset
};
}

III. Summary

Any point on the page has coordinates:

  1. Relative to the window – elem.getBoundingClientRect().
  2. Relative to the document – elem.getBoundingClientRect() plus the current page scroll.

Window coordinates are great to use with position:fixed, and document coordinates do well with position:absolute.

Both coordinate systems have their “pro” and “contra”, there are times we need one or the other one, just like CSS position absolute and fixed.

Coding Problem: Find 1st Bad Version

Posted on 2018-09-10 | In Algorithm | Comments:

I. problem

given 500 revisions of programs, write a program that will find and return the FIRST bad revision given a isBad(revision i) function.

II. 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
function isBad( version ){ return version === 0 }

function firstBadVersion( versions ){
if( typeof versions === 'undefined' || versions.length === 0 )
return -1;

let left = 0, right = versions.length-1;
while( left < right ){
let mid = left + Math.floor((right-left)/2)
if( isBad(versions[mid]) ){
right = mid
}else{
left = mid+1
}
}
return versions[left]===0 ? left : -1

}

// Testing 0 1 2 3 4 5 6 should return index: 4
let versions = [1,1,1,1,0,0,0]
console.log(firstBadVersion(versions)) // 4

versions = [1,1,1,1,1,0]
console.log(firstBadVersion(versions)) // 5

versions = [0, 0, 0]
console.log(firstBadVersion(versions)) // 0

versions = [0]
console.log(firstBadVersion(versions)) // 0

versions = [1]
console.log(firstBadVersion(versions)) // -1

versions = [1, 1, 1]
console.log(firstBadVersion(versions)) // -1

versions = []
console.log(firstBadVersion(versions)) // -1

console.log(firstBadVersion()) // -1

'this' & arrow function

Posted on 2018-09-05 | In Full stack | Comments:

I. this in function

  1. what is this ?

    • all regular functions have this keyword
    • this determines how a function is called on whom!
  2. this in global context

    • this refers gloabl object, no matter in strict mode or not

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // In web browsers, the window object is also the global object:
      console.log(this === window); // true

      a = 37;
      console.log(window.a); // 37

      this.b = "MDN";
      console.log(window.b) // "MDN"
      console.log(b) // "MDN"
  3. this in function context [important]

    There are 7 cases that different function context results in different this reference;

    The value of this depend on how the function is called

    1. this in simple call

      • non-strict mode

        if this is not set explicitly, it default to global object

        1
        2
        3
        4
        5
        6
        7
        function f1() { return this; }

        // In a browser:
        f1() === window; // true

        // In Node:
        f1() === global; // true
      • strict mode

        if this is not set explicitly, the value of this remains undefined

      • 1
        2
        3
        4
        5
        6
        function f2() {
        'use strict'; // see strict mode
        return this;
        }

        f2() === undefined; // true
      • change the value of this, using apply() or call()

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        // An object can be passed as the first argument to call or apply and this will be bound to it.
        var obj = {a: 'Custom'};

        // This property is set on the global object
        var a = 'Global';

        function whatsThis() {
        return this.a; // The value of this is dependent on how the function is called
        }

        whatsThis(); // 'Global'
        whatsThis.call(obj); // 'Custom'
        whatsThis.apply(obj); // 'Custom'
    2. this in bind() method

      • NOTE: bind() doesn’t change the orignal function, instead it return a new function with updated value of this

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        function f() { return this.a; }

        var g = f.bind({a: 'azerty'});
        console.log(g()); // azerty

        var h = g.bind({a: 'yoo'}); // bind only works once!
        console.log(h()); // azerty

        var o = {a: 37, f: f, g: g, h: h};
        console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
    3. this in arrow function

      • this cannot be changed, it default to the value of the enclosing lexical context‘s this

      • 1
        2
        3
        var globalObject = this;
        var foo = (() => this);
        console.log(foo() === globalObject); // true
      • About call() & apply()

        • if this arg is passed to call, bind, or apply on invocation of an arrow function it will be ignored.

        • the thisArg still works

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          // Call as a method of an object
          var obj = {func: foo};
          console.log(obj.func() === globalObject); // true

          // Attempt to set this using call
          console.log(foo.call(obj) === globalObject); // true

          // Attempt to set this using bind
          foo = foo.bind(obj);
          console.log(foo() === globalObject); // true

          another example: this of arrow function, directly depends on its enclosing context

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          // Create obj with a method bar that returns a function that
          // returns its this. The returned function is created as
          // an arrow function, so its this is permanently bound to the
          // this of its enclosing function. The value of bar can be set
          // in the call, which in turn sets the value of the
          // returned function.
          var obj = {bar: function() {
          var x = (() => this);
          return x;
          }
          };

          // Call bar as a method of obj, setting its this to obj
          // Assign a reference to the returned function to fn
          var fn = obj.bar();

          // Call fn without setting this, would normally default
          // to the global object or undefined in strict mode
          console.log(fn() === obj); // true

          // But caution if you reference the method of obj without calling it
          var fn2 = obj.bar;
          // Then calling the arrow function this is equals to window because it follows the this from bar.
          console.log(fn2()() == window); // true
    4. this in object method

      • When a function is called as a method of an object, its this is set to the object the method is called on.

      • 1
        2
        3
        4
        5
        6
        7
        8
        var o = {
        prop: 37,
        f: function() {
        return this.prop;
        }
        };

        console.log(o.f()); // 37
      • this on the object’s prototype chain

        If the method is on an object’s prototype chain, this refers to the object the method was called on, as if the method were on the object.

    5. this in constructor

      • this is bound to the new object being constructed

      • #A# if constructor doesn’t return: result of new will be the object bound to this

      • #B# if constructor return an object: result of new will be the return value

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        function C() { this.a = 37; }

        var o = new C();
        console.log(o.a); // 37

        function C2() {
        this.a = 37;
        return {a: 38};
        }

        // if an object was returned during construction, then the new object that this was bound to simply gets discarded.

        o = new C2();
        console.log(o.a); // 38
    6. this in DOM event handler

      • this is set to the target element the event fired from

      • this is set to the current element to which the event handler is attached; not the target element

      • this === e.currentTarget

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        // When called as a listener, turns the related element blue
        function bluify(e) {
        // Always true
        console.log(this === e.currentTarget);
        // true when currentTarget and target are the same object
        console.log(this === e.target);
        this.style.backgroundColor = '#A5D9F3';
        }

        // Get a list of every element in the document
        var elements = document.getElementsByTagName('*');

        // Add bluify as a click listener so when the
        // element is clicked on, it turns blue
        for (var i = 0; i < elements.length; i++) {
        elements[i].addEventListener('click', bluify, false);
        }
    7. this in inline event handler

      • this is set to the DOM element where the listener is placed

      • however, only the outer code has its this set this way

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        <!-- 1. this will refer to 'button' -->
        <button onclick="alert(this.tagName.toLowerCase());">
        Show this
        </button>

        <!-- 2. 'this' will refer to 'window' -->
        <button onclick="alert((function() { return this; })());">
        Show inner this
        </button>

II. arrow function

  1. Best suited for non-method functions, ie. not related to method in object

  2. No separate this of its own, using others’

    • An arrow function does not have its own this

    • this value of the enclosing lexical context is used

    • how to find this of enclosing lexical context:

      • check if this is present in current scope;

      • check this is present in its enclosing scope;

      • 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
        //---------- version1: works -----------------
        function Person(){
        this.age = 0;

        setInterval(() => {
        this.age++; // |this| properly refers to the Person object
        }, 1000);
        }

        var p = new Person();

        //---------- version2: doesn't work ----------
        function Person() {
        // The Person() constructor defines `this` as an instance of itself.
        this.age = 0;

        setInterval(function growUp() {
        // In non-strict mode, the growUp() function defines `this`
        // as the global object (because it's where growUp() is executed.),
        // which is different from the `this`
        // defined by the Person() constructor.
        this.age++;
        }, 1000);
        }

        var p = new Person();
  3. No binding of arguments

    • Arrow functions do not have their own arguments object.

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var arguments = [1, 2, 3];
      var arr = () => arguments[0];

      arr(); // 1

      function foo(n) {
      var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
      return f();
      }

      foo(3); // 6
    • using rest parameters is a good alternative to using an arguments object

    • 1
      2
      3
      4
      5
      6
      function foo(n) { 
      var f = (...args) => args[0] + n;
      return f(10);
      }

      foo(1); // 11
  4. When not use arrow function? ==> when need this explicitly

    1. don’t use as methods of object:

      • because we need this, but arrow function doesn’t have its own this

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        'use strict';

        var obj = {
        i: 10,
        b: () => console.log(this.i, this),
        c: function() {
        console.log(this.i, this);
        }
        }

        obj.b(); // prints undefined, Window {...} (or the global object)
        obj.c(); // prints 10, Object {...}
    2. don’t use as function constructor: because we will use new to construct , which need this

    3. don’t add prototype to it: because it doesn’t have this

    4. don’t use in yeild or generators:

1…456…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