默认分类
只是一个默认分类

throttle 和 debounce 函数

一、throttle 函数节流

节流就是控制函数执行的频率,比如mousemove、window对象的resize和scroll 事件等,这些事件出发频率都很高。所以我们会用节流函数进行封装,以提高性能。
function throttle(func, wait, options) {

  var leading = true,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  return debounce(func, wait, {
    'leading': leading,
    'maxWait': wait,
    'trailing': trailing
  });
}

二、debounce 函数去抖

debounce和throttle很像,debounce是空闲时间必须大于或等于一定值的时候,才会执行调用方法。

 function debounce(func, wait, options) {
      var lastArgs,
          lastThis,
          maxWait,
          result,
          timerId,
          lastCallTime,
          lastInvokeTime = 0,
          leading = false,
          maxing = false,
          trailing = true;
  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  wait = toNumber(wait) || 0;
  if (isObject(options)) {
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }

  function invokeFunc(time) {
    var args = lastArgs,
        thisArg = lastThis;

    lastArgs = lastThis = undefined;
    lastInvokeTime = time;
    result = func.apply(thisArg, args);
    return result;
  }

  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time;
    // Start the timer for the trailing edge.
    timerId = setTimeout(timerExpired, wait);
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result;
  }

  function remainingWait(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime,
        result = wait - timeSinceLastCall;

    return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
  }

  function shouldInvoke(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime;

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
  }

  function timerExpired() {
    var time = now();
    if (shouldInvoke(time)) {
      return trailingEdge(time);
    }
    // Restart the timer.
    timerId = setTimeout(timerExpired, remainingWait(time));
  }

  function trailingEdge(time) {
    timerId = undefined;

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time);
    }
    lastArgs = lastThis = undefined;
    return result;
  }

  function cancel() {
    if (timerId !== undefined) {
      clearTimeout(timerId);
    }
    lastInvokeTime = 0;
    lastArgs = lastCallTime = lastThis = timerId = undefined;
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(now());
  }

  function debounced() {
    var time = now(),
        isInvoking = shouldInvoke(time);

    lastArgs = arguments;
    lastThis = this;
    lastCallTime = time;

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime);
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = setTimeout(timerExpired, wait);
        return invokeFunc(lastCallTime);
      }
    }
    if (timerId === undefined) {
      timerId = setTimeout(timerExpired, wait);
    }
    return result;
  }
  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced;
}

三、区别

我们用一个输入框的例子来解释一下这两个函数的区别,节流函数可以运用在保存按钮上,如果你连续点击保存按钮时,可以控制最短保存间隔为一秒,这样就能去掉重复请求;而去抖函数可以用在自动保存上,只有停止编辑一秒之后才会触发保存功能,如果这一秒内又重新开始编辑,就会取消计时器。所以再涉及重复调用的问题上都可用这两个函数进行优化。

JS中的一些类型判断

一、判断Array

一般来说我们判断类型会使用 typeof 操作符来判断就足够了,但是如果你想判断用typeof判断数组,他只会返回一个'object '。你要是问问什么,那就要好好温习一下基础知识了,最新的JS定义了七种数据类型:

六种 原型 数据类型:

  • Boolean. 布尔值,true 和 false.
    1. 一个表明 null 值的特殊关键字。 JavaScript 是大小写敏感的,因此 null 与 Null、NULL或其他变量完全不同。
    1. 变量未定义时的属性。
  • Number. 表示数字,例如: 42 或者 3.14159。
  • String. 表示字符串,例如:"Howdy"
  • Symbol ( 在 ECMAScript 6 中新添加的类型).。一种数据类型,它的实例是唯一且不可改变的。
  • Object

Array并不属于JS基本类型,所以对数组的判断ES5给出了一个单独的isArray()方法。
另外还可以使用Object.prototype.toString()来判断,下面给出了对应表格。

Value               toString   typeof
"foo"               String     string
new String("foo")   String     object
1.2                 Number     number
new Number(1.2)     Number     object
true                Boolean    boolean
new Boolean(true)   Boolean    object
new Date()          Date       object
new Error()         Error      object
[1,2,3]             Array      object
new Array(1, 2, 3)  Array      object
new Function("")    Function   function
/abc/g              RegExp     object
new RegExp("meow")  RegExp     object
{}                  Object     object
new Object()        Object     object 

关于这种方法的原理不再过多叙述,可以参考这篇文章JavaScript:Object.prototype.toString方法的原理

二、判断空对象

function isEmptyObject(e) {  
    var t;  
    for (t in e)  
        return !1;  
    return !0  
}  

这是jQuery的源码,原理就是使用 for in 来遍历对象中的属性名.如果一个对象有属性名则return false,否则return true;

三、判断 Null

你也许会想到使用 typeof ,但是他只会返回一个 object

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是0。由于
null 代表的是空指针(大多数平台下值为0x00),因此,null的类型标签也成为了0,typeof
null就错误的返回了"object"

其实判断Null是最简单的,只需要用===即可

参考文章
1.typeof

Socket、SOCKS和WebSocket

一、socket

socket是一个网络概念,它起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作,而socket就是这个模式在网络编程中的实现,它通常称为套接字插口
当应用进程需要进行网络通信时,必须首先发出socket系统调用,然后操作系统会把进行网络通信的系统资源如:CPU时间、带宽等分配给这个应用进程;通信结束时,应用进程再掉用关闭socket方法,通知操作系统回收之前分配的资源。

二、sockes5

sockes5是一种代理上网的加密协议,注意不要与socket搞混了哦。现在最好用的翻墙工具ShadowSockes其实就是基于sockes5协议实现的。

这里简单介绍一下建立代理链接的流程:
1.验证:
客户端告诉代理服务器自已支持的验证方式
代理服务器收到上面的报文,选择自已所能支持的验证方式,然后客户端发送相应验证(如用户名密码),最后服务端返回验证状态。
2.建立代理连接:
客户端发送要建立的的代理连接的地址及端口
代理服务器收到客户端的请求后创建到目标机器的连接或创建监听目标机器的socket
3.数据包转发
TCP直接转发,UDP需要客户端先发起UDP ASSOCIATE请求,得到UDP中继地址和端口后,再在报文上加上请求头才能发送。

三、WebSocket

WebSocket一种在单个 TCP 连接上进行全双工通讯的协议。
意思就是浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。
在之前要实现这种效果需要AJAX轮询或者HTTP持久连接(长链接),但是这种方法都会消耗很多服务器资源和带宽,所以HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。

最常见的例子,就是用WebSocket实现在线聊天和视频直播中的弹幕。我们可以使用Socket.IO框架,他会自动选择合适双向通信协议,对于不支持WebSocket,会回退到其他方法。

参考资料:
1.WebSocket

Angular 中的异步

之前已经了解过ES6中使用Promise,而在Angular中是通过 $q 服务实现的。

一、$q 的方法:

  1. $q.defer() 返回一个 deferred 对象。
  2. $q.reject(reason) 包装一个错误,以使回调链能正确处理下去。
  3. $q.when(value, [successCallback], [errorCallback], [progressCallback]) 返回一个 promise 对象,为了与ES6保持一致他还有一个别名 resolve
  4. $q.all(promises) 合并多个 promise 成一个,当所有的 promise 都完成时这个新的才算完成。
  5. $q.race(promises) 合并多个 promise 成一个,只要有一个 promise 完成这个新的就算完成。

二、defer 和 promise 的方法

1.defer 对象有两个方法一个属性。

promise 属性就是返回一个 promise 对象的。
resolve() 成功回调
reject() 失败回调

2.promise 对象只有 then() 一个方法,注册成功回调函数和失败回调函数,再返回一个 promise 对象,以用于链式调用。
$q.reject() vs deferred.reject()

一个基本的例子:

var TestCtrl = function($q){
    var defer = $q.defer();
    var promise = defer.promise;
    promise.then(function(data){console.log('ok, ' + data)},
        function(data){console.log('error, ' + data)});
    //defer.reject('xx');
    defer.resolve('xx');
}

首先通过 $q 服务得到一个 defer 实例,再通过这个实例的 promise 属性得到一个 promise 对象。promise 对象负责定义回调函数,defer 实例负责触发回调。

拓展阅读:
1.广义回调管理
2.$q