群组信息 私有

administrators

 

成员列表

  • Promise - Part 7 of 函数式编程

    Promise (承诺) 函数用于实现callback的功能,即在一个函数结束后调用另外一个函数。
    比如下面这个例子:

    function do_thing1(){
      setTimeout( function(){
        console.log( 'do thing1' );
      }, 1000 );
    }
     
    function do_thing2(){
      console.log( 'do thing2' );
    }
     
    do_thing1();
    do_thing2();
    

    这里我们先调用do_thing1因为我们希望do_thing2do_thing1结束后再执行,但是do_thing2没有等!

    我们可以用传统的callback来解决这个问题如下:

    function do_thing1(callback){
      setTimeout( function(){
        console.log( 'do thing1' );
        callback && callback()
      }, 1000 );
    }
     
    function do_thing2(){
      console.log( 'do thing2' );
    }
    do_thing1(do_thing2);
    

    我把do_thing2当做一个变量传给do_thing1,等do_thing1结束后来调用do_thing2。

    接下来,我们用Promise来解决这个问题:

    const do_thing1b = new Promise((resolve, reject) =>
      setTimeout(() => {
        console.log("do_thing1b");
        resolve("done");
      }, 1000)
    );
    
    const do_thing2b = () => {
      console.log("do thing2b");
    };
    
    do_thing1b.then(() => do_thing2b());
    

    因为do_thing1b现在是个Promise,所以我们可以用then来串接其他函数,即do_thing2,这样就确保do_thing2b一定在do_thing1b结束后才被调用。这就"承诺"!

    发布在 香草Javascript🌿
  • 微软浪费我不少的时间

    用Mac快4年了,偶尔到PC上除了一点东西,我擦,那个慢啊!不比不知道,我们在Windows上浪费了多少时间!

    发布在 笔记
  • Recursion - Part 6 of 函数式编程

    递归函数指自己调用自己直到被停止。
    说起Recursion - 递归,你如果学过计算机科学, 那么一定会想起斐波那契。(1,1,2,3,5,8,...)

    function feibo(n) {
      if (n === 1) {
        return 1;
      } else if (n < 1) {
        return 0;
      } else {
        return feibo(n - 1) + feibo(n - 2);
      }
    }
    

    其实在我的前端工作中,很少涉及斐波那契这样的算法题。那么需要前端需要递归吗?看看下面这个例子,

    const categories = [
      { id: "animals", parent: null },
      { id: "mammals", parent: "animals" },
      { id: "cats", parent: "mammals" },
      { id: "dogs", parent: "mammals" },
      { id: "aHuang", parent: "dogs" },
      { id: "aMi", parent: "cats" },
      { id: "aTu", parent: "dogs" },
      { id: "aMao", parent: "cats" }
    ];
    
    const makeTree = (categories, parent) => {
      let node = {}; //这个let很重要,它是block scoped的,也就是说下一次进入这个block,新的node被产生
      categories
        .filter(c => c.parent === parent)
        .forEach(item => (node[item.id] = makeTree(categories, item.id)));
      return node;
    };
    
    console.log(JSON.stringify(makeTree(categories, null), null, 2));
    

    结果如下:

    {
      "animals": {
        "mammals": {
          "cats": {
            "aMi": {},
            "aMao": {}
          },
          "dogs": {
            "aHuang": {},
            "aTu": {}
          }
        }
      }
    }
    

    这里我们把一组数据转换成树状结构,按照父系相同归类。这个如果用for loop,代码一定很乱,makeTree这么写还是挺酷的吧?

    总而言之,递归的作用是简化代码 - 这也是函数式编程的中心思想!

    看Pen: https://codepen.io/adamcai/pen/LMEdgR

    发布在 香草Javascript🌿
  • Curry - Part 5 of 函数式编程

    Curry (咖喱)函数 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。如果你不知道我在说什么,没关系,一步一步来:
    首先看一个普通函数:

    function person = (name, height, weight) {
      return name + '身高' + height + ', 体重' + weight;
    }
    

    用ES6箭头函数可以简写为:

    const person = (name, height, weight) =>
      name + '身高' + height + ', 体重' + weight;
    
    console.log(person('Tom', '1米78', '150公斤'));
    //Tom身高1米78, 体重150公斤
    

    如果你不知道为什么可以这么写,请查看[这里]。(https://j4fun.com/topic/48/map-arrow函数的简写-part-2-of-函数式编程)
    接下来,我们把以上函数用咖喱函数来表示:

    function curryPersonFunction (name) {
      return function personHeightWeight(height, weight) {
        return name + '身高' + height + ', 体重' + weight;
      }
    }
    

    注意:curryPersonFunction()函数现在只接一个参数name,然后返回一个函数personHeightWeight()。这么一来,我们可以用下面的函数调用来实现上面普通函数需要实现的结果:

    const tom = curryPersonFunction('Tom'); //tom 在这里是一个函数
    console.log(tom('1米78', '150公斤'));
    //Tom身高1米78, 体重150公斤
    

    以上的例子或许不能解释为什么要用Curry函数,但是发挥点想象力,你可能注意到,我们可以用Curry函数把一部分函数单独分离出来,这样就可以重复使用它来提高代码的可读性。

    比如下面这个例子:

    const data = {
        "user": "adam",
        "posts": [
            { "title": "why curry?", "contents": "curry is cool" },
            { "title": "why functional", "contents": "functional is clean" }
        ]
    };
    
    function getObjProp(propName, obj){
      return obj[propName];
    }
    
    const titles = getObjProp("posts", data).map(post => {
      return getObjProp("title", post)
    });
    
    console.log(titles);
    //["why curry?","why functional"]
    

    上面例子里,getObjProp是一个普通函数,它接受两个参数,一个是属性名,另外一个是object,然后返回这个属性的值。如果把它写成curry函数,在配上“级联”函数,比如map,我们就不用每次都传入object了。

    const get = prop => obj => obj[prop];
    const titles = get('posts')(data).map(get('title'));
    

    本章对于codePen:
    例子一:Curry基础
    例子二:Curry和Map的妙用

    发布在 香草Javascript🌿
  • Reduce - Part 3 of 函数式编程

    虽然reduce, map, filter常常被放在一起介绍,但是reduce的用法比较特殊,也比较难掌握,虽然在函数式编程的第一章,我们介绍过reduce,但是我还是觉得有必要在这里更加深入的介绍一下。

    基本上我可以用下面表达式来描述reduce

    const arr=[1,2,3];
    function f (a, b) { return a + b; }
    
    arr.reduce (f) = f(f(arr[0], arr[1]), arr[2]);
    

    分解开来,就是:

    const result = f(arr[0], arr[1]) = f(1, 2) = 3;
    result = f(result, arr[2]) = f(3,3) = 6;
    

    其实reduce还有第二个参数,也就是所谓的初始值,如果我们定义了这个值,那么,第一次传入给处理函数的参数就是不是数组的前两个成员,而是,这个初始值和数组的第一个成员。表达式如下:

    arr.reduce(f, INIT) = f(f(f(INIT, arr[0]), arr[1]), arr[2]);
    

    如果数组成员不是简单的数值或者字符串,那是这个初始值就变得很有用。

    看下面这个数组:

    const orders =[
      {amount: 1},
      {amount: 2},
      {amount: 3},
      {amount: 4}
    ]
    

    如果你用上面的reduce来计算总和就行不通了:

    const total = orders.reduce((x,y)=>x.amount + y.amount);
    

    虽然第一次调用求和函数,等到结果 = orders[0].amount + orders[1].amount = 3, 但是第二次我们把3 和 orders[2] 传给求和函数,我们就无法得到正确结果,因为3.amount是不存在的。
    解决这个问题,我们需要用到初始值,如下:

    const result2 = arr.reduce(
      (x,y) => {
        return x + y.amount;
      }, 1
    );
    

    看Pen: https://codepen.io/adamcai/pen/aQXEqB?editors=0010

    发布在 香草Javascript🌿
  • Map+Arrow函数的简写 - Part 2 of 函数式编程

    上一章节,我们学习了高阶函数,其中map就是一个高阶函数,一个非常常用非常实用的高阶函数。
    让我们重温一下这个函数的用法:

    function double (x) {
      return x*2;
    }
           
    const arr = [1,2,3];
    
    //map - return array
    const newArr1 = arr.map(double);
    console.log(newArr1);
    //[2,4,6]
    

    以上写法直观,但是有点"土",特别在这个ES6普遍流行的日子里。以上的函数可以通过Arrow函数,简化为:

    const newArr1 = arr.map(x=>x*2);
    

    什么玩意?是的,对于初学者来说,这个写法有点懵。下面我来解释一下:

    function double(x){
       return x*2;
    }
    //用arrow函数表示:
    const double = (x) => {
       return x*2;
    }
    //因为只有一个参数,所以括号可以省略:
    const double = x => {
       return x*2;
    }
    //又因为,函数返回值可以用一个return搞定,所以花括号又可以省略
    const double = x=> x*2;
    // 注意:在高级函数里,函数可以当做一个值,所以我们可以吧double直接传给map函数
    const newArr1 = arr.map(x=>x*2);
    

    现在应该清楚了吧?重复一下重点

    • 如果arrow函数只有一个参数,那么括号可以省略
    • 如果arrow函数返回值可以用一个return搞定,那么花括号又可以省略
    发布在 香草Javascript🌿
  • 高阶函数 - Part 1 of 函数式编程

    高阶函数(Higher-order functions) 指一种特定函数,你可以把其他函数当做变量传递个它。
    应用EJS上的例子:

    function repeat(n, action) {
      for (let i = 0; i < n; i++) {
        action(i);
      }
    }
    
    repeat(3, console.log);
    // → 0
    // → 1
    // → 2
    

    以上repeat就是一个高阶函数 - 它接受两个参数,第二个参数action是函数类型。

    在函数式编程里,你一定要知道下面这个几个Javascript自带的高阶函数:

    map

    map - 把数组成员一个个传给另外一个函数,这个函数把处理结果一个个返回,然后map把它们放到新的数组里返回。
    例子:

    function double (x) {
      return x*2;
    }
           
    const arr = [1,2,3];
    
    //map - return array
    const newArr1 = arr.map(double);
    console.log(newArr1);
    

    filter

    filter - 把数组成员一个个传给另外一个函数,这个函数返回boolean值,然后filter把返回true的成员放到新的数组里返回。

    function greaterThan2(x){
      return x>2;
    }
    
    const arr = [1,2,3];
    
    //filter - return array
    const newArr2 = arr.filter(greaterThan2);
    console.log(newArr2);
    

    reduce

    reduce - 把数组成员从左到右,传入另一个函数,接下来把得到结果和下一个成员再一起传入那个函数,最终返回一个值(不再是数组了

    function plus(x, y) {
      return x+y;
    }
         
    const arr = [1,2,3];
    
    //reduce - return value
    //注意:reduce把数组成员从左到右,传入另一个函数,接下来把得到结果和下一个成员再一起传入那个函数
    //[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
    //例如:f(f(1,2),3) = f(3,3) = 6
    const newArr3 = arr.reduce(plus);
    console.log(newArr3);
    

    欢迎到codepen 调测: https://codepen.io/adamcai/pen/vQbWzy?editors=0010

    最后,你很可能要用到这个函数编程的利器 - Lodash https://lodash.com/

    发布在 香草Javascript🌿
  • 关于unique "key" prop的报错

    关于unique "key" prop的报错,大家估计都很熟悉。React的报错信息也很清晰明朗:

    Warning: Each child in an array or iterator should have a unique "key" prop. See https://fb.me/react-warning-keys for more information.

    这个错误通常是发生在使用map来循环render一个组件时,没有给子元素添加“key”。例如:

    {
      props.data.map(({ currency, rate, name }) => (
        <Table.Row>
          <Table.Cell>{currency}</Table.Cell>
          <Table.Cell>{name}</Table.Cell>
          <Table.Cell>{rate}</Table.Cell>
        </Table.Row>
      ))
    }  
    

    大家注意到:这里我们没有给<Table.Row>加上unique的key。解决这个问题很容易。在这个例子里:currency刚好是个字符串类型,并且是unique的,所以把它当key用,加上就解决问题了,即:<Table.Row key={currency}>

    但是在有些情况下,React并不会告诉你这个错误发生在哪一个组件上,那么就有点头大了。比如下面这个例子:

    return [
      <RateTable data={this.state.data} />,
      <Buttons {...this.state} />
    ];
    

    从React V16开始,我们可以return多个子元素,而不用像以前一样,这些子元素一点要被包裹在一个node里返回。其实这就Array的概念,所以React也检查unique key。但是这两个元素没有相关性,加key有点别扭。这里建议用以下的写法:

    return (
    <>
      <RateTable data={this.state.data} />,
      <Buttons {...this.state} />
    </>
    );
    

    <>比用<div>好,因为它不会引入多余的<div>

    最有要重申一下:这个key在同一个node(父节点)下,一点要是unique的。不然的话,会造成React的render混乱!!!也就是说你不加key,可能只是一个warning,加的不对,页面显示错误,加还不如不加。🤡

    发布在 React
  • 推荐阅读

    11/26/2018 - 🌧
    函数式编程(Functional Programming)和面向对象编程(Object-oriented Programming)之间的口水仗不少。
    很多人估计没想到函数式编程居然翻身做主人了,感谢React!感谢Facebook!下面这篇FP的介绍不错,推荐阅读:
    https://dev.to/leandrotk_/functional-programming-principles-in-javascript-26g7

    发布在 笔记
  • noopener noreferrer

    第一次碰到noopener noreferrer是来自eslint的报错:

    Prevent usage of unsafe target='_blank' (react/jsx-no-target-blank)

    用了十几年的target="_blank" 突然间不安全了。

    先了解一下这两个参数:

    noopener

    “Instructs the browser to open the link without granting the new browsing context access to the document that opened it — by not setting the Window.opener property on the opened window (it returns null).”
    指示浏览器打开链接而不授予对打开它的文档的新浏览上下文访问权限 - 即不在打开的窗口上设置Window.opener属性(它返回null)。”

    noreferrer

    “Prevents the browser, when navigating to another page, to send this page address, or any other value, as referrer via the Referer: HTTP header.”
    “当导航到另一个页面时,阻止浏览器通过Referer:HTTP标头发送此页面地址或任何其他信息。”

    通常站长们是不会没事找事把危险网站链接到自己的网站上。我想这个漏洞是指,某一天原来安全的网站开始不老实了,或者被黑客控制了,这时就有问题了:
    攻击者可以使用此功能将某些内容下载到你的设备,拦截正在发送的私人数据,更改丢弃的Cookie或将你的读者带到他们想要的任何页面。 所以,后果严重!

    还是不明白黑客怎么搞得,不用太难过,梵总我也不明白。专家都说了,就照着干吧!

    发布在 笔记