KieSun / today-i-learned

记录今天学了什么
509 stars 34 forks source link

2019.7.22 - 7.28 中你学到什么? #3

Open KieSun opened 5 years ago

jialudev commented 5 years ago
Math.trunc(13.37) // 13

...没有进位有啥用。。

mengsixing commented 5 years ago

20190726

学习归并排序

归并排序使用算法中的分治思想,将一个大数组拆分成 2 个小数组,直到最后拆分为 1 个元素的数组,最后再将每个小数组按顺序合并成大数组。

将二个有序数组合并也十分简单,只需要从二个数组的第一个数开始比较,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数组为空,那直接将另一个数组的数据依次取出即可。

归并排序的效率是比较高的,设数列长为 N,将数列分开成小数列一共要 logN 步,每步都是一个合并有序数列的过程,时间复杂度可以记为 O(N),故一共为 O(N*logN)。

// 归并排序算法
function mergeSort(array) {
  // 避免污染传入的数组
  const temp = [...array];
  splitArray(temp, 0, array.length - 1);
  return temp;
}

// 将大数组拆分成两个小数组
function splitArray(array, start, end) {
  if (start < end) {
    const mid = Math.floor((start + end) / 2);
    splitArray(array, 0, mid);
    splitArray(array, mid + 1, end);
    mergeArray(array, start, mid, end);
  }
}

// 合并两个排序好的数组
function mergeArray(array, start, mid, end) {
  var i = start;
  var j = mid + 1;
  var k = 0;
  var temp = [];
  while (i <= mid && j <= end) {
    if (array[i] <= array[j]) {
      temp[k] = array[i];
      i++;
    } else {
      temp[k] = array[j];
      j++;
    }
    k++;
  }

  while (i <= mid) {
    temp[k] = array[i];
    i++;
    k++;
  }

  while (j <= end) {
    temp[k] = array[j];
    j++;
    k++;
  }

  for (let index = 0; index < k; index++) {
    array[index + start] = temp[index];
  }
}

var array = [4, 3, 1, 9, 6, 2, 7, 8, 5];
console.warn('归并排序', mergeSort(array));
KRISACHAN commented 5 years ago
Math.trunc(13.37) // 13

...没有进位有啥用。。

不知道啊,有这个API,应该有它的用处吧

yes1am commented 5 years ago

2019-07-27

JS 函数对象参数的陷阱

1. 正文

上周在实现某个弹层功能的时候,用到了rc-util里的 contains 方法函数, 结果 code-review 的时候同事对该代码提出了疑问:

rc-util 源码仓库

export default function contains(root, n) {
  let node = n;
  while (node) {
    if (node === root) {
      return true;
    }
    node = node.parentNode;
  }
  return false;
}

上述代码是 antd 内部抽象的一个工具方法,用来判断某个dom是否为另一个dom的祖先节点。

同事疑问的是 let node = n; 这段代码是不是多余的?

首先一开始的理解是 函数参数 n 是一个对象,一个dom节点对象。
如果用 node 保存 n 的值,防止 node = node.parentNode 这段代码执行的时候,会改变传入的实参 n 对应的值。

毕竟以下的代码我们都很熟悉:

function contains(root, n) {
  if(n) {
    n.a = 3
  }
}

const A = {a:1};
const B = {a:2};
contains(A,B)
console.log(B)    // {a:3}

即当实参为对象时,函数内部是可以改变该对象的值从而影响函数之外的实参。

但是测试另外一段代码,发现和理解的不一样:

function contains(root, n) {
  if(n) {
    n = {a:3}
  }
}

const A = {a:1};
const B = {a:2}
contains(A,B)
console.log(B) // {a:2}

n.a = 3n = {a:3} 这两段代码是不一样的。

网上也有相关资料,其实可以简单的理解为: 当函数一开始执行时,n 是指向实参 B 的一个引用.

n.a = 3 是在引用上关联了一个属性,此时和 B 还是同一个引用,因此会改变实参B的值。

n = {a:3} 则使得 n 不再指向实参 B, 而是指向一个新对象{a:3},也就是 nB 彻底断绝了关系,因此不会改变实参 B 的值。

是不是可以给蚂蚁的团队提个issue建议删除该代码,不过有这句代码也不会有什么bug~

2. 相关资料

JavaScript深入之参数按值传递

ABasket commented 5 years ago

2019-07-27

开始学习数据结构与算法 Java版

  1. 算法的评估
  2. 时间复杂度的估算
  3. 大O表示法
  4. 裴波那契数列复杂度分析

共用时2个小时,视频+思考+敲代码

裴波那契数列

// 裴波那契数列指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……
// value = 前两个数的和 表达式F(n)=F(n-1)+F(n-2)
// 算法1
public static long fib(long n) {
        if(n <= 1) return n;  // 执行1+1=2次数  n <= 1 一次 return n 一次
        return fib(n-1)+fib(n-2); // 0.5*2^n次
}
// 算法2
public static long fib(long n) {
        if(n <= 1) return n;       // 2
        long first = 0;            // 1
        long second = 1;         //  1
        for (long i = 0; i < n - 1; i++) {  // n 
            long sum = first + second;      // n
            first = second;                 // n
            second = sum;                  // n
        }

        return second;                     // 1
}
/*

(1)阵列元素相加为2n+3 = O(n)

(2)矩阵相加为2n^2+2n+1 = O(n^2)

(3)矩阵相乘为2n^3+4n^2+2n+2 = O(n^3)

 (4) 执行次数为常数  2 = O(1)
算法2的时间复杂度是O(n) 
 2+1+1+4n+1 = 4n+5  取最高阶  n  O(n)
算法1的时间复杂度是O(2^n)
比如:fib(5)
   图1
 可以看到fib(5) , 第一行是1个,第二行是2个(fib(4) 和 fib(3)), 第三行是 4个(fib(3),fib(2),fib(2),fib(1)) , 第四行是 6个。第五行 2个。
    1 + 2 + 4 + 8 
    = 2 ^0 + 2 ^ 1 + 2^2 + 2^3
    = 2^4 - 1
    = 2^(n - 1) - 1
     = 0.5 * 2^n
所以时间复杂度是 O(2^n)
链接:https://blog.csdn.net/M316625387/article/details/89467298

*/

在这里插入图片描述

​ (图1)csdn博客

KieSun commented 5 years ago

2019/07/22

01 graphql 中多级分页查询时的 N+1 问题

此处只能在客户端避免多层分页查询,而当有恶意查询时会加大服务器压力。可以使用 Hash Query 避免此类问题,避免暴露原始 Query,同时也在生产环境禁掉 introspection

{
  users (page: 1, pageSize: 3) {
    id
    todos (page: 1, pageSize: 3) {
      id 
      name
    } 
  }
}
select id from users limit 3

select id, name from todo where user_id = 1 limit 3
select id, name from todo where user_id = 2 limit 3
select id, name from todo where user_id = 3 limit 3

记录

02 在前端中把 gql 统一管理减小打包体积

准确说,已经把 gql 统一管理为 query.gql,通过 loader 引入,会被打入 common.js。 而在个别页面又单独通过 graphql-tag 引入,被按需加载打包,而此时 graphql-tag 又依赖 graphql,gql 统一管理后,此时的两个模块的依赖也就没有了。

通过 webpack 分析,gzip 后的总体积变化: 191.64KB -> 176.51KB,体积缩减还是比较可观的。

优化记录

03 获取当前 commit hash

可以通过 npm scripts 注入到项目中,方便集成到 sentrylogger 等工具组件中

git rev-parse --verify HEAD  | cut -c 1-8

后来找到更简单的命令 git rev-parse --short HEAD

2019/07/23

01 本地更好的流式日志格式化

服务器端的日志很重要,一般设置为 jsonl 格式,方便在 elk 上查询或者使用 spark 分析。而在本地调试时就不是很直观了,而日志又是 stream 的数据。此时可以使用 jq 对日志进行美化

# 实时查看日志,并且只关注 message 字段
tail -f logs/db.log | jq '{message}'

此时你不仅有了更好的可视化,而且只需要关注你自己关心的字段就可以了,体验很棒

02 Intersection Observel 做路由页面预加载与图片懒加载

由于我的网站使用了 next.js 的技术栈,最近对 next 升级到了 v9,对 next 一直有所关注。在今天读了 Next.9 features,其中有一个点:

Next.js 9 will automatically prefetch components as they appear in-viewport.

其中原理便是使用 Intersection Observel 来监听元素是否出现在当前可视窗口内。 Next.js 可以对新的页面资源做懒加载,举一反三,我们也可以使用它对图片做懒加载。

03 了解一个 APP 从开发到盈利所经历的事

2019/07/24

01 在 React 中使用 React.Fragment 或者 <></> 包含多个子元素

恩,这个是查看某项目代码时看到的,虽然一直知道了 react 目前已经可以包含多元素,但也没有细究,今天偶然得知,受用很多。

(
  <>
    <td>Hello</td>
    <td>World</td>
  </>
);

02 sar (System Activity Monitor)

sarvmstatiostat 相比,不仅可以实时监控 linux 系统 IO/memory/cpu 等的信息,而且可以 获取一段时间内的系统统计信息,比如他可以获取一天时间内的 CPU 使用率。我也把它记录在了我的博客里

linux 指标监控

03 Apollo Federation & GraphQL Gateway

简而言之,Apollon Federation 可以把多个 graphql service 组合成一个 API Gateway。如果公司的所有业务与微服务都使用了 GraphQL,则它正好可以做一个不错的服务整合,这还挺令人兴奋的。

2019/07/25

总结了LF 与 CRLF 的一篇文章

了解一些底层知识,视野会开阔很多。如本篇文章可以通过系统调用来看换行符到底是什么。

用户增长模型 RARRA

我的理解,用户留存比较重要,应作为着力点

2019/07/26

了解 vuepress 如何写插件,并给我的博客写了一个归档的插件

https://github.com/shfshanyue/blog/blob/master/.vuepress/config.js

2019/07/27

尝试使用 netlify 新建了我的博客

博客地址 https://shanyue.netlify.com/

至于我现在的博客还是使用 hugo 构建,不得不说 hugo 的构建速度真是很快。另外使用自建的 gitlab,gitlab-ci,docker-compose 来做自动部署,不得不说运维这些还是有些成本的。而 netlify 就简单很多了,这些运维工作都不需要你来关心。

云是一种趋势,以后考虑能够 SAAS 化的服务全部 SAAS 化。

graphql-code-generator 自动为前端生成 graphql schema 相对应的 ts 的 type

毫无疑问,前端不用手动配置关于接口的 interface,大大解放了前端的生产力,而且减少了失误率。

大佬每天都记录了学习的内容,真的很棒!

RuinousCheng commented 5 years ago
Math.trunc(13.37) // 13

...没有进位有啥用。。

不知道啊,有这个API,应该有它的用处吧

要进位的话就用Math.round(), 这个就是用来去除小数部分的,还会隐式把参数转为数字。

luohong123 commented 5 years ago

20190728 study

lllllllqw commented 5 years ago

2019-07-27

JS 函数对象参数的陷阱

1. 正文

上周在实现某个弹层功能的时候,用到了rc-util里的 contains 方法函数, 结果 code-review 的时候同事对该代码提出了疑问:

rc-util 源码仓库

export default function contains(root, n) {
  let node = n;
  while (node) {
    if (node === root) {
      return true;
    }
    node = node.parentNode;
  }
  return false;
}

上述代码是 antd 内部抽象的一个工具方法,用来判断某个dom是否为另一个dom的祖先节点。

同事疑问的是 let node = n; 这段代码是不是多余的?

首先一开始的理解是 函数参数 n 是一个对象,一个dom节点对象。 如果用 node 保存 n 的值,防止 node = node.parentNode 这段代码执行的时候,会改变传入的实参 n 对应的值。

毕竟以下的代码我们都很熟悉:

function contains(root, n) {
  if(n) {
    n.a = 3
  }
}

const A = {a:1};
const B = {a:2};
contains(A,B)
console.log(B)    // {a:3}

即当实参为对象时,函数内部是可以改变该对象的值从而影响函数之外的实参。

但是测试另外一段代码,发现和理解的不一样:

function contains(root, n) {
  if(n) {
    n = {a:3}
  }
}

const A = {a:1};
const B = {a:2}
contains(A,B)
console.log(B) // {a:2}

n.a = 3n = {a:3} 这两段代码是不一样的。

网上也有相关资料,其实可以简单的理解为: 当函数一开始执行时,n 是指向实参 B 的一个引用.

n.a = 3 是在引用上关联了一个属性,此时和 B 还是同一个引用,因此会改变实参B的值。

n = {a:3} 则使得 n 不再指向实参 B, 而是指向一个新对象{a:3},也就是 nB 彻底断绝了关系,因此不会改变实参 B 的值。

是不是可以给蚂蚁的团队提个issue建议删除该代码,不过有这句代码也不会有什么bug~

2. 相关资料

JavaScript深入之参数按值传递

分析思路没啥问题, 不过这么做的真正原因其实是因为一条规则: 不对函数的参数进行赋值, 规则见 eslint

yes1am commented 5 years ago

@lllllllqw 学习了,原来是这样, 看了下utils 最终是基于 airbnb做的eslint规范,确实 airbnb规范存在这条规则。

13168335674 commented 5 years ago

移动端打开指定App或者下载App

navToDownApp() {
      let u = navigator.userAgent
      if (/MicroMessenger/gi.test(u)) {
        // 如果是微信客户端打开,引导用户在浏览器中打开
        alert('请在浏览器中打开')
      }
      if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {
        // Android
        if (this.openApp('en://startapp')) {
          this.openApp('en://startapp') // 通过Scheme协议打开指定APP
        } else {
          //跳转Android下载地址
        }
      } else if (u.indexOf('iPhone') > -1) {
        if (this.openApp('ios--scheme')) {
          this.openApp('ios--scheme') // 通过Scheme协议打开指定APP
        } else {
          // 跳转IOS下载地址
        }
      }
    },
    openApp(src) {
      // 通过iframe的方式试图打开APP,如果能正常打开,会直接切换到APP,并自动阻止a标签的默认行为
      // 否则打开a标签的href链接
      let ifr = document.createElement('iframe')
      ifr.src = src
      ifr.style.display = 'none'
      document.body.appendChild(ifr)
      window.setTimeout(function() {
        // 打开App后移出这个iframe
        document.body.removeChild(ifr)
      }, 2000)
    }
欢迎一起讨论,如果有更好的写法欢迎指教

😁推荐个库 callapp-lib