第22章,高级技巧

高级函数

安全的类型检测

typeof会出现无法预知的行为
instanceof在多个全局作用域中并不能正确工作
调用Object原生的toString方法,会返回[Object NativeConstructorName]格式字符串。每个类内部都有一个[[Class]]属性,这个属性中就指定了上述字符串中的构造函数名。
原生数组的构造函数名与全局作用域无关,因此使用toString方法能保证返回一致的值,为此可以创建如下函数:
function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}

也可以基于这一思路测试某个值是不是原生函数或正则表达式:

//判断是否原生函数
function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
//判断是否原生函数
function isFunction(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}
注:对于IE中以COM对象形式实现的任何函数,isFunction都将返回false,因为他们并不是js原生函数
注:Object的toString方法不能检测非原生构造函数的函数名。

作用域安全的构造函数

对于构造函数,使用了new操作符,则首先创建一个新的对象,将this指针指向新创建的对象,如果没有使用new操作符,则this指针会指向window对象,作用域安全的构造函数:

function Person(name,age,job){
if(this instanceof Person){ //判断this是否是正确的类型
this.name = name;
this.age = age;
this.job = job;
}else{
return new Person(name,age,job);
}
} var per1 = Person("Nicholas",29,"Software Engineer");
alert(window.name); //""
alert(per1.name); //"Nicholas" var per2 = new Person("Shelby",34,"Ergonomist");
alert(per2.name); //"Shelby"
建议:推荐使用作用域安全的构造函数作为最佳实践

惰性载入函数

对于多分支的if语句,有些时候并不需要每次都查询if语句,例如createXHR函数:
function createXHR(){
if(typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest;
}else if(typeof ActiveXObject != "undefined"){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
for(i=0,len=versions.length;i < len;i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("No XHR Object available");
}
}
需要每次都对浏览器进行检测,根本没有必要,只要检测一次就足够了。对于此情况,解决方案:惰性载入技巧
惰性载入表示函数分支只会执行一次。
方法1、在函数被调用时处理函数,在第一次调用时该函数会被覆盖为另一个按合适方式执行的函数。

function createXHR(){
if(typeof XMLHttpRequest != "undefined"){
createXHR = function(){ //将原函数覆盖
return new XMLHttpRequest();
};
}else if(typeof ActiveXObject != "undefined"){
createXHR = function(){ //将原函数覆盖
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
for(i=0,len=versions.length;i < len;i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
}else{
createXHR = function(){ //将原函数覆盖
throw new Error("No XHR object available.");
};
}
return createXHR();
}

方法2、在声明函数时指定适当的函数,这样第一次调用函数不损失性能,在代码首次加载时会损失性能

var createXHR = (function(){
if(typeof XMLHttpRequest != "undefined"){
return function(){
return new XMLHttpRequest();
};
}else if(typeof ActiveXObject != "undefined"){
return function(){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
for(i=0,len=versions.length;i < len;i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
}else{
return function(){
throw new Error("No XHR object available.");
};
}
})();

函数绑定

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用。
var handler = {
message : "Event handled", handleClick : function(event){
alert(this.message);
}
}; var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn,"click",handler.handleClick); //按钮按下显示undefined,不显示Event handled

按下按钮实际显示的是undefined,并不会显示Event handled。问题在于没有保存handler.handleClick的环境,所以this最后指向了DOM按钮,而非handler(IE8中this指向window),可以使用闭包解决问题:

var handler = {
message : "Event handled", handleClick : function(event){
alert(this.message);
}
}; var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn,"click",function(event){
handler.handleClick(event); //按钮按下显示Event handled
});
问题:闭包难以理解和调试。
大部分js库为此实现了可以将函数绑定到指定环境的函数,一般叫做bind()
简单的bind函数,接收一个函数和环境,返回一个给定函数中调用给定函数的函数,并且将所有参数原封不动传过去
//bind函数解决方案
function bind(fn,context){
return function(){
return fn.apply(context,arguments);
};
}

调用方法:

EventUtil.addHandler(btn,"click",bind(handler.handleClick,handler));
ECMAScript5为所有函数定义了原生的bind函数,进一步简化了操作。调用方法
EventUtil.addHandler(btn,"click",handler.handleClick.bind(handler)); //传入作为this的对象

支持的浏览器:IE9+,Firefox4+,Chrome

函数柯里化

用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定一样:使用闭包返回一个函数。区别:函数被调用时返回的函数还需要设置一些传入参数。

防篡改对象

注意:一旦把对象定义为防篡改对象,将不可撤销,跟定义了属性的[[Configurable]]为false的结果差不多

不可扩展对象

使用Object.preventExtensions()方法,让你不能再给对象添加属性和方法
var person = { name : "name"};
Object.preventExtensions(person); person.age = 28;
alert(person.age); //undefined
Object.isExtensible可检测对象是否可扩展

密封对象

密封对象不可扩展,而且已有成员的[[Configurable]]特性将被设置为false。意味着不能修改或删除属性和方法
使用Object.seal()方法密封对象,使用Object.isSealed()方法确定对象是否密封。

var person = { name : "name" };
alert(Object.isExtensible(person)); //true
alert(Object.isSealed(person)); //false Object.seal(person);
alert(Object.isExtensible(person)); //false
alert(Object.isSealed(person)); //true

冻结对象

最严格防篡改级别。冻结对象既不可扩展,又是密封,对象的数据属性的[[Writable]]特性会被设置为false。如果定义了[[Set]]函数,访问器属性仍然可写。
使用Object.freeze()方法冻结对象,Object.isFrozen()方法检测对象是否冻结。

高级定时器

js是单线程的,定时器仅仅只是计划代码在未来某个时间执行,执行时间并不确定。意思就是,js是单线程的,所有需要处理的代码都要排到执行队列中去,而定时器,只是在我们设定的时间之后将代码添加到队列中去,并不一定马上执行代码。

重复定时器

setInterval定时器的功能缺陷:可能在代码再次被添加到队列中时,之前的代码还没有完成执行,结果导致代码运行好几次。js引擎足够聪明,能避免这个问题,在使用setInterval时,仅当没有定时器的任何其他代码实例时,才将定时器代码加入到队列。不过,还是有问题,1、某些间隔会被跳过,多个定时器的代码执行之间的间隔可能比预期小。
使用setTimeout方法(超时调用,执行一次后再定义一个,模拟循环)来定义重复定时器可以避免这些缺点。

函数节流

函数节流背后的基本思想是指,某些代码不可以在没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定时间间隔之后运行代码。当第二次调用函数时,会清除前一次的定时器并设置另一个,如果前一个已经执行过了,此操作无意义。然而,若前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的在于只有执行函数的请求停止了一段时间后才执行。

自定义事件

拖放

第24章,最佳实践

可维护性

特性:

1、可理解
2、直观
3、可适应
4、可扩展
5、可调试

代码约定

1、可读性
(1)使用若干空格而非制表符进行缩进
(2)注释
函数和方法:每个函数或方法都应该包含一个注释,用于描述目的和用于完成任务所可能使用的算法,参数意义,返回值
大段代码:描述任务的注释
复杂算法:解释如何做的注释
Hack:因浏览器差异,js代码通常会包含一些hack,这些需要注释。
2、变量和函数命名
(1)变量名应为名词
(2)函数名应以动词开头,getName(),返回布尔值的函数以is开头,isEnable()
(3)使用合乎逻辑的名字,不必担心长度,长度问题可以使用后处理和压缩。
3、变量类型透明
很容易忘记变量所应包含的数据类型。合适的命名方式可以一定程度上缓解问题。还有以下三种表示变量数据类型的方式。
(1)变量初始化
(2)使用匈牙利标记法来指定变量类型。即,在变量前加一个或多个字符表示变量类型。
(3)使用变量注释
var found /*Boolean*/ = false;

松散耦合

1、解耦HTML/JavaScript
理想情况:HTML和JavaScript应完全分离,使用外部文件和DOM附加行为来包含JavaScript
2、解耦CSS/JavaScript
//CSS对于JavaScript的紧密耦合
element.style.color = "red";
element.style.backgroundColor = "blue"; //CSS对于JavaScript的松散耦合
elements.className = "edit";
3、解耦应用逻辑/事件处理程序

编程实践

1、尊重对象所有权

意思是,不能修改不属于自己的对象。就是说,不是自己创建或维护的对象,就不要更改它的属性和方法,即
(1)不要为实例或原型添加属性
(2)不要为实例或原型添加方法
(3)不要重定义已存在的方法

2、避免全局变量

最多创建一个全局变量

3、避免与null进行比较

4、使用常量

var Constants = {
INVALID_VALUE_MSG:"Invalid value!",
INVALID_VALUE_URL:"/errors/invalid.php"
}; function validate(value){
if(!value){
alert(Constants.INVALID_VALUE_MSG);
location.href = Constants.INVALID_VALUE_URL;
}
}

性能

注意作用域

1、避免全局查找
2、避免with语句

选择正确方法

1、避免不必要的属性查找
2、优化循环
(1)减值迭代在大多数情况下更高效,即循环从最大值开始
(2)简化终止条件
(3)简化循环体
(4)使用后测试循环,do-while循环
3、展开循环
当循环次数是确定的,消除循环并使用多次函数调用往往会更快。
Duff装置。
4、避免双重解释

最小化语句

1、多变量声明

//4个语句---浪费
var count = 5;
var color = "red";
var values = [1,2,3];
var now = new Date(); //一个语句
var count = 5;
color = "red";
values = [1,2,3];
now = new Date();

2、插入迭代值

var name = values[i];
i++;
//合并
var name = values[i++];

3、使用数组和对象字面量

优化DOM操作

1、最小化现场更新(使用仓库)
可以使用DocumentFragment,一次插入多项更改
2、使用innerHTML
对于大的DOM更改,innerHTML方法,比使用标准的DOM方法(createElements,appendChild之类)速度快的多。
3、使用事件代理
4、注意HTMLCollection

压缩

代码长度和配重

代码长度:浏览器所需解析字节数
配重:实际从服务器传送到浏览器的字节数
1、文件压缩(使用压缩工具)
删除额外空白
删除所有注释
缩短变量名
2、HTTP压缩

第25章,新兴API

requestAnimationFrame()

与动画相关

Page VisibilityAPI

因不知道用户是否正在与页面交互而推出的,能够让开发人员知道页面是否对用户可见

Geolocation API

地理定位,navigator.geolocation对象。

File API

宗旨:为Web开发人员提供一种安全的方式,以便在客户端访问用户计算机中的文件

FileReader类型

实现的是一种异步文件读取机制,方法:
readAsText(file,encoding):以纯文本形式读取文件,保存到result属性中,第二个参数可选
readAsDataURL(file):读取文件并将文件以数据URI的形式保存到result属性
readAsBinaryString(file):读文件并将一个字符保存在result属性,字符串中每个字符表示一字节
readAsArrayBuffer(file):读文件并将一个包含文件内容的ArrayBuffer保存在result属性中

读取部分内容

File对象支持slice方法

对象URL

也被称为blob URL,指的是引用在File或Blob中的数据的URL

Web计时

Web Workers

JavaScript高级程序设计(第三版)学习笔记22、24、25章的更多相关文章

  1. JavaScript高级程序设计第三版学习笔记(一)之数据类型区分详谈

    null.NaN.undefined三者的区别是什么? 在初次接触到JavaScript的时候,傻傻的分不清null.NaN.undefined三者到底区别何在,在实际的项目开发中也因为这个问题而困惑 ...

  2. JavaScript高级程序设计第三版-读书笔记(1-3章)

    这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript   提供核心语言功能 DOM     提供访问 ...

  3. JavaScript高级程序设计第三版.CHM【带实例】

    从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...

  4. DOM 操作技术【JavaScript高级程序设计第三版】

    很多时候,DOM 操作都比较简明,因此用JavaScript 生成那些通常原本是用HTML 代码生成的内容并不麻烦.不过,也有一些时候,操作DOM 并不像表面上看起来那么简单.由于浏览器中充斥着隐藏的 ...

  5. Javascript高级程序设计第三版-笔记

    1.JS数值最大值最小值: >Number.MIN_VALUE <5e-324 >Number.MAX_VALUE <1.7976931348623157e+308 判断数值是 ...

  6. javascript高级程序设计第三版书摘

    在HTML 中使用JavaScript <script>元素 在使用<script>元素嵌入 JavaScript 代码时,只须为<script>指定 type 属 ...

  7. 22.1 高级函数【JavaScript高级程序设计第三版】

    函数是JavaScript 中最有趣的部分之一.它们本质上是十分简单和过程化的,但也可以是非常复杂和动态的.一些额外的功能可以通过使用闭包来实现.此外,由于所有的函数都是对象,所以使用函数指针非常简单 ...

  8. 21.1 XMLHttpRequest 对象【JavaScript高级程序设计第三版】

    IE5 是第一款引入XHR 对象的浏览器.在IE5 中,XHR 对象是通过MSXML 库中的一个ActiveX对象实现的.因此,在IE 中可能会遇到三种不同版本的XHR 对象,即MSXML2.XMLH ...

  9. 2.1 <script>元素【JavaScript高级程序设计第三版】

    向 HTML 页面中插入 JavaScript 的主要方法,就是使用<script>元素.这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现.后来 ...

  10. 14.5 富文本编辑【JavaScript高级程序设计第三版】

    富文本编辑,又称为WYSIWYG(What You See Is What You Get,所见即所得).在网页中编辑富文本内容,是人们对Web 应用程序最大的期待之一.虽然也没有规范,但在IE 最早 ...

随机推荐

  1. 【暑假】[实用数据结构]UVa11235 Frequent values

    UVa 11235 Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 11241   Accep ...

  2. 2015上海网络赛 A Puzzled Elena

    题意:给定一棵树,求这个节点的所有子树中包括他本身与它互质的节点的个数. 解题思路:题利用dfs序+容斥原理+前缀和性质解决.题目中要求每个结点,和多少个它的子结点互素.如果每次为了求一个点去跑一遍d ...

  3. 【转】Spark是基于内存的分布式计算引擎

    Spark是基于内存的分布式计算引擎,以处理的高效和稳定著称.然而在实际的应用开发过程中,开发者还是会遇到种种问题,其中一大类就是和性能相关.在本文中,笔者将结合自身实践,谈谈如何尽可能地提高应用程序 ...

  4. RDMA编程实例

    1,RDMA verbs Multicast Code for Multicast Using RDMA_CM(Remote directory memory access_connect manag ...

  5. Thinking in java——Generics

    ​Ordinary classes and methods work with specific types: either primitives or class types. If you are ...

  6. iOS语音播放之切换听筒和扬声器的方法解决方案

    关于流媒体播放的相关知识可以加本人QQ:564702640 一起来讨论 [[UIDevice currentDevice] setProximityMonitoringEnabled:YES]; // ...

  7. emWin -- 模拟器系列1 - 如何建立模拟器开发环境

    面对如此强大的emWin,大家是否都有跃跃欲试的冲动呢?但是没有硬件可以调试的童鞋,难道只能望洋兴叹?非也.非也.Segger公司早就考虑到了.Segger推出模拟器的目的不仅仅是为了解决没有硬件的烦 ...

  8. 【Stage3D学习笔记续】山寨Starling(六):动画实现和测试

    我发布了一个版本v0.2,该版本是未优化版本,且没有添加Touch事件体系,但是由于是最基础且未优化的,所以可以通过参考代码快速的了解实现原理. 接下来的一段笔记开始进行渲染优化,我会把所有的目光都集 ...

  9. iOS 有关自动轮播图片

    //初始化当前视图 _currentImageView = [[UIImageView alloc] init]; [_currentImageView setImageWithURL:[NSURL ...

  10. IOS 在Ipad 横屏 上使用UIImagePickerController

    转载前请注明来源:http://www.cnblogs.com/niit-soft-518/p/4381328.html 最近在写一个ipad的项目,该项目必须是横屏.进入正题,有一项功能是要调用系统 ...