JS高程:读书摘要(二十)终章

2019-03-30  本文已影响0人  Upcccz

一、可维护性

松散耦合

HTML 是数据,JavaScript 是行为。因为它们天生就需要交互,所以有多种不同的方法将这两个技术关联起来,一般来说,你应该避免在JavaScript 中创建大量HTML,也避免在HTML使用JS字符串处理事件。

HTMLJavaScript 解耦可以在调试过程中节省时间,更加容易确定错误的来源,也减轻维护的难度:更改行为只需要在JavaScript 文件中进行,而更改标记则只要在渲染文件中。

由于CSS 负责页面的显示,当显示出现任何问题时都应该只是查看CSS文件来解决。然而,当使用了JavaScript来更改某些样式的时候,就出现了第二个可能已更改和必须检查的地方。结果是JavaScript 也在某种程度上负责了页面的显示,并与CSS 紧密耦合。

通过只修改某个元素的CSS 类,就可以让大部分样式信息严格保留在CSS 中。JavaScript 可以更改样式类,但并不会直接影响到元素的样式。只要应用了正确的类,那么任何显示问题都可以直接追溯到CSS 而非JavaScripts

在事件处理程序中包含大量应用逻辑的弊病就是,如果没有发生预想的结果怎么办?是不是表示事件处理程序没有被调用还是指应用逻辑失败?其次,如果有操作需要同样的应用逻辑,那就必须复制功能代码或者将代码抽取到一个单独的函数中。

解耦需要注意的是

编程实践

因为排除null并不代表数据类型的确定,例:期望得到数组却得到了字符串,通过了与null相比较的验证,但依然可能出现错误。如果看到了与null 比较的代码,尝试使用以下技术替换:

  1. 使用常量

二、性能

注意作用域

随着作用域链中的作用域数量的增加,访问当前作用域以外的变量的时间也在增加。访问全局变量总是要比访问局部变量慢,因为需要遍历作用域链,在一个函数中将用到多次的全局对象存储为局部变量。

with语句会创建自己的作用域,增加了其中执行的代码作用域链长度,况且with主要用来消除额外的字符串,使用局部变量可以完成相同的事情。

选择正确的方法

算法类型:O(1)、O(log n)、O(n)、O(n²)

常量值查找和访问数组元素都是O(1)操作,而对象上的任何属性查找是O(n)操作,要比访问变量或者数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索。简而言之,属性查找越多,执行时间就越长。

一旦多次用到对象属性,应该将其存储在局部变量中。第一次访问该值会是O(n),然而后续的访问都会是O(1),就会节省很多。

如果需要遍历的对象的项是规定且数量不大时,展开循环可以消除建立循环和处理终止条件的额外开销,使代码运行得更快。

Duff 装置技术,以8次为限进行循环展开。

var iterations = Math.floor(values.length / 8); 
var leftover = values.length % 8; // 余数
var i = 0;

if (leftover > 0){ // 在这里处理多余出来的项
    do {
        process(values[i++]); // i++ 在当行不会加1 当行执行完才加1  
    } 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);// 用整除数 来决定进行8次循环的次数
//某些代码求值——避免!!
eval("alert('Hello world!')");
//创建新函数——避免!!
var sayHi = new Function("alert('Hello world!')");
//设置超时——避免!!
setTimeout("alert('Hello world!')", 500);
最小化语句数
优化DOM交互
var list = document.getElementById("myList"),
    fragment = document.createDocumentFragment(),
    item,
    i;
for (i=0; i < 10; i++) {
    item = document.createElement("li");
    fragment.appendChild(item);
    item.appendChild(document.createTextNode("Item " + i));
}
list.appendChild(fragment);

或者使用字符串拼接 + innerHTML

任何时候要访问HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进行一个查询,这个查询开销很昂贵。最小化访问HTMLCollection 的次数可以极大地改进脚本的性能。

1、 进行了对getElementsByTagName() 的调用;
2、 获取了元素的 childNodes 属性;
3、 获取了元素的 attributes 属性;
4、 访问了特殊的集合,如document.formsdocument.images 等。

以上方法都都会返回HTMLCollection 对象,应该使用变量来存储遍历元素集合时中的元素,避免过多直接访问元素集合的变量。

var images = document.getElementsByTagName("img"), // HTMLCollection对象
    image,
    i, len;
for (i=0, len=images.length; i < len; i++){
    // 取出变量的对象 避免之后的循环体过多的直接方法HTMLCollection对象变量
    image = images[i]; 
    //处理
}

三、新兴API

Page Visibility API
Geolocation API 地理定位

1、navigator.geolocation 对象

会触发请求用户共享地理定位信息的对话框。接受三个参数,成功回调,失败回调【可选】,选项对象【可选】。

成功回调接受一个Position对象参数,拥有coordstimestamp两个属性。

cords是一个对象,包含主要信息。

失败回调接受一个错误信息对象,拥有codemessage两个属性,code表示错误的类型:用户拒绝共享(1)、位置无效(2)或者超时(3)。message保存着错误文本信息。

选项对象:

除非确实需要非常精确的信息,否则建议保持enableHighAccuracyfalse值(默认值)。将这个选项设置为true需要更长的时候,而且在移动设备上还会导致消耗更多电量。类似地,如果不需要频繁更新用户的位置信息,那么可以将maximumAge设置为Infinity,从而始终都使用上一次的坐标信息。

用于跟踪用户的位置,接受的参数与getCurrentPosition()方法完全相同,可以使用定时调用getCurrentPosition()方法来替代。

调用watchPosition()会返回一个数值标识符,用于跟踪监控的操作。基于这个返回值可以取消监控操作,只要将其传递给clearWatch()方法即可(与使用setTimeout()clearTimeout()类似)。

File API

File 对象

通过选择文件输入框选择一个或多个文件时,files集合中包含的就是一组File对象。

有下列只读属性:

FileReader类型 : 实现一种异步文件读取机制。

为了读取文件,提供了以下方法

事件:progresserrorload,分别表示是否又读取了新数据、是否发生了错误以及是否已经读完了整个文件。

progress事件的事件对象,包含lengthComputable(进度信息是否可用的布尔值)、loaded(已经加载的字节数)和total(总字节数)三个属性。

读取错误触发error事件,相关信息保存在FileReadererror属性中,1 表示未找到文件,2 表示安全性错误,3 表示读取中断,4 表示文件不可读,5 表示编码错误。

文件成功加载后会触发load 事件;如果发生了error 事件,就不会发生load 事件。

读取部分内容

这个方法在Firefox中的实现叫mozSlice(),在Chrome 中的实现叫webkitSlice()Safari5.1 及之前版本不支持这个方法。slice()方法接收两个参数:起始字节及要读取的字节数。这个方法返回一个Blob的实例,BlobFile 类型的父类型。

对象URL

对象URL 也被称为blob URL,指的是引用保存在FileBlob 中数据的URL。使用对象URL的好处是可以不必把文件内容读取到JavaScript 中而直接使用文件内容。为此,只要在需要文件内容的地方提供对象URL 即可。要创建对象URL,可以使用window.URL.createObjectURL()方法,并传入FileBlob 对象。

let blob = new Blob([response.data], {type: 'application/x-xls'}) // 后台返回的数据response.data
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'xx模板.xls' // 文件名
link.click() // 立即下载文件
var fileList = document.getElementById('fileInput')
fileList.onchange = function(e){
    var files = e.files
    var url = createObjectURL(files[0]);
    if (url){
        if (/image/.test(files[0].type)){
            output.innerHTML = "<img src=\"" + url + "\">";
        } else {
          output.innerHTML = "Not an image.";
        }
    } else {
        output.innerHTML = "Your browser doesn't support object URLs.";
    }
}

读取拖放的文件

从桌面上把文件拖放到浏览器中也会触发drop 事件。而且可以在event.dataTransfer. files中读取到被放置的文件,当然此时它是一个File 对象,与通过文件输入字段取得的File 对象一样。但是必须取消dragenterdragoverdrop 的默认行为。在drop 事件中,可以通过event.dataTransfer.files 读取文件信息。

使用XHR上传文件

首先,要创建一个FormData对象,通过它调用append()方法并传入相应的File 对象作为参数。然后,再把FormData 对象传递给XHRsend()方法,结果与通过表单上传一模一样。

if (event.type == "drop"){
    data = new FormData();
    files = event.dataTransfer.files;
    i = 0;
    len = files.length;
    while (i < len){
        data.append("file" + i, files[i]); 
        //第一个参数需要与后台一致,不然后台读不到
        i++;
    }
    data.append("otherParams" , "otherValue");// 也可以同时传其他参数
    xhr = new XMLHttpRequest();
    xhr.open("post", "FileAPIExample06Upload.php", true);
    xhr.onreadystatechange = function(){
        if (xhr.readyState == 4){
            alert(xhr.responseText);
        }
    };
    xhr.send(data);
}
Web 计时

Web 计时机制的核心是window.performance 对象。对页面的所有度量信息,包括那些规范中已经定义的和将来才能确定的,都包含在这个对象里面。Web Timing 规范一开始就为performance对象定义了两个属性。

redirectCount:页面加载前的重定向次数。
type:数值常量,表示刚刚发生的导航类型。 0代表页面第一次加载,1代表页面重载过,2页面是通过“后退”或“前进”按钮打开的。

当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等位于脚本前面的css加载完才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。

浏览器渲染过程.png
Web Workers

通过使用Web WorkersWeb应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

// main.js

var worker = new Worker("stufftodo.js");
//这行代码会导致浏览器下载stufftodo.js,但只有worker 接收到消息才会实际执行文件中的代码。

worker.postMessage('start! ');
// 消息内容可以是任何能够被序列化的值 发送到stufftodo.js的数据

worker.onmessage = function(event){
    var data = event.data; 
    //对数据进行处理
}
// 接受来自stufftodo.js传回的值

worker.onerror = function(event){
    console.log("ERROR: " + event.filename + " (" + event.lineno + "): " +
                  event.message);
};
// filename、lineno 和message,分别表示发生错误的文件名、代码行号和完整的错误消息。

worker.terminate(); //立即停止Worker 的工作
// 后续的所有过程都不会再发生(包括error 和message 事件也不会再触发)。

关于Web Worker,最重要的是要知道它所执行的JavaScript 代码完全在另一个作用域中,与当前网页中的代码不共享作用域。在Web Worker 中,同样有一个全局对象和其他对象以及方法。但是,WebWorker 中的代码不能访问DOM,也无法通过任何方式影响页面的外观。

在这个特殊的全局作用域中,thisself引用的都是worker 对象。为便于处理数据,Web Worker 本身也是一个最小化的运行环境。

// stufftodo.js

// 接受来自主进程的数据
self.onmessage = function(event){
    var data = event.data;
    //别忘了,默认的sort()方法只比较字符串
    data.sort(function(a, b){
        return a – b;
    });
    self.postMessage(data); // 给主进程发送计算过的数据 
    // 会触发main.js中的onmessage事件
};

// main.js
var data = [23,4,7,9,2,14,6,651,87,41,7798,24],
    worker = new Worker("stufftodo.js");

worker.onmessage = function(event){
    var data = event.data;
    //对排序后的数组进行操作
};
//将数组发送给worker 排序
worker.postMessage(data);

包含其他脚本

// stufftodo.js

importScripts("file1.js", "file2.js");
// 在work中引入其他脚本

即使file2.js 先于file1.js 下载完,执行的时候仍然会按照先后顺序执行。而且,这些脚本是在Worker 的全局作用域中执行,如果脚本中包含与页面有关的JavaScript 代码,那么脚本可能无法正确运行。请记住,Worker 中的脚本一般都具有特殊的用途,不会像页面中的脚本那么功能宽泛。

web worker可以运行异步JavaScript 代码,避免阻塞用户界面。在执行复杂计算和数据处理的时候,这个API非常有用;要不然,这些任务轻则会占用很长时间,重则会导致用户无法与页面交互。

上一篇下一篇

猜你喜欢

热点阅读