大杂烩 | 一枝秋雨

大杂烩

惰性载入函数

函数执行的分支仅会发生一次,有两种实现方案。

方案一:函数被调用时再处理函数,在第一次调用的过程中,覆盖原函数。

function createXHR() {
  if (typeof XMLHttpRequest !== undefined) {
    createXHR = function() {
      return new XMLHttpRequest();
    }
  } else if (....) {
    createXHR = function() {
      //TOOD
    }
  } else {
    createXHR = function() {
      //TOOD
    }
  }
}

以后再调用 createXHR() 的时候,就会直接调用分配后的函数,没有 if 分支了。

方案二: 在声明函数时就指定适当的函数,第一次调用函数时不会损失性能,而在代码首次加载时会损失一点性能

var createXHR = (function() {
  if (XMLHttpRequest !== undefined) {
    return function() {
      return new XMLHttpRequest();
    }
  } else if(...) {
    return function() {
      //...
    }
  } else {
    return function() {
      //...
    };
  }
}());

思想创建一个匿名、自执行的函数,用以确定使用哪个函数实现。

惰性载入函数的优点是只在执行代码时牺牲一点儿性能

函数绑定

函数绑定要创建一个函数,可以在特定的 this 中以指定参数调用另一个函数,通常和回调函数与处理程序一起使用,以便在将函数作为变量传递的同时保证代码的执行环境

function bind(fn, content) {
  return function () {
    return fn.apply(content, arguments);
  }
}
 
EventUtil.addHandler(btn, "click", bind(handler.handlerClick, handler));
 
//ECMAScript5
EventUtil.addHandler(btn, "click", handler.handlerClick.bind(handler));

函数柯里化

用于创建已经设置好了一个多个参数的函数,使用一个闭包返回一个函数

调用另一个函数并未它传入要柯里化的函数和参数

function curry(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var innerArgs = Array.prototype.slice.call(arguments);
 
    var finalArgs = args.concat(innerArgs);
 
    return fn.apply(null, finalArgs);
  }
}
 
function add(n1, n2) {
  return n1 + n2;
}
 
curry(add,6)(2)

定时器

浏览器只是负责进行排序,指派某段代码在某个时间点运行的优先级

  • setTimeout()
  • setInterval()

除了主 JavaScript 执行进程外,还有一个需要在进程下一次空闲时执行的代码队列,在 JavaScript 中没有任何代码是立即执行的,但一旦进程空闲则尽快执行

设置的定时器时间表示代码会在到达执行的时间会将代码加入到队列中,即指定的时间间隔表示何时将定时器的代码添加到队列,而不是何时实际执行的代码。

存在两个问题

  1. 某些间隔会被跳过
  2. 多个定时器的代码执行之间的间隔可能会比预期的小

函数节流

目的是只有在执行函数的请求停止了一段时间之后才执行

var processor = {
  timeoutId: null,
 
  // 实际进行处理的方法
 
  performProcessing: function() {
    //实际执行的代码
  },
 
  //初始处理调用方法
 
  process: function() {
    clearTimeout(this.timeoutId);
 
    var that = this;
 
    this.timeoutId = setTimeout(function(){
      that.performProcessing();
    }, 100);
  }
}

process() 是初始化任何处理所必需要调用的,performProcessing() 实际进行应完成的处理

function throttle(method, content) {
  clearTimeout(method.tId);
 
  method.tId = setTimeout(function() {
    method.call(content);
  }, 100)
}
 
window.onresize = function() {
  throttle(process);
}

自定义事件

有助于将不同部分的代码相互之间解藕,让维护更加容易,并减少引入错误的机会

EventTarget.js

离线 Web 应有

1. 首先确保应用知道是否能上网

navigator.onLine  为 true 表示设备能上网,为 false 表示设备不能离线

window 的两个事件 online 和 offline ,反别是当网络从离线变在线或从在线变离线时触发

为了检测应用是否离线,在页面加载后,最好先通过 navigator.onLine 取得初始值的状态,然后通过上述两个事件来确定往事链接状态是否变化

2. 必须能访问一定的资源

3. 必须有一块本地空间用于保存数据

应用缓存

mainfile file 文件列出要下载和缓存的文件

<html mainfest="/offline.mainfest"> 告诉页面 /offline.mainfest 中包含着描述文件,这个文件的 MIME 类型必须是 text/cache-mainfest

HMLT5 API applicationCache,有 status 属性及一些事件

{
  0: 无缓存
  1: 闲置
  2: 检查中
  3: 下载中
  4: 更新完成
  5: 弃用
}

数据存储

1. Cookie

最初使用在客户的用于存储会话信息的

限制

Cookie 是绑定在特定的域名下,大小也有限制,在4098B 以内,每个域对 Cookie 的数量也有限制。

组成

  • 名称:Cookie 的名称需要经过 URL 编码,
  • 值:Cookie 的值也需要 URL 编码
  • 域:可以是子域,如不指定会被任务来自设置 Cookie 的那个域
  • 路径: 指定特殊的路径
  • 失效时间
  • 安全标志:指定后 cookie 只使用 SSL 链接的时候才发送到服务器

document.cookie 获取cookie字符串

2. Web 存储机制

提供一种在 Cookie 之外存储会话数据的途径

提供一种存储大量可以跨会话存在的数据的机制

sessionStorage 对象实现

存储特定于某个会话的数据,也就是该数据只保持到浏览器关闭

localStorage 对象

持久保存客户端数据方案,要访问 localStorage 对象,页面必须来自同一个域名,使用同一个协议,且同一个端口

storage 事件 

对 Storage 对象进行的修改,都会触发 storage 事件,事件event的属性值

  • domain 发生变化的存储空间的域名
  • key 设置或者删除的key
  • newValue 如果设置新值,则是新值;如果删除键,则是 null
  • oldValue 键被更改之前的值

对于 localStorage 而言,大多数桌面浏览器会设置每个来源 5MB 的限制, Chrome 和 Safari 对每个来源的限制是 2.5M,而 IOS 和 Android 的限制也是 2.5MB。

性能

Duff 装置

//credit: Jeff Greenberg for JS implementation of Duff&#39;s Device
 
var iterations = Math.ceil( values.lenght / 8);
var startAt = values % 8;
var i = 0;
 
do {
  switch (startAt) {
    case 0: process(values[i++]);
    case 1: process(values[i++]);
    case 2: process(values[i++]);
    case 3: process(values[i++]);
    case 4: process(values[i++]);
    case 5: process(values[i++]);
    case 6: process(values[i++]);
    case 7: process(values[i++]);
  }
  startAt = 0;
} while (--iterations > 0);

Duff 装置的实现是通过将 values 数组中元素个数除以 8 来计算出循环需要进行多少次迭代的,然后使用取整的上限函数确保结果是整数。如果完全根据除 8 来进行迭代,可能会有一些不能被处理到的元素,这个变量保存在 startAt 变量中。展开循环可以提升大数据集的处理速度

Speed Up Your Site 

将 do while 循环分成两个单独的循环

//credit: Speed Up Your Site
 
var iterations = Math.ceil( values.lenght / 8);
var leftOver = values % 8;
var i = 0;
 
if (leftOver > 0) {
  do {
    process(values[i++]);
  } while (--leftOver > 0);
}
 
do {
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
} while (--iterations > 0);

比原始的 Duff 装置实际上要快 40%

性能

  • 避免全局查找
  • 避免 with 语句
  • 避免不必要的查找
  • 优化循环

    •   减值迭代
    •   简化终止条件
    •   简化循环体
    •   使用后测试循环
  • 展开循环
  • 其他优化

    •   原生方法较快
    •   switch 语句较快
    •   位运算较快
  • 最小化语句
  • 多个变量声明
  • 插入迭代
  • 使用数组和对象自变量
  • 优化DOM 交互

1. 最小化现场更新

使用文档片段 CreateDocumentFragment()

2. 使用 innerHTML

页面创建 DOM 节点的方法 createElement() 和 appendChild() 之类的方法以及 innerHTML,对于大的 DOM 更改,使用 innerHTML 要比标准的 DOM 方法快很多。当把 innerHTML 设置为某个值,后台会创建一个 HTML 解析器,使用内部的 DOM 调用来创建 DOM 结构

3. 使用事件代理 

4. 注意 HTMLCollection

任何时候访问 HTMLCollection,不管是属性还是方法,都是在文档上进行一次查询。发生以下情况时会返回 HTMLCollection 对象

  • 调用 getElementsByTagName()
  • 获取元素的 childNode 属性
  • 获取元素的 attributes 属性
  • 访问了特定的集合 如 document.forms、document.images 等

新型API

1. requestAnimationFrame()

console.log("aa");
requestAnimationFrame(function() {
  console.log("bb");
})
console.log("cc")
 
// aa -> cc ->bb

2. Page Visibility

让开发人员知道页面是否对用户可见

document.hidden 表示页面是否隐藏的布尔值

document.visibilityState 

  • 页面在后台标签页中或浏览器最小化
  • 页面在前台标签中
  • 实际的页面已经隐藏,但用户可以看到页面的预览
  • 页面在屏幕外执行预渲染处理

visibilitychange 事件 当文档从可见于不可见切换时触发,document 上的事件

3. Geolocation API

地理定位 API 在浏览器中实现是 navigator.geolocation 对象

  • getCurrentPosition(/*成功回调*/, /*失败回调*/, /*可选参数*/)
  • watchPosition(/*成功回调*/, /*失败回调*/, /*可选参数*/)

4、File API

HTML5 在 DOM 中为文件输入元素添加了一个 files 集合,在通过文件输入字段选择一个或多个文件时,files 集合中将会包含一组 File 对象,每个 File 对象对应着一个文件,每个 File 对象有如下只读属性

  • name: 文件名
  • size: 字节大小
  • type: 字符串、文件的 MIME 类型
  • lastModifiedDate: 字符串

FileReader 类型

是一种异步文件读取机制,可以将 FileReader 想象成 XMLHttpRequest 区别在于它只是读取文件系统

  • readAsText(file, encoding) 纯文本读取文件,将结果保存在 result 属性中
  • readAsDataURL(file) 读取文件并将文件以数据 URL的形式保存在 result 属性中
  • readAsArrayBuffer(file) 读取文件并将一个包含文件内容的 ArrayBuffer 保存在 result 属性中
  • process 事件: 读取数据,每隔 50 ms 触发一次
  • error 事件:发生错误,错误 信息保存在 error 属性中,错误 code 码,1 文件未找到, 2 安全性错误 3 读取中断 4 文本不可读 5 编码错误
  • XHR 上传文件

使用 formData 可以很容易上传,方案如下:首先要创建一个 FormData 对象,通过调用 append() 方法传入相应的 File 对象作为参数,然后再把 FormData 对象传递给 XHR 的 send() 方法,结果与表单上传一模一样

var data = new FormData();
data.append(files);
xhr.send(data);

load 事件:读完

5. window.performance

给出页面加载和渲染过程的很多信息,对性能优化有价值

performance.navigation 属性也是一个对象,多与页面导航有关

  • redirectCount:页面加载前的重定向次数
  • type:发生的导航类型
{
  0: 第一次价加载
  1: 页面重载
  2: 通过后退、前进按钮打开
}

performance.timing 属性也是一个对象,该属性都是时间戳

6. Web Workers

可以运行异步 JavaScript 代码,避免阻塞用户界面,适用于在执行复杂计算和数据处理

Web Workers 规范通过让 JavaScript 在后台运行解决浏览器冻结的情况。

var worker = new Workers("a.js");
 
//传递消息
worker.postMessage({
  //...
})
 
 
worker.onmessage = function(evt) {
  //evt.data 数据
}
 
worker.onerror = function(evt) {
  console.log(evt.filename + " " + evt.linene + " " evt.message);
}
 
worker.terminate(); //立即执行worker 的工作

Web Worker 中的代码不能访问 DOM,也无法通过任何方式影响页面的外观,它所执行的代码完全在另外一个作用域中,与当前网页中的代码不共享作用域, Web Worker 中的全局对象时 worker 对象本身。

JavaScript基础系列

面试题