• React 拖拽组件对比

    • react-dnd
      • react 友好
      • 组件间解耦
        • Describing the dragged data as a plain object helps you keep the components decoupled and unaware of each other.
        功能丰富概念较多,使用较复杂
        • drag source
        • drop target
        • backend
        需要IE 10+ react-beautiful-dnd
        • 是为垂直和水平列表专门构建的更高级别的抽象,没有提供 react-dnd 提供的广泛功能
        • 外观漂亮,可访问性好,物理感知让人感觉更真实的在移动物体
        • 开发理念上是拖拽,不支持copy/clone react-sortable-hoc
          • 不支持拖拽到另一个容器中?
          • 未看到copy/clone
          react-draggable
          • 只是对组件的一个wrapper来提供拖拽能力

          相关链接

  • a + b in JavaScript

    计算方法

    1. 将A和B都转换为原始值(primitive,执行ToPrimitive),这里记为A1,B1
    2. 如果A1和B1中有一个值为string,则将A1、B1都转换为string(执行ToString),其值记为A2、B2,将A2 B2连接后就是A+B的结果
    3. 否则的话将A1、B1都转换为number(执行ToNumber),其值记为A3、B3,将A3 B3相加即为A+B的结果

    ToPrimitive(obj,preferredType)

    JS引擎内部转换为原始值ToPrimitive(obj,preferredType)函数接受两个参数,第一个obj为被转换的对象,第二个preferredType为希望转换成的类型(默认为空,接受的值为Number或String)

    在执行ToPrimitive(obj,preferredType)时如果第二个参数为空并且obj为Date的实例时,此时preferredType会被设置为String,其他情况下preferredType都会被设置为Number

    如果preferredType为Number,ToPrimitive执行过程如下:

    1. 如果obj为原始值,直接返回;
    2. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
    3. 否则调用obj.toString(),如果执行结果是原始值,返回之;
    4. 否则抛异常。

    如果preferredType为String,将上面的第2步和第3步调换,即:

    1. 如果obj为原始值,直接返回;
    2. 否则调用obj.toString(),如果执行结果是原始值,返回之;
    3. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
    4. 否则抛异常。

    我写的一个ToPrimitive函数可以在这里查看 >> JS Bin – Collaborative JavaScript Debugging

  • 一行命令kill所有被node占用端口

    lsof -i:3000 | grep node | awk '{ print $2 }' | xargs kill
    lsof -i:3000

    列出所有占用3000端口的文件

    grep node

    过滤结果,使其只包含有node的行

    awk '{ print $2 }'

    awk对每一行进行处理 ,以空白符(空格、tab等)将每行数据分割,$0为所有数据,$N为第N列

    • 可以用-F命令来指定分隔符,如 awk -F ‘:’
    xargs kill

    xargs将管道中的读取的数据作为参数传递给kill

    cat /etc/passwd |awk  -F ':'  'BEGIN {print "name,shell"}  {print $1","$7} END {print "blue,/bin/nosh"}'
    name,shell
    root,/bin/bash
    daemon,/bin/sh
    bin,/bin/sh
    sys,/bin/sh
    ....
    blue,/bin/nosh

    awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。

    • 开始时执行{print “name,shell”}
    • 循环执行 {print $1″,”$7}
    • 结束时执行 {print “blue,/bin/nosh”}

    参考文章

  • fetch下载文件

    fetch跨域下载文件

    为何读取到的content-disposition header为null

    在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,

    • Cache-Control
    • Content-Language
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma

    如果要访问其他头,则需要服务器设置Access-Control-Expose-Headers

    Access-Control-Expose-Headers:Content-Disposition

    保存文件

    fetch(`${url}`, {
    method: 'POST',
    headers: {
    ...
    },
    mode: 'cors',
    body: JSON.stringify({
    companyId: this.state.params.companyId,
    startDate: this.state.listParams.startDate,
    endDate: this.state.listParams.endDate,
    })
    }).then(res => res.blob().then(blob => {
    let a = document.createElement('a');
    let url = window.URL.createObjectURL(blob); // 获取 blob 本地文件连接 (blob 为纯二进制对象,不能够直接保存到磁盘上)
    let disposition = res.headers.get('content-disposition');
    let filename = (disposition && disposition.replace(/attachment;.*filename=/, '')) || 'uv.xls'
    console.log('disposition:', disposition)
    console.log('filename:', filename)
    a.href = url;
    a.download = filename;
    a.click();
    // 使用完ObjectURL后需要及时释放, 否则会浪费浏览器存储区资源.
    window.URL.revokeObjectURL(url);
    }))
    }
     

    参考文章

  • 亲手实现一个Promise

    为何要手写Promise?直接import不行吗?

    身处技术圈,前端技术真的是日新月异,异步的实现方式从callback、到Promise、再到Generator、Async/Await,有了长足的发展,Promise作为发展过程中的一种产物其既是callback的一种改良,也是Generator、Async/Await的基础。

    直接import一个Promise类库当然可行(在支持的浏览器中直接使用Promise也未尝不可),但是如果我们能够亲手实现一个Promise,那么我们还能够:

    • 对Promise的理解更加深刻
    • 提升自己的类库编写能力,尤其是各种边界值的处理

    另外有一点需要说明,本文针对的是有一定Promise基础的同学,基础知识不在本文范畴之内,不了解的话请自行Google :)

    实现的内容

    实现之前我们先来思考下,一个Promise中到底有哪些是必须要实现的?

    按照Promise/A+的标准来说,只需要Promise的then方法即可,至于怎么实现、constructor怎么写,完全没有提及。不过虽然看似东西很少,但是实质上还是有蛮多东西要处理的。

    首先,constructor是必须的,在这里我们需要做一些初始化的动作:

    • 初始状态处理
    • 对传入的resolver进行判断并做容错处理
    • 执行传入的resolver函数
    • 定义resolve、reject函数

    其次,then方法由于是每个实例上都有,所以会挂在prototype上面,then方法中我们需要做的事情有

    • 对传入的onFulfilled、onRejected进行判断并做容错处理
    • 判断当前Promise的状态并返回一个新的Promise

    然后,我们还需要一个resolvePromise函数,这个函数根据标准而来(其实不管怎样,Promise的实现中都是少不了这样的处理过程)

    至于原型上的catch方法以及race、all等静态方法,暂时不处理,而且这些在我们实现了Promise的基础代码后都是很容易的事情,暂时不做处理。

    Constructor

    function Promise(resolver) {
    
      var self = this
      self.status = 'pending'
      self.fulfilledCallbacks = []
      self.rejectedCallbacks = []
      self.data = null
    
      if(typeof resolver !== 'function') {
        throw new TypeError('Promise resolver ' + resolver + 'is not a function')
      }
    
      function onFulfilled(data) {
        setTimeout(function () {
          if(self.status !== 'pending') return
          self.status = 'fulfilled'
          self.data = data
          var currentCallback
    
          for(var i = 0; i < self.fulfilledCallbacks.length; i++) {
            currentCallback = self.fulfilledCallbacks[i]
            typeof currentCallback === 'function' && currentCallback(self.data)
          }
        })
      }
    
      function onRejected(reason) {
        setTimeout(function () {
          if(self.status !== 'pending') return
          self.status = 'rejected'
          self.data = reason
          var currentCallback
          for(var i = 0; i < self.rejectedCallbacks.length; i++) {
            currentCallback = self.rejectedCallbacks[i]
            typeof currentCallback === 'function' && currentCallback(self.data)
          }
        })
      }
    
      try {
        resolver(onFulfilled, onRejected)
      }catch (e){
        onRejected(e)
      }
    
    }
    

    可以看到,在构造函数中最上面是对初始状态的处理。

    self.status = 'pending' // 初始化时必定是pengding状态,
    self.fulfilledCallbacks = []  // fulfilled后执行的回调
    self.rejectedCallbacks = []  // rejected后执行的回调
    self.data = null  // 初始化时Promise内部的数据
    

    接着对resolver做了判断,

    if(typeof resolver !== 'function') {
      throw new TypeError('Promise resolver ' + resolver + 'is not a function')
    }
    

    因为对于一个Promise来说,传入的resolver不是function就没有意义了,比如

    var p = new Promise(100) //那么这里传入100的时候,到底想干嘛?

    然后是onFulfilled和onRejected这两个函数的定义,即我们传递给一个Promise实例里的resolve和reject两个参数

    var p = new Promise( (resolve, reject) => {
      resolve() // resolve即onFulfilled、reject即onRejected
    })
    
    function onFulfilled(data) {
        setTimeout(function () {
          if(self.status !== 'pending') return
          self.status = 'fulfilled'
          self.data = data
          var currentCallback
    
          for(var i = 0; i < self.fulfilledCallbacks.length; i++) {
            currentCallback = self.fulfilledCallbacks[i]
            typeof currentCallback === 'function' && currentCallback(self.data)
          }
        })
      }
    

    在onFulfilled中首先我们看到的是一个setTimeout,这样做的目的是为了确保onFulfilled是异步执行的,至于为何要异步执行,目前还没有搞懂,个人感觉是非必须的,但是如果设置为同步的会导致测试无法通过,在查看bluebird的源代码时发现这部分根据data做了判断,如果非Promise会是同步,而是Promise的话会异步执行,这个坑先留着,待后面解决。

    setTimeout内先对状态进行判断,如果其已经是fulfilled或rejected我们直接返回,接着就是一个for循环执行回调。

    onRejected和onFulfilled大同小异,不多赘述。

    try {
      resolver(onFulfilled, onRejected)
    }catch (e){
      onRejected(e)
    }
    

    最后我们直接在一个try/catch中执行传入的resolver,以便对执行resolver时候的错误进行处理。

    Promise.prototype.then

    Promise.prototype.then = function (onFulfilled, onRejected) {
      var self = this
      var p
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(val){return val}
      onRejected = typeof onRejected === 'function' ? onRejected : function(reason){throw reason}
    
      if(self.status !== 'pending') {
        return p = new Promise(function (resolve, reject) {
          setTimeout(function () {
            var functionToCall = self.status === 'fulfilled' ? onFulfilled : onRejected
            var result
            try {
              result = functionToCall(self.data)
              resolvePromise(p, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      } else {
        return p = new Promise(function (resolve, reject) {
          self.fulfilledCallbacks.push(function(){
            try {
              var result = onFulfilled(self.data)
              // 如果上个then的返回值是个Promise实例 或者Promise resolver里面resolve的结果是个Promise实例
              resolvePromise(p, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
          self.rejectedCallbacks.push(function(){
            try {
              var result = onRejected(self.data)
              // 如果上个then的返回值是个Promise实例 或者Promise resolver里面resolve的结果是个Promise实例
              resolvePromise(p, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    }
    

    根据标准Promise.prototype.then的返回值是一个新的Promise,所以我们可以看到2处类似这样的代码

    return p = new Promise(function (resolve, reject) {
      ...
    })
    

    这2处就是根据当前Promise实例(self)的状态来做不同的处理

    首先看非pending状态

    • 非pending,即self已经被resolved/rejected
    • 这个时候我们需要”马上”对p做处理,而不是将其放入回调中,另外为了确保then中代码块异步执行,相关代码都被包在了一个setTimeout中
    • result即是执行完onFulfilled后的结果,由于并不知道result会是什么样的结果,我们在单独的一个resolvePromise函数中处理,根据result来确定p

    接着再看pending,由于self还是pending状态,那么就等到self resolve或者是reject的时候再来处理,即将相应的代码放入fulfilledCallbacks/rejectedCallbacks中即可

    resolvePromise

    function resolvePromise(promise, x, resolve, reject){
      var then
      var thenCalled
    
      if(promise === x) reject(new TypeError('Chaining cycle detected for promise!'))
    
      if(x instanceof Promise) {
        if(x.status === 'pending') {
          x.then(function (value) {
            resolvePromise(promise, value, resolve, reject)
          }, reject)
        }else{
          x.then(resolve,reject)
        }
    
      } else if(x !== null && (typeof x === 'function' || typeof x === 'object') ){
    
        try{
          then = x.then
          if(typeof then === 'function') {
            then.call(x, function(y){
              if(thenCalled) return
              thenCalled = true
              resolvePromise(promise, y, resolve, reject)
            }, function(r){
              if(thenCalled) return
              thenCalled = true
              return reject(r)
            })
          } else {
            resolve(x)
          }
        }catch(e){
          if(thenCalled) return
          thenCalled = true
          return reject(e)
        }
    
      }else{
        resolve(x)
      }
    
    }
    

    刚开始的时候不是很立即为什么会有这么一个过程,想着直接根据then中onFulfilled返回的值(result)做判断,然后返回不同的Promise即可,后来发现result有各种不同的情况,而且不止一个地方需要用到,那么将其抽取出来是就合情合理了。

    resolvePromise中主要的处理就是根据传入的x来决定promise(输入参数中的promise)的状态,其中最主要的2块

    • x instanceof Promise 这个主要是判断如果是我们自己创建的Promise的实例
    • x !== null && (typeof x === ‘function’ || typeof x === ‘object’) 这个尝试将x当做thenable处理,确保我们的Promise能够与其他Promise类库交互

    其他代码更多的是边界值的判断,这里就不做赘述了。

    总结

    之前面试的时候被问到怎么实现一个Promise,一脸懵逼,这个问题压根没想过,然后临阵发挥的时候一直纠结于resolve在哪里,现在想想真是傻,为何为停在这里。。。🙃

    参考资料

  • JS原型链回顾

    前两年在看JS原型链的时候花了挺久终于搞懂了,时间一久又渐渐生疏,趁机画个图总结下

     

    主要知识点:

    1. 在用构造函数F创建新的实例对象f时,会为f创建__proto__属性,指向Fprototype
    2. 假如现在要查找a属性,那么先看看f上有没有,没有的话再看看f.__proto__(即F.prototype)上有没有,没有的话再看f.__proto__.__proto__有没有,一步一步往上查

    另外关于Function和Object比较特殊的点:

    1. Object因为是构造函数,是函数,所以作为Function的实例,Object.__proto__ 指向其构造函数的prototype,即Function.prototype,Object.__proto__  === Function.prototype
    2. 同理,Function因为是构造函数,是函数,所以作为实例,Function.__proto__指向其构造函数的prototype,即Function.prototype,Function.__proto__ ===  Function.prototype
    3. Function.prototype因为是对象,是Object的实例,所以其__proto__指向 Object.prototype

    参考文章:

  • iterm2自动恢复会话/标签设置

    自从换了公司并习惯在Mac上做开发之后,与命令行打交道的机会就变多了,经常用iterm2这个神器但是有一个很蛋疼的地方,就是在退出之后上一次的会话,或者是标签等都没有保存,这样就不得不再打开一个个tab,敲一遍遍路径(虽然有autojump会稍微快点)。好了,不废话,下面告诉你怎么搞。

    1、下载2.9beta及以上版

    因为这个功能在2.1.4中还没有,所以乖乖去下吧,不然打破头皮也没用。

    下载地址在这里: iTerm2 最新稳定版

    当然版本可能会更新,所以你也可以在这里找最新的地址:https://iterm2.com/downloads.html

    2、安装并设置

    安装我就不多说了,和其他APP的安装一样,解压到应用程序文件夹就可以了。

    2.1 设置Session Restoration

    然后,打开 Preference>Advanced>Enable session restoration

    iterm2-SessionRestoration

    iterm2-Session Restoration

    有一点需要注意:我在装完这个beta版之后上面截图的选项默认已经是Yes,但是特么的没有一点效果,在将这个选项关掉,退出,然后再打开,再退出,然后才有效。所以如果你也遇到这个问题,试试我的这个方法吧。

    2.2 设置Startup

    打开Preference>General,然后设置Startup为 Use System Window Restoration Setting

    iterm2-Startup

    iterm2-Startup

    2.3 设置系统选项

    没错,还要修改系统设置,因为这是iterm2利用的系统特性才能完成的功能。

    打开 系统偏好设置>通用,把退出应用时关闭窗口选项关掉。

    iterm2-系统设置

    iterm2-系统设置 更多...

  • CSS3 transform的一些细节

    之前虽然用CSS3 transform做过动画相关项目,目的就是让元素动起来,看着比较酷炫,仅此而已。但是最近想用transform实现小于12px文字的时候却发现transform并未将由于文字过长而换行的文字改为单行,那正好趁着这个机会小小的研究一把。

    用transform实现小于12px大小时的一些细节

    众所周知Chrome里面(据说只有中文版,未测试)文字最小为12px,那么要实现10px的文字大小,就会用到transform

    transform:scale(.5)

    这对于正常的应用来说是没问题的,比如下面的例子

    See the Pen font-size smaller than 12px demo1 by Bane Lee (@zenoven) on CodePen.

    例1

    .a是正常的20px大小,.b用font-size设置为10px但是chrome下无效,.c用transform实现10px大小。 但是当遇到空间小而数据多并且要求不换行的时候,用scale来缩小文字就会有一定的问题,如下例无论缩放多小,文字始终还是两行。

    See the Pen transform has no effect by Bane Lee (@zenoven) on CodePen. 更多...