标签:JavaScript 设计模式


前言:最近在学习Node.js,了解了node中的事件驱动机制,而相应的事件循环实现和设计模式中观察者相似,于是打算将观察者模式顺带学习一番。

定义
通过查阅书籍,以及一些其他文档,观察者模式的定义可以简述为以下几个步骤:

  1. 目标状态发生观察者感兴趣的改变
  2. 目标对象发送一个通知消息(怎样发送,或是通过什么发送呢)
  3. 目标对象调用每个观察者的更新方法
  4. 最后,当观察者不再对目标状态的更新感兴趣,则观察者将自动分离

角色及作用

  • Subject (目标) 维护一系列观察者,方便添加或者删除观察者;
  • Observer(观察者)为那些目标状态发生改变时需获得通知的对象(具体观察者?)提供一个更新接口;
  • ConcreteSubject(具体目标) 状态发生改变时,向Observer发出通知,存储ConcreteObserver的状态;
  • ConcreteObserver(具体观察者)存储一个ConcreteObserver的引用,实现更新接口,以使自身状态和目标状态保持一致。

以下实现一个观察者模式的组件

首先,模拟一个目标可能拥有的一系列Observer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function ObserverList() {
this.observerlist = [];
}
ObserverList.prototype.Add = function (obj) {
this.observerlist.push(obj);
};
ObserverList.prototype.Empty = function () {
this.observerlist.length = 0;
// this.observerlist = [];
};
ObserverList.prototype.Count = function () {
return this.observerlist.length;
};
ObserverList.prototype.Get = function (index) {
if (index > -1 && index < this.observerlist.length) {
return this.observerlist[index];
}
};
// 从观察者头尾添加其他观察者
ObserverList.prototype.Insert = function (obj, index) {
let pointer = -1;
if (index === 0) {
this.observerlist.unshift(obj);
pointer = index;
} else if (index === this.observerlist.length) {
this.observerlist.push(obj);
pointer = index;
}
return pointer;
};
ObserverList.prototype.IndexOf = function (obj, startIndex) {
let pointer = -1, i = startIndex;
while(i < this.observerlist.length) {
if (this.observerlist[i] === obj) {
pointer = i;
}
i++;
}
return pointer;
};
// 从列表首尾删除观察者
ObserverList.prototype.RemoveIndexAt = function (index) {
if (index === 0) {
this.observerlist.shift();
} else if (index === this.observerlist.length - 1) {
this.observerlist.pop();
}
};
function extend(obj, extention) {
for (let key in obj) {
extention[key] = obj[key];
}
};

接下来,来模拟目标(Subject)和在观察者列表上添加。删除或通知观察者的能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.AddObserver = function (observer) {
this.observers.Add(observer);
};
Subject.prototype.RemoveObserver = function (observer) {
this.observers.RemoveIndexAt(this.observers.IndexOf(observer, 0));
};
Subject.prototype.Notify = function (context) {
let observerCount = this.observers.Count();
for (let i = 0; i < observerCount; i++) {
// 这里的Update方法将在Observer实例中实现,一般会重写自定义更新行为;
this.observers.Get(i).Update(context);
}
};

然后定义一个框架来创建新的Observer

1
2
3
4
5
6
7
//模拟一个观察者对象
function Observer() {
//之后的实例中会重写自定义的Update方法
this.Update = () => {
//...
}
}

上面实现了一个观察者模式,接下来通过一个实例程序,学习怎样使用观察者模式:

  1. 用于向页面添加新可见的CheckBox按钮;
  2. 控制CheckBox,将充当一个目标,通知其他CheckBox需要进行检查;
  3. 用于添加新CheckBox的容器

实现代码如下:

1
2
3
<button id="addNewObserver">Add New Observer checkbox</button>
<input type="checkbox" id="mainCheckbox">
<div id="observerContainer"></div>

下面是样例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 样例脚本
var controlCheckbox = document.getElementById('mainCheckbox'),
addBtn = document.getElementById('addNewObserver'),
container = document.getElementById('observerContainer');
// 目标对象,用于控制所有的观察者 具体目标ConcreteSubject
extend(new Subject(), controlCheckbox);
// 点击CheckBox会触发通知到观察者上
controlCheckbox["onclick"] = new Function("controlCheckbox.Notify(controlCheckbox.checked)");
// 添加新的观察者
addBtn["onclick"] = AddNewObserver;
// 具体观察者ConcreteObserver
function AddNewObserver() {
var check = document.createElement("input");
check.type = "checkbox";
extend(new Observer(), check);
check.Update = function (value) {
this.checked = value;
};
controlCheckbox.AddObserver(check);
container.appendChild(check);
}

以上就是观察者模式的简单实现和使用,主要弄清楚目标,观察者,具体目标,具体观察者这四者之间的关系,以及弄清它们之间的调用的关系。总体说来还是比较容易理解。

主要参考:JavaScript设计模式 [美] Addy Osnabi 著

标签:前端工程师 Interview

前言:应该说是从2017-3月份就开始了自己的求职之旅,一路跌跌撞撞,前前后后面了不少公司,是时候总结归纳一番了。
主要面试集中在我从随手科技实习过后的那两个月里,10多家的面试,面出了经验。总体说来,机会是留给有准备的人的,准备好了,机遇便随处可见。

今日头条一面

  1. CSS文字省略的样式写法;
  2. 写一个匹配电话号码的正则表达式;
  3. 说说自己对闭包的理解,并完成相应题目的回答;
  4. 使用Ajax和JSONP进行数据交互的区别;
  5. script标签中defer和async的理解,说明添加这两个属性之后文件加载和执行的顺序;
  6. git 指令的相关操作;
  7. 对箭头函数的理解,尤其考察this;
  8. localStorage和sessionStorage、cookies的区别;
  9. 如何判断一个引用类型;
  10. 手写快速排序算法,重点考察代码风格和实现思路

由于手写快排环节错了纰漏,因此完美错失此次机会。。。

挖财一面

  1. position的取值,以及区别;
  2. 关于transform属性的考察,哪些属性可以过渡,哪些属性不可以;
  3. 伪类和伪元素是什么,区别在于;
  4. 怎样获取页面的根元素,怎样获取页面中所有的标签元素;
  5. children()和childNode()的区别;
  6. 去重的方法;
  7. 怎样实现Object.assign()方法;
  8. 对象的复制方式(JSON.parse(JSON.stringify()),这种方法的缺点是什么);
  9. JSON数据格式的一些限制;
  10. Promise封装数据请求的方法;
  11. Commonjs、CMD、AMD模块规范的理解和使用;
  12. 对webpack的了解和使用,怎样的一个实现过程;
  13. 前端安全XSS、CRSF的了解;
  14. JSONP的原理,回调函数的作用域在哪;
  15. 平时如何学习前端的;
  16. 自己做的项目有何亮点;
  17. 如何绘制一个等边三角形。

…问了超多问题,虽然有几个地方大不太好,但。。。没有然后了

滴滴出行一面(杭州)

  1. 实现一个千分位的函数,其他问题和上面的差不多
  2. 地图上面要渲染10万数据,如何做优化;

滴滴出行二面

  1. 数组的深复制;
  2. HTTPS和HTTP的通信过程,有何不同,主要说明HTTPS
  3. 几种模块化规范的区别;
  4. Vue.js中data和props的区别;
  5. 重绘和重排;
  6. GPU加速,什么情况下会触发这个东西;
  7. 对JSON对象的理解;
  8. TCP的三次握手和四次挥手过程;
  9. margin值重叠的情况

滴滴出行三面

  1. 说一下React.js、Vue.js、angular.js三者的区别,为何会选用Vue.js,它们分别适用何种场景;
  2. 移动端的页面为何适用H5开发,使用原生开发不好吗,它们的区别是什么;
  3. 自己的优势在哪;
  4. map数据结构是怎么回事;

。。。当时接到了滴滴杭州和北京的电话,滴滴出行北京面了四面,之后也没有音讯,恍恍惚惚。。。

百度一面(上海)

  1. Ajax出现的背景,为何会出现,解决了何种问题;
  2. get和post方法的区别;
  3. 自己实习所接触的项目,项目的功能模板,是一个什么样的东西;
  4. 计算机网络的七层协议(OSI标准模型);
  5. 查找单链表中的第K个元素;
    。。。

刚开始还能聊,之后一下没有底气了,气氛略显尴尬。。。

百度糯米(一面)

  1. HTTP状态码,403表示什么;
  2. cookie是在哪里设置的;
  3. JSONP中返回的东西是什么,怎样返回的;
  4. 使用CDN的原因什么
    。。。

百度糯米(二面)

  1. JSONP的原理;
  2. “use strict” 的限制是什么;
  3. JQ事件委托是怎么一回事;
  4. flex布局;
  5. 移动端适配 rem;
    。。。

百度糯米(三面)
问了一些稀奇古怪的问题,由于自己没有太放在心上,之后也没有收到消息了。。。

追一科技(深圳)

  1. HTML5、CSS3的一些新特性,CSS3动画;
  2. ES6的一些新特性,Promise的转变;
  3. 数组去重的方法;
  4. 对模块化、工程化的理解;
  5. sessionStorage和localStorage的区别;
  6. Sass使用一些特性;
  7. 实习做过什么东西;
  8. Vue.js相关(父子组件通信,兄弟组件通信,生命周期函数,路由钩子,DOM操作相关,底层原理等);
  9. typeof的取值有哪些;
  10. 怎样判断一个数组;
  11. 前端安全XSS和CRSF
  12. 对框架的关注;
  13. 是否了解过Node.js;

追一科技二面

  1. 扫描二维码,登录网页版微信的过程;
  2. 说一下Generator;
  3. 认为JS中难点,不易理解的地方有哪些;
  4. 常见的排序算法的时间复杂度,由低到高的排列;
  5. 对数据库的了解;

…面了两面吧…

水滴互助(北京)

  1. 简单说明自己的情况;
  2. 实习期做的一些事情,公司主站有多大,使用的什么技术;
  3. Vue-router路由转换,怎样做页面的跳转;
  4. fetch.js的实现,async,await同时发送多个请求;
  5. 组件之间相互通信;
  6. webpack的配置,比如文件放在哪里,如何匹配文件;
  7. Scss-loader和css-loader的区别和相应的作用;
  8. call、apply、bind三者的区别;
  9. ES6的新特性;
  10. export 和 export default的区别;
  11. ES6中新的字面量,什么是对象字面量;
  12. 解构赋值是怎么使用的;

之后说去北京进行现场面,没有去。。。

石墨文档一面

  1. 说一下自己的前端经验和项目;
  2. Vuex状态管理、mutation和action的区别;
  3. Vue-router的路径管理,怎样返回404页面;
  4. react和vue.js的区别,相应的模式是什么;
  5. 服务端页面渲染;
  6. HTML5的一些API;
  7. CSS3的一些新东西;
  8. position 的取值,sticky是什么;
  9. 设置元素垂直居中的方式;

在线编程了,当初特别想去的一家,虐惨。。。

英语流利说一面

  1. W3C标准盒模型;
  2. 垂直居中的方法;
  3. 对象的继承;
  4. TCP和UDP的区别;
  5. 订阅者/发布者实现方式;
  6. Object.keys()方法的返回值是什么;
  7. 时间捕获和事件冒泡;
  8. ES6中的箭头函数;

面试过了,之后发的笔试题没有用react写,后悔了。。。

还有几家其他,问的问题都大同小异,通过面试确实巩固了自己的基础知识,对自我有了更加深刻的认识。路漫漫其修远兮,吾将上下而求索。
加油吧,随时准备清零!

2018.2.10 晚

标签:计算机网络 TCP协议 读书笔记

###运输层连接的管理
TCP是面向连接的协议。此连接是一种逻辑连接,连接的端点称为套接字(IP地址:端口号)。运输连接是用来传送TCP报文的。它包括三个阶段:连接建立、数据传送、连接释放。

TCP连接建立过程中需要解决的三个问题:

1. 要使每一方都确知对方的存在;
2. 要允许双方协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等);
3. 能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。

TCP 连接的建立采用客户服务端的方式。

  • 先弄清一些TCP首部报文段格式的概念
    标志位
    1)确认ACK (ACKnowledgement) 仅当 ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置为1。
    2)同步SYN(SYNchronization)在连接建立时用来同步序号。当 SYN = 1,ACK = 0 时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN = 1, ACK = 1。因此,SYN置为1就表示这是一个连接请求或接受请求报文。
    3)终止FIN(FINish)用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放连接。
    序号
    1)Seq序号 占32位,用来标识发送方向接收方发送的报文段中第一个字节的序号,发起方发送数据时对此进行标记。
    确认号
    1)Ack确认号 占32位,是期望收到对方下一个报文段的第一个数据字节的序号。只有当ACK=1时,确认号字段才有效,Ack=Seq+1
    ###TCP的连接建立(三次握手)
    在连接之初,客户端和服务端都处于CLOSED状态,客户端主动发送连接建立请求,服务端通过监听请求,连接被动打开。如下图,展示了连接建立的详细过程:

说明:
将客户端设为A方,服务器端设为B方。A主动打开连接,B被动打开连接。
一开始,B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。此时服务器进程处于LISTEN状态,等待客户的连接请求,如有,即作出响应。接下里就开始开始握手了

第一次握手:A的TCP客户进程也先创建传输控制块TCB,然后像B发出连接请求报文段,此时首部中的同步位SYN=1,并选择一个初始序号Seq=X。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。

第二次握手:B收到连接请求报文后,若同意建立连接,则向A发送确认。在确认报文段中,应该设置SYN=1 ACK=1,确认号Ack=X+1,同时也为自己选择一个初始序号为Seq=Y。这时TCP服务器进程进入SYN-RCVD(同步状态)。

第三次握手:TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号Ack=Y+1,而自己的序号Seq=X+1。这时,连接已经建立,A进入ESTABLISHED(已建立连接)状态。B收到A的确认后,也进入ESTABLISHED状态。

思考:为什么A还要发送一次确认呢?
这主要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。

###TCP的连接释放(四次挥手)
数据传输结束后,通信的双方均可释放连接。大致过程如下图所示:

说明:
现在A和B都处于ESTABLISED状态。A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。

第一次挥手:A把连接释放报文段首部的终止控制位FIN置1,其序号为Seq=u,它等于前面已传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。

第二次挥手:B收到连接释放报文段后即发出确认,确认号Ack=u+1,而这个报文段自己的序号为v,它等于已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSED-WAIT(关闭等待)状态。A收到来自B的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放报文段。

注意:TCP服务器进程这时应通知高层应用程序,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭状态,即A已经没有数据要发送了,但B若发送数据,A仍要接受。也就是说,从B到A这个方向的连接并没有关闭,这个状态可能会持续一些时间。

第三次挥手:若B已经没有要向A发送数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1.现假定B的序列号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号Ack=u+1。这时B就进入LAST-ACK(最后确认)状态,等待A的确认。

第四次挥手:A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置为1,确认号Ack=w+1,而自己的序号为Seq=u+1,然后进入TIME-WAIT(时间等待)状态。现在TCP连接还没有释放掉,必须经过时间等待计时器设置的时间2MSL后,A才进入到CLOSED状态。B只要收到A发出的确认后,就进入CLOSED状态。A、B在撤销了相应的传输控制块TCB后,就释放了这次的TCP连接。

思考:为什么A在TIME-WAIT状态必须等待2MSL的时间呢?
1)为了保证A发送的最后一个ACK报文段能够到达B。
2)防止已失效的连接请求报文段又传到了B,因而产生错误。

主要参考:
http://blog.csdn.net/sssnmnmjmf/article/details/68486261
计算机网络(第6版)谢希仁 著

the good parts

第一章

1.分析javascript

函数/弱类型/动态对象/对象字面量
函数基于词法作用域(权威指南8.8.1)

第二章

2.基本语法知识

总:空白,标识符,数字,字符串(16位),语句(每个script标签提供一个编译且立即执行的编译单元)变量应该被定义在函数的头部。表达式,字面量(属性名可以是字符串和标识符,对象,数组,函数,正则表达式 )
注意:

  • 语句:视为假的值有:false,null,undefined,空字符串,NaN,数值0;其他所有的值都被当作真;for in语句,try-catch语句,throw 语句
  • typeof取值:number,string,boolean,object,function,undefined。
  • 运算符的结合性:一元/赋值/三元条件运算符结合性从右到左,(权威指南5.2.4运算符的结合性);“%”求余和求模的区别

第三章 对象

3.对象

  • 对象字面量:名/值对,检索(从undefined的成员属性中取值会导致TypeError错误,此时可以利用&& 来避免错误);更新:扩充和替换;引用:对象通过引用来传递,它们永远不会被复制
  • 原型和委托(随着原型一层一层的寻找需要的属性)
  • 反射(访问对象属性的值):hasOwnProperty()返回布尔值,不会检查原型链
  • 枚举:常用的过滤器有hasOwnProperty(),typeof用来排除函数。
  • 删除 delete
  • 减少全局变量污染:最小化使用全局变量(只创建一个唯一的全局变量)

第四章 函数

4.函数

  • 所谓编程就是将一组需求分解成一组函数与数据结构的技能。
  • 函数对象:每个函数在创建时会附加两个隐藏的属性:函数的上下文和实现函数行为的代码
  • 通过函数字面量创建的函数对象包含一个连到外部上下文的链接,这被称为闭包。
  • 调用: 函数在调用时,会接收两个附加的参数:this和arguments,this 的值取决于调用模式(方法调用模式,函数调用模式,构造器调用模式,apply调用模式)
    四种调用方式:
  • 方法调用模式:当一个函数被保存为对象的一个属性,称它为方法,当此方法被调用时,this被绑到该对象
  • 函数调用模式:不是对像的属性时,this被绑定到全局对象。解决方法:将this赋给that 变量
  • 构造器调用模式:如果一个函数前面带上new,背地里创建一个链接到该函数prototype成员的新对象,同时this会被绑定到该对象上。
  • Apply 调用模式 :apply()接受的两个参数:1.绑定到this上的值,2.参数数组;

  • 其他:参数,返回,异常,扩充类型的功能(如给Number类型扩充integer方法等),递归(函数自身调用自身),作用域(作用:减少名称冲突,提供自动内存管理,因为没有块级作用域,最好在函数体的顶部声明函数中可能用到的所有变量),闭包(私有变量,包在一个函数体里)

  • 闭包 注意点:避免在循环中创建函数。
  • 回调的概念:找相关例子。
  • 模块:使用函数和闭包来构造模块,是一个提供接口却隐藏状态与实现的函数或对像。模块模式的一般形式:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或将其保存到一个可以访问的地方。什么是单例模式(对象字面量创建的对象)?
  • 级联:一些方法没有返回值,如果让这些方法返回this而不是undefined,就可以启用级联。可产生出极富表现力的接口。
  • 柯里化
  • 记忆:将先前操作的结果记录在某个对象中,(数组或对象)

第五章 继承

5.继承(继承模式:没法保护隐私)

  • 伪类
  • 对象说明符:可以将json对象传递给一个构造器,将返回一个构造完全的对象。
  • 原型:差异化继承是什么。
  • 函数化:应用模块模式保护函数隐私。更好的封装和信息隐藏。
  • 部件:把对象组装起来。

第六章 数组

6.数组

  • length:(参考权威指南7.2 数组元素的读和写),【】将其中的表达式转换成一个字符串;设置更大的length值不会给数组分配更多的空间,设小则会删除一些元素。
  • 删除数组中的一个元素 :delete与splice的区别。
  • 数组的一些方法

Javascript忍者秘籍

第五章:闭包(详解)

1.闭包如何工作:例如一个简单的闭包

1
2
3
4
5
6
7
8
9
10
<script>
var outerValue = "ninja";
function outerFunction() {
console.log(outerValue);
console.log("I can see the ninja.");
}
outerFunction();
</script>

不那么简单的闭包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var outerValue = "ninja";
var later;
function outerFunction() {
var innerValue = "some";
function innerFunction() {
console.log(outerValue);
console.log(innerValue);
}
later = innerFunction;
}
outerFunction();//此函数执行后,它的作用域就不存在了
later();

关于上面的解释:在外部函数中声明innerFunction()的时候,不仅声明了函数,还创建了一个闭包(不光包含函数声明,还包含了函数声明的那一时刻点上该作用域中的所有变量),像一个安全气泡一样,函数获得了执行操作的所有东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//上述例子的加强版,闭包可以访问到的一些内容(核心原则)
var outerValue = "ninja";
var later;
function outerFunction() {
var innerValue = "some";
function innerFunction(paramValue) {
console.log(outerValue);
console.log(innerValue);
console.log(paramValue);//???
console.log(toolLate); //???
}
later = innerFunction;
}
console.log(toolLate); //???能还是不能
var toolLate = "ronin";
outerFunction();
later('walker');
关于闭包的三个有趣的概念
* a.内部函数的参数是包含在闭包中的;
* b.作用域之外的所有变量,即便是函数声明之后的那些声明,也都包含在闭包中。
* c.相同的作用域内,尚未声明的变量不能提前调用。???

2. 使用闭包

  • 1) 使用闭包模拟私有变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Ninja() {
    var feints = 0;
    this.getFeints = function() {
    return feints;
    };
    this.feint = function() {
    feints++;
    };
    }
    var ninja = new Ninja();
    ninja.feint();
    console.log(ninja.getFeints());
    console.log(ninja.feints);
  • 2) 在计时器间隔回调中使用闭包(示例sublime)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//闭包可以用来跟踪动画的步骤
<script>
//计时器
function animated(elementId) {
var elem = document.getElementById(elementId);
var tick = 0;
var timer = setInterval(function(){
if(tick < 100) {
elem.style.left = elem.style.top = tick + "px";
tick++;
} else {
clearInterval(timer);
console.log(tick);
console.log(elem);
console.log(timer);
}
}, 10);
}
animated('box');
//此示例说明函数在闭包里执行的时候,不仅可以在闭包创建的时刻上看到这些变量的值,还可以对其进行更新。
</script>
  • 3) 给函数绑定一个特定的上下文(见sublime)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>...</title>
<script>
function bind(context, name) {
return function (){
return context[name].apply(context, arguments);
};
}
var button = {
clicked: false,
click: function(){
this.clicked = true;
console.log(button.clicked);
}
};
var elem = document.getElementById("test");
elem.addEventListener("click", bind(button,"click"), false);
</script>
</head>
<body>
<button id="test">Click Me!</button>
</body>
</html>
  • 4)函数重载(主要有以下作用)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    //说明:缓存记忆是一个让函数具备一种可以记忆它历史被调用时所产生的运算结果的能力的过程。
    a.使用闭包实现的缓存记忆功能(在函数调用时,自动进行缓存记忆)
    Function.prototype.memoized = function(key){
    this._values = this._values || {}; //保存一个数据存储对象
    return this._values[key] !== undefined ?
    this._values[key] :
    this._values[key] = this.apply(this, arguments);
    };
    Function.prototype.memoize = function() {
    var fn = this; //通过变量赋值将上下文带到闭包中。否则,???
    return function(){
    //在缓存记忆函数中封装原始的函数
    return fn.memoized.apply(fn, arguments);
    };
    };
    var isPrime = (function(num) { //使用素数函数计算作为测试
    var prime = num !=1;
    for (var i = 2; i < num; i++) {
    if (num % i == 0) {
    prime = false;
    break;
    }
    }
    return prime;
    }).memoize();
    console.log(isPrime(17));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//说明:函数包装(封装函数逻辑)价值在于:在重载一些已经存在的函数时,同时保持原始函数在被包装后仍然能够有效使用。
b.使用新功能包装旧函数(难度指数: *****)
function wrap(object, method, wrapper) { //wrapper表示要代替原有的方法执行的方法
var fn = object[method]; //记住原有函数,以便稍后可以使其通过闭包进行引用
return object[method] = function() {
return wrapper.apply(this, [fn.bind(this)].concat(
Array.prototype.slice.call(arguments)));
};
if (Prototype.Brower.Opera) {
wrap(Element.Methods, "readAttribute",
function(original, elem, attr) {
return attr == "title" ?
elem.title :
original(elem, attr);
});
}
}
  • 5) 及时函数:通过参数限制作用域内的名称;使用及时函数将短名称引用到一个有限的作用域内;利用即时函数妥善处理迭代问题(循环问题错误: 闭包记住的是变量的引用,而不是闭包创建时刻该变量的值。);类库包装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    在封闭的作用域内,强制使用一个名称
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>...</title>
    </head>
    <body>
    <img src="../../task/" alt="">
    <script>
    $ = function(){ alert('not jQuery!');};
    (function($){
    $('img').on('click', function(event){
    $(event.target).addClass('clickedOn');
    })
    })(jQuery);//'$'会成为函数体内所创建内部函数的闭包的一部分
    //so,即便事件程序在即时函数执行并消失以后很长一段时间才执行,该处理函数还是可以将‘$’引用到JQuery的。
    </script>
    </body>
    </html>

总结:

每个通过闭包进行信息访问的函数都有一个“锁链”,如果我们愿意可以在它上面加任何信息。但使用闭包时,闭包里的信息会一直保存在内存里,直到这些信息确保不再使用(可以安全进行垃圾回收),或页面卸载时,js引擎才能清理这些信息。灵活和方便,用于封装,but 内存浪费,内存泄漏,性能消耗。变量的作用域依赖于变量所在的闭包