这篇文章我们主要来学习下即时对象初始化初始化时分支、函数属性-备忘模式以及配置对象。这篇的内容会有点多。

六、即时对象初始化

  保护全局作用域不受污染的另一种方法,即时对象初始化模式。这种模式使用带有init()方法的对象,该方法在创建对象后将会立即执行。init()函数需要负责所有的初始化任务。

({
// 在这里可以定义设定值
// 又名配置常数
maxwidth:600,
maxheight:400, // 还可以定义一些实用的方法
gimmeMax:function () {
return this.maxwidth + "x" + this.maxheight;
}, // 初始化
init:function() {
console.log(this.gimmeMax());
// 更多初始化任务
}
}).init()

  就语法而言,对待这种模式就像在使用对象字面量创建一个普通的对象。也可以将字面量包装到括号中(分组操作符),它指示JavaScript引擎将大括号作为对象字面量,而不是作为一个代码块(也不是if或者for循环)。在该括号结束之后,可以立即调用init()方法。

  这两种方法都可以运行:

({...}).init();
({...}.init());

  这种方法可以在执行一次性初始化任务时,保护全局命名空间。

  与仅仅将一堆代码包装到匿名函数的方法相比,这种模式看起来涉及更多的语法特征,但是如果初始化任务更加复杂,它会使整个初始化过程显得更有结构化。比如,私有帮助函数是非常清晰可辩别的,因为他们是临时对象的属性,而在即时函数模式中,他们就很可能只是分散在各处的函数而已。

  这种模式主要适用于一次性的任务,而且在init()完毕后也没有对该对象的访问,如果想要在init()完毕后保存对该对象的一个引用,可以通过在init()尾部添加"return this;"语句实现该功能。

var a = ({
// 在这里可以定义设定值
// 又名配置常数
maxwidth:600,
maxheight:400, // 还可以定义一些实用的方法
gimmeMax:function () {
return this.maxwidth + "x" + this.maxheight;
}, // 初始化
init:function() {
console.log(this.gimmeMax());
// 更多初始化任务
return this;
}
}).init()
console.log(a.maxheight)

七、初始化时分支

  初始化时分支(Init-time branching,也称为加载时分支Load-time branching)是一种优化模式。当知道某个条件在整个程序声明周期内都不会发生改变的时候,仅对该条件测试一次时很有意义的。浏览器嗅探就是一个典型的例子。

  查明DOM元素的计算样式或附加的事件处理程序是另外一个可以受益于初始化时分支模式的场景。绝大多数程序开发员都已经编写过这样的代码,至少有一次在他们的客户端编程生命周期内,既可用于附加或删除事件监听器的工具:

// 之前
var utils = {
addListener: function(el,type,fn) {
if(typeof window.addEventListener === 'function') {
el.addEventListener(type,fn,false);
} else if(typeof document.attachEvent === 'function') { //'IE'
el.attachEvent('on' + type,fn);
} else {
el['on' + type] = fn;
}
},
removeListener:function(el,type,fn){
// 几乎一样
}
}

  此段代码效率低下。每次在调用utils.addListener()或utils.removeListener()时,都会重复执行相同的检查。

  当使用初始化分支的时候,可以在脚本初始化加载时一次性探测出浏览器特征。此时,可以在整个页面生命周期内重定义函数运行方式:

// 之后
var utils = {
addListener:null,
removeListener:null
};
//实现
if(typeof window.addEventListener === 'function') {
utils.addListener = function(el,type,fn){
el.addEventListener(type,fn,false);
};
utils.removeListener = function(el,type,fn){
el.removeEventListener(type,fn,false);
};
} else if(typeof document.attachEvent === 'function') { //判断为IE浏览器
utils.addListener = function(el,type,fn){
el.attachEvent('on' + type,fn);
};
utils.removeListener = function(el,type,fn){
el.detachEvent('on' + type,fn);
};
} else { //更早的版本
utils.addListener = function(el,type,fn){
el['on' + type] = fn;
};
utils.removeListener = function(el,type,fn){
el['on' + type] = null;
};
}

  其实,简单来说,个人理解,分支初始化的意义就在于:把重复的事情仅做一次。

八、函数属性—备忘模式

  函数是对象,因此它们具有属性。事实上,它们确实还有属性和方法。比如,对于每一个函数,无论使用什么样的语法来创建它,它都会自动获得一个length属性,其中包含了该函数期望的参数数量。

function func(a, b, c) {}
console.log(func.length); //

  可以在任何时候将自定义属性添加到你的函数中。自定义属性的其中一个用例是缓存函数结果(即返回值),因此,在下一次调用该函数时就不用重做潜在的繁重计算。缓存函数结果,也被称为备忘。

var myFunc = function (param) {
if(!myFunc.cache[param]) {
var result = {};
// ... 开销很大的操作 ...
myFunc.cache[param] = result;
}
return myFunc.cache[param];
}; // 缓存存储
myFunc.cache = {};

  上面的例子中,函数myFunc创建了一个属性cache,该属性可以通过myFunc.cache像通常那样进行访问。cache属性是一个对象,其中使用传递给函数的参数param作为键,而计算结果作为值。计算结果可以是需要的任意复杂数据结构。

  上面的代码假定该函数只需要一个参数param,并且它是一个基本数据类型。如果有更多以及更复杂的参数,对此的通用解决方案是将它们序列化。即,可以将参数对象序列化为一个JSON字符串,并使用该字符串作为cache对象的键:

var myFunc = function () {
var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
result;
if(!myFunc.cache[cachekey]) {
result = {};
// ... 开销很大的操作 ...
myFunc.cache[cachekey] = result;
}
return myFunc.cache[cachekey];
}; // 缓存存储
myFunc.cache = {};

  请注意,在序列化的过程中,对象的“标识”将会丢失。如果有两个不同的对象并且恰好都具有相同的属性,这两个对象将会共享同一个缓存条目。

  编写前面的函数的另一种方法是使用arguments.callee来引用该函数,而不是使用硬编码函数名称。虽然在目前这是可行的,但是在ES5的严格模式中并不支持arguments.callee。

var myFunc = function (param) {
var f = arguments.callee,
result;
if(!f.cache[param]) {
result = {};
// ... 开销很大的操作 ...
f.cache[param] = result;
}
return f.cache[param];
}; // 缓存存储
myFunc.cache = {};

九、配置对象

  配置对象模式(configuration object pattern)是一种提供更简洁API的方法,尤其是在建立一个库或任何将被其他程序使用的代码的情况。

  其实配置对象的概念很简单,或许你已经在开发工作中使用了:

// 由于软件的开发是不断推进的,需求必然会频繁变更。
// 假设,我们正在编写一个addPerson()的函数,该函数接受人员的名和姓作为参数。
function addPerson(first,last){
//...
}; // 后来,又要加入人员的性别,住址,电话啥的,住址和电话是可选的
// 这里把可选的参数放在末尾
function addPerson(first,last,gender,address,phone){
//...
}; // 然后,又需要直到用户名,注册的日期等,这是必填的。
// 于是,就是这样了
function addPerson(first,last,gender,address,phone,username,addDate){
//...
}; // 这时候,可选的参数,也必须要传递了
addPerson('first','last','gender',null,null,'username','addDate'); // 这个参数列表变得越来越长,越来越无法维护

  所以,我们可以仅使用一个参数来代表所有参数,让我们将该参数对象成为conf,即“配置”的意思。

addPerson(conf);

  然后,该函数的使用者可以这样做:

var conf = {
username:'zaking',
first:"Bruce",
last:"Wayne"
};
addPerson(conf);

  配置对象的优点在于:

  • 不需要记住众多的参数以及其顺序。
  • 可以安全忽略可选参数。
  • 更加易于阅读和维护。
  • 更加易于添加和删除参数。

  缺点是这样的:

  • 需要记住参数名称。
  • 属性名称无法被压缩。

  当函数创建DOM元素时,这种模式可能是非常有用的,例如,可以用在设置元素的CSS样式中,因为元素和样式可能具有大量的可选特征和属性。

  这篇文章的内容就到这里了,都是一些小的point,但是其实也很有必要去学习一下,下一章将会是函数部分的最后一章,我们会聊一聊“Curry化”。

《JavaScript 模式》读书笔记(4)— 函数4的更多相关文章

  1. JavaScript模式读书笔记 第4章 函数

    2014年11月10日 1.JavaScript函数具有两个特点: 函数是第一类对象    函数能够提供作用域         函数即对象,表现为:         -1,函数能够在执行时动态创建,也 ...

  2. JavaScript模式读书笔记 文章3章 文字和构造

    1.对象字面量     -1.Javascript中所创建的自己定义对象在任务时候都是可变的.能够从一个空对象開始,依据须要添加函数.对象字面量模式能够使我们在创建对象的时候向其加入函数.       ...

  3. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

  4. 《编写可维护的javascript》读书笔记(中)——编程实践

    上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...

  5. C语言深度解剖读书笔记(6.函数的核心)

    对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求 ...

  6. Javascript & JQuery读书笔记

    Hi All, 分享一下我学JS & JQuery的读书笔记: JS的3个不足:复杂的文档对象模型(DOM),不一致的浏览器的实现和便捷的开发,调试工具的缺乏. Jquery的选择器 a. 基 ...

  7. 《你不知道的javascript》读书笔记1

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. js的工作原理 引擎:从头到尾负责整个js的编译和运行.(很大一部 ...

  8. JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)

    this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...

  9. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  10. SQL反模式读书笔记思维导图

    在写SQL过程以及设计数据表的过程中,我们经常会走一些弯路,会做一些错误的设计.<SQL反模式>这本书针对这些经常容易出错的设计模式进行分析,解释了错误的理由.允许错误的场景,并给出更好的 ...

随机推荐

  1. 抛开贾跃亭!法拉第FF91能成功吗?

    在本届CES 2018上,FF 91又一次刷屏了,而且实实在在地允许试乘了. 抛开贾跃亭的因素不谈,你觉得FF 91能成功吗? 最开始知道法拉第FF91这款电动汽车的名字时,总感觉怪怪的--像是把法拉 ...

  2. Spring5源码分析(1)设计思想与结构

    1 源码地址(带有中文注解)git@github.com:yakax/spring-framework-5.0.2.RELEASE--.git Spring 的设计初衷其实就是为了简化我们的开发 基于 ...

  3. Redis-输入输出缓冲区

    一.client list id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后会重置为0addr:客户端连接的ip和端口fd:socket的文件描述符,与lsof命令结 ...

  4. 《软件自动化测试开发-Java和Python测试开发指南》第6次印刷

    2017年1月 第1次印刷 2017年5月 第2次印刷 2017年9月 第3次印刷 2017年11月 第4次印刷 2018年4月 第5次印刷 2018年6月 第6次印刷 欢迎留言,点赞前2名,可获2折 ...

  5. 数据大爆炸:KDD 2016

    2016"> 饕餮盛宴 ACM SIGKDD国际会议(简称KDD)是数据挖掘领域的顶级国际会议,由ACM (计算机协会)的数据挖掘及知识发现专委会(SIGKDD)负责组织筹办.在现今如 ...

  6. 再谈拍照,OPPO这次拿什么和iPhone7拼?

    ​一年一度的iPhone新机如期而至,双摄像头成为iPhone 7 Plus标配,尽管在这之前,双摄像头已有少数厂商在手机上装备,但苹果一出,市场必定全面跟进.无论各大厂商是否采用双摄像头,在手机拍照 ...

  7. Spring源码阅读笔记03:xml配置读取

    前面的文章介绍了IOC的概念,Spring提供的bean容器即是对这一思想的具体实现,在接下来的几篇文章会侧重于探究这一bean容器是如何实现的.在此之前,先用一段话概括一下bean容器的基本工作原理 ...

  8. USB小白学习之路(7) FPGA Communication with PC by CY7C68013,TD_init()解析

    注:这个TD_Init()只对EP6进行了配置,将其配置成为Bluk_In端口,而没有对EP2进行配置.这篇文章直接把寄存器的图片贴上来了,看起来比较杂.感兴趣的可以看下一篇文章,是转自CSDN,对E ...

  9. Java工作流引擎结合可视化表单开发,10分钟完成一个业务流程发布

    回忆以前工作流引擎的应用,感觉历历在目啊!当初公司接了一个项目关于政府单位公文流转的管理系统,一开始客户跟我画了十多张业务流程图.话说这十多张业务流程图,涉及的业务范围还蛮多,像用审批授权,开通流程, ...

  10. Spring源码阅读笔记04:默认xml标签解析

    上文我们主要学习了Spring是如何获取xml配置文件并且将其转换成Document,我们知道xml文件是由各种标签组成,Spring需要将其解析成对应的配置信息.之前提到过Spring中的标签包括默 ...