• [Javascript] Getter and Setter Abstractions


    JavaScript provides primitive types and means of processing those. However, those are not enough. Real data must somehow come into the program and data must somehow leave the program, for it to become useful to us. In this talk, we will see how two abstractions are essential for data flow and to build up other abstractions, such as Iterator, Iterable, Observable, Scheduling, and others. Talk

    Getter as abstractions:

    const ten = 10;
    
    // Abstractions 
    // Laziness
    const getTen = () => {
            
      console.log("hi"); // hook for side effects
    
      // Implementation flexibility
      return 5 + 5;
      // return 2 * 5;
      // return 10
    }

    Benefits:

    • First of all: 'getTen' if you don't call the function, the calcuation inside the fucntion never run.
    • You can do any side effects inside function.
    • Implmentation flexibility, you can do different ways to aecieve the same effects.

    Think about this code:

    function add(getX, getY) {
       const x = getX();
       const y = getY();
       return x + y;
    }

    'getX' and 'getY' are abstract. What we are doing now is adding two abstract number in concrete world. What if we add in abstract world?

    function add(getX, getY) {
       return () => { // put code into a get getter function
         const x = getX();
         const y = getY();
         return x + y;
       }
    }

    Now the function are complete lazy, lazyniess is a good thing, when you have some lzay you use getter can make things concrete.

    function add(getX, getY) {
       return () => {
         const x = getX();
         const y = getY();
         return x + y;
       }
    }
    
    const getTen = () => 10;
    const getY = Math.random;
    
    const getSum = add(getTen, getY); // so far no calcuation happens

    Lazy initialization and lazy iteration:

    let i = 0;
    const array = [10,20,30,40];
    function getArrayItem() {
      // lazy iteration
      return array[i++];
    }
    
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem()); // undefined

    This is a concrete example, we call the fucntion mutiplue time and loop though the array. 

    And we also see that after call result as 'undefined', if we want to loop the array again from zero idnex, we have to do:

    let i = 0;
    const array = [10,20,30,40];
    function getArrayItem() {
      // lazy iteration
      return array[i++];
    }
    
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    
    i = 0;
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());

    We call 

    i = 0;

    Of course this is not good at all, we leak the information. 

    The way to improve it is by using getter & lazyniess:

    function getGetArrayItem() {
      // lazy initialization
      let i = 0;
      const array = [10,20,30,40];
      return function() {
        // lazy iteration
        return array[i++];
      }
    }
    
    let getArrayItem = getGetArrayItem();
    
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    
    getArrayItem = getGetArrayItem();
    
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());
    console.log(getArrayItem());

    getter-getter: Abstract List:

    Think about the following example:

    function range(left, right) {
      
      return () => {
        // lazy initialization
        let x = left;
        return () => {
          // lazy iteration
          if (x > right) {
            return undefined;
          }
          return x++;
        }
      }
    }
    
    const getGet = range(10 , 14);
    const get = getGet();
    
    console.log(get()); // 10
    console.log(get());
    console.log(get());
    console.log(get());
    console.log(get());  // 14
    console.log(get());  // undefined
    console.log(get());
    console.log(get());

    It prints out the range of nuber, which we defined, we can notice after the right limit of the number, it just print out undefined.

    One way to solve the undefined problem is using for loop:

    function range(left, right) {
      
      return () => {
        // lazy initialization
        let x = left;
        return () => {
          // lazy iteration
          if (x > right) {
            return undefined;
          }
          return x++;
        }
      }
    }
    
    const getGet = range(10 , 14);
    
    for(let get = getGet(), x = get(); x !== undefined; x = get()) {
      console.log(x) // 10 ... 14
    }

    The good thing about this code is that no matter how large the range it is, the menory size is always 1. Every time you call the fucntion, it pull one value from the abstract getter function every time. It makes CPU and memory efficient.


    Completion markers:

    function range(left, right) {
      
      return () => {
        // lazy initialization
        let x = left;
        return () => {
          // lazy iteration
          if (x > right) {
            return {done: true};
          }
          return {done: false, value: x++};
        }
      }
    }
    
    const getGet = range(10, 14);
    
    for(let get = getGet(), res = get(); !res.done; res = get()) {
      console.log(res.value) // 10 ... 14
    }

    We added {done: true | false} as a complete marker.


    Convert to Symbol.iterator:

    function range(left, right) {
      
      return {
        [Symbol.iterator]: () => {
        // lazy initialization
        let x = left;
        return {
          next: () => {
            // lazy iteration
            if (x > right) {
              return {done: true};
            }
            return {done: false, value: x++};
          }
        }
       }
      }
    }

    Javascript notice that when you are using [Symbol.iterator] and have  'next' inside, then it provides you a nice syntax to loop over the iterator and get the value of out it.

    for(let x of range(10, 14)) {
      console.log(x) // 10 ... 14
    }

    We might have done this:

    function range(left, right) {
      
      return {
        [Symbol.iterator]: () => {
        // lazy initialization
        let x = left;
        return {
          next: () => {
            // lazy iteration
            if (x > right) {
              return {done: true};
            }
            return {done: false, value: x++};
          }
        }
      }
      }
    }
    
    
    for(let x of range(0,14)) {
      if(x % 2 === 0) {
        console.log(x)
      }
    }

    We using 'if' inside 'for' loop, well it is nothing wrong, but we can do better. Because range() is abstract function, we don't need to pull all the value done to the concrete world to do the filtering, we can also do the filtering in abstract function.

    const filter = pred => iterations => {
      let z = [];
      for (let x of iterations) {
        if(pred(x)) z.push(x); 
      }
      return z;
    };
    
    function range(left, right) {
      
      return {
        [Symbol.iterator]: () => {
        // lazy initialization
        let x = left;
        return {
          next: () => {
            // lazy iteration
            if (x > right) {
              return {done: true};
            }
            return {done: false, value: x++};
          }
        }
      }
      }
    }
    
    for(let x of filter(x => x % 2 === 0)(range(0,14))) {
      console.log(x)
    }

    Setter-setter abstraction:

     You can think of "setter-setter" is callback:

    const setSetTen = (setTen) => {
      setTen(10)
    }
    
    setSetTen(console.log) // 10

    The benifits of doing setter-setter is 

    • Async
    • Inversion of control
    const setSetTen = (setTen) => {
      setTimeout(() => {
        //Async
        setTen(10)
      }, 1000)
      
    }
    
    setSetTen(console.log) // 10

    Setter-setter to Observable:

  • 相关阅读:
    C# 如何定义让PropertyGrid控件显示[...]按钮,并且点击后以下拉框形式显示自定义控件编辑属性值
    “Word自动更改后的内容保存到通用文档模板上。是否加载该模板?“的解决办法
    Web服务器之iis,apache,tomcat三者之间的比较
    [转]C#如何把文件夹压缩打包然后下载
    [转]C#压缩打包文件
    C#——Marshal.StructureToPtr方法简介
    [Android Pro] 内容提供者ContentProvider的基本使用
    [Linux] awk命令详解
    [Linux] AWK命令详解(大全)
    [Android UI] ProgressBar自定义
  • 原文地址:https://www.cnblogs.com/Answer1215/p/9043261.html
Copyright © 2020-2023  润新知