├── doc
├── design mode
│ ├── 中介者模式.md
│ ├── 备忘录模式.md
│ ├── 解释器模式.md
│ ├── 迭代器模式.md
│ ├── 策略模式.md
│ ├── 外观模式.md
│ ├── 装饰着模式.md
│ ├── 命令模式.md
│ ├── 代理模式.md
│ ├── 访问者模式.md
│ ├── 状态模式.md
│ ├── 单例模式.md
│ ├── 桥接模式.md
│ ├── 观察者模式.md
│ ├── 简单工厂设计模式.md
│ ├── 工厂方法模式.md
│ ├── 建造者模式.md
│ ├── 模板方法模式.md
│ ├── 原型模式.md
│ ├── 职责链模式.md
│ ├── 适配器模式.md
│ ├── 抽象工厂模式.md
│ ├── 组合模式.md
│ ├── 享元模式.md
│ └── 面向对象.md
├── es6
│ └── let、const命令.md
├── dataStructure
│ ├── stack.md
│ ├── queue.md
│ ├── Set.md
│ ├── test.js
│ ├── array.md
│ ├── tree.md
│ ├── Map&HashMap.md
│ └── linkedList.md
└── basic_js
│ ├── JavaScript中的执行上下文和变量对象.md
│ ├── 夯实JS系列--变量、作用域和内存问题.md
│ ├── 原型和原型链.md
│ ├── 谈谈闭包.md
│ ├── 编写高质量代码基本要点.md
│ ├── 彻底明白this指向.md
│ ├── 忍者级别的操作函数.md
│ ├── prototype-based.md
│ └── JavaScript中的跨域总结.md
├── img
├── 171030.png
├── pay
│ ├── pay.png
│ └── wx.jpg
├── 171105_01.png
├── 17120401.png
├── 20151118102648527.jpg
└── QQ20171204-180745@2x.png
├── .idea
├── vcs.xml
├── modules.xml
└── YOU-SHOULD-KNOW-JS.iml
└── README.md
/doc/design mode/中介者模式.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/design mode/备忘录模式.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/design mode/解释器模式.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/design mode/迭代器模式.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/es6/let、const命令.md:
--------------------------------------------------------------------------------
1 | # let、const命令
2 |
3 | ## let命令
4 |
--------------------------------------------------------------------------------
/img/171030.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/171030.png
--------------------------------------------------------------------------------
/img/pay/pay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/pay/pay.png
--------------------------------------------------------------------------------
/img/pay/wx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/pay/wx.jpg
--------------------------------------------------------------------------------
/img/171105_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/171105_01.png
--------------------------------------------------------------------------------
/img/17120401.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/17120401.png
--------------------------------------------------------------------------------
/img/20151118102648527.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/20151118102648527.jpg
--------------------------------------------------------------------------------
/img/QQ20171204-180745@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/YOU-SHOULD-KNOW-JS/HEAD/img/QQ20171204-180745@2x.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/doc/design mode/策略模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之策略模式
2 |
3 | ### 概念
4 | 策略模式:将定义的一组算法封装起来,使其相互之间可以替代。封装的算法具有一定的独立性,不会随着客户端变化而变化
5 |
6 | 从结构上看,他和状态模式非常的相似,也是在内部封装一个对象,然后通过返回的借口对象实现对内部对象的调用,不同的是,策略模式不需要管理状态,状态之间没有依赖关系,策略之间可以相互替换,在策略对象内部保存的是一些相对独立的一些算法。
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/YOU-SHOULD-KNOW-JS.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/doc/design mode/外观模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之外观模式
2 |
3 | ### 概念
4 | 外观模式:为一组复杂子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统访问更加的容易。
5 |
6 | ### 代码演示
7 |
8 | ```javascript 1.6
9 | // 使用外观模式注册事件监听
10 | function addEvent(dom,type,fn) {
11 | if(dom.addEventListener){
12 | dom.addEventListener(type,fn,false);
13 | }else if(dom.attachEvent){
14 | dom.attachEvent('on'+type,fn);
15 | }else{
16 | dom['on'+type] = fn;
17 | }
18 | }
19 | // 使用外观模式获取事件对象
20 |
21 | var getEvent = function(event) {
22 | return event || window.event;
23 | }
24 | ```
25 |
26 | 通过对接口的二次封装,使其简单易用,隐藏起内部的复杂度,外观模式就是对接口的外层包装,以供上层代码调用。因此外观模式封装的接口方法不需要接口的具体实现,只需要按照接口的使用规则使用即可。
--------------------------------------------------------------------------------
/doc/design mode/装饰着模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之装饰着模式
2 |
3 | ### 概念
4 |
5 | 装饰着模式,在不改变源对象的基础上,通过对其进行包装拓展使原有对象可以满足用户的更复杂需求
6 |
7 | ### 代码演示
8 |
9 | 这里我拿给输入框添加事件举例
10 |
11 | ```javascript 1.6
12 | var decorator = function(input ,fn) {
13 | //获取时间源
14 | var input = document.getElementById(input);
15 | if(typeof input.onclick === 'function'){
16 | //缓存事件源原有的回调函数
17 | var oldClickFn = input.onclick;
18 | input.onclick = function (ev) {
19 | oldClickFn();
20 | fn();
21 | }
22 | }else{
23 | input.onclick = fn;
24 | }
25 | }
26 | ```
27 |
28 | 装饰着模式很简单,就是对原有对象的属性和方法的添加。相比于之前说的适配器模式是对原有对象的适配,添加的方法和原有的方法功能上大致相似。但是装饰着提供的方法和原有方法功能项则有一定的区别,且不需要去了解原有对象的功能。只要原封不动的去使用就行。不需要知道具体的实现细节。
--------------------------------------------------------------------------------
/doc/design mode/命令模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之命令模式
2 |
3 | ### 概念
4 |
5 | 命令模式:用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行。也就是说该模式旨在将函数的调用、请求和操作封装成一个单一的对象,然后对这个对象进行一些列的处理。他也可以用来消除调用操作的对象和实现操作的对象之间的耦合。这为各种具体的类的更换带来了极大的灵活性。
6 |
7 | ### 代码演示
8 |
9 | ```javascript 1.6
10 |
11 | //1.一个连有炮兵和步兵,司令可以下命令调动军队打仗
12 | var lian = {};
13 | lian.paobing = function(pao_num){
14 | console.log(pao_num+"门炮准备战斗");
15 | }
16 | lian.bubing = function(bubing_num){
17 | console.log(bubing_num+"人准备战斗");
18 | }
19 | lian.lianzhang = function(mingling){
20 | lian[mingling.type](mingling.num);
21 | }
22 |
23 | //司令下命令
24 | lian.lianzhang({
25 | type:"paobing",
26 | num:10
27 | });
28 | lian.lianzhang({
29 | type:"bubing",
30 | num:100
31 | });
32 |
33 | ```
--------------------------------------------------------------------------------
/doc/design mode/代理模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之代理模式
2 |
3 | ### 概念
4 | 代理模式:由于一个对象不能直接引用另一个对象,所以需要代理对象在这两个对象之间起到中介的作用
5 |
6 | ### 代码演示
7 | ```javascript 1.6
8 | // 先声明美女对象
9 | var girl = function (name) {
10 | this.name = name;
11 | };
12 |
13 | // 这是dudu
14 | var dudu = function (girl) {
15 | this.girl = girl;
16 | this.sendGift = function (gift) {
17 | alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
18 | }
19 | };
20 |
21 | // 大叔是代理
22 | var proxyTom = function (girl) {
23 | this.girl = girl;
24 | this.sendGift = function (gift) {
25 | (new dudu(girl)).sendGift(gift); // 替dudu送花咯
26 | }
27 | };
28 |
29 | var proxy = new proxyTom(new girl("酸奶小妹"));
30 | proxy.sendGift("999朵玫瑰");
31 | ```
32 | 假如dudu要送酸奶小妹玫瑰花,却不知道她的联系方式或者不好意思,想委托大叔去送这些玫瑰,那大叔就是个代理
33 |
34 | 其实在日常开发中,我们遇到很多这种情况,比如跨域,之前总结过跨域的所有东西,其中的jsonp,window.name还是location.hash都是通过代理模式来实现的。
35 |
36 | 代理模式具体的从我的另一篇文章,JavaScript中的跨域总结去体会哈
37 |
--------------------------------------------------------------------------------
/doc/design mode/访问者模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之访问者模式
2 |
3 | ### 概念
4 | 访问者模式:针对于对象结构中的元素,定义在不改变对象的前提下访问结构中元素的方法
5 |
6 | 在访问者模式中,主要包括下面几个角色
7 |
8 | 1、抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
9 |
10 | 2、访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
11 |
12 | 3、抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
13 |
14 | 4、元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
15 |
16 | 5、结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
17 |
18 |
19 | ### 代码演示
20 |
21 | ```javascript 1.6
22 | // 访问者
23 | function Visitor() {
24 | this.visit = function( concreteElement ) {
25 | concreteElement.doSomething();
26 | }
27 | }
28 | // 元素类
29 | function ConceteElement() {
30 | this.doSomething = function() {
31 | console.log("这是一个具体元素");
32 | }
33 | this.accept = function( visitor ) {
34 | visitor.visit(this);
35 | }
36 | }
37 | // Client
38 | var ele = new ConceteElement();
39 | var v = new Visitor();
40 | ele.accept( v );
41 | ```
--------------------------------------------------------------------------------
/doc/design mode/状态模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之状态模式
2 |
3 | ### 概念
4 | 状态模式:当一个对象内部状态发生变化的时候,会导致起行为变化,看起来就像改变了对象
5 |
6 | 状态模式定义了一个对象,这个对象可以通过管理其内部状态从而是其行为发生变化。状态模式是一个非常出色的设计模式,主要由两个角色构成
7 |
8 | - 环境类:拥有一个状态成员,可以修改其状态并做出反应
9 | - 状态类:表示一种状态,包含相应的处理方法
10 |
11 | ### 代码演示
12 | 对于一个简单的例子,我们可以将不同的判断结构封装在一个状态对象内,然后该状态对象返回一个可被调用的状态方法,用于调用对象内部某个方法。
13 |
14 | ```javascript 1.6
15 | var ResultState = function() {
16 | var States = {
17 | state0:function() {
18 | console.log('这是第一种结果');
19 | },
20 | state1:function() {
21 | console.log('这是第二种结果');
22 | },
23 | state2:function() {
24 | console.log('这是第三种结果');
25 | },
26 | state3:function() {
27 | console.log('这是第四种结果');
28 | }
29 | };
30 | //获取某一种状态并执行相应的方法
31 | function show(result) {
32 | States['state'+result]&&States['state'+result]();
33 | }
34 |
35 | return {
36 | show:show
37 | }
38 | }()
39 |
40 | ResultState.show(4);
41 | ```
42 |
43 | 上面代码只是一个雏形,对于状态模式主要目的就是将条件判断的不同结构,转化为状态对象的内部状态,既然是状态对象的内部状态,所以一般是作为状态对象的私有变量,然后提供一个能够调用状态对象内部状态的接口方法对象
44 |
45 |
--------------------------------------------------------------------------------
/doc/design mode/单例模式.md:
--------------------------------------------------------------------------------
1 | # JavaScript设计模式之单例模式
2 |
3 | > 学习一个东西,一定要明白这个东西的概念是什么,这个东西被提出来的目的是什么
4 |
5 | ## 概念
6 | 单例模式又称为单体模式,其实就是只允许实例化一个对象,有时我们也可以用一个对象类规划命名空间,仅仅有条的管理对象的属性和方法.
7 |
8 |
9 | 单例模式比较常规常见,比较简单,直接看代码
10 |
11 | ## 代码演示
12 | ```javascript 1.6
13 | //命名空间管理
14 | var Neal = {
15 | g:function(id) {
16 | return document.getElementById(id)
17 | },
18 | css:function(id,key,value) {
19 | this.g(id).style[key] = value;
20 | },
21 | //...
22 | }
23 | //模块分明
24 | var A = {
25 | Util:{
26 | util_method1:function() {
27 |
28 | },
29 | util_method2:function() {
30 |
31 | }
32 | },
33 | Tool:{
34 | tool_method1:function(){},
35 | tool_method2:function(){},
36 | }
37 | //...
38 | }
39 | //惰性单例
40 | var LazySingle = (function() {
41 | //单例实例引用
42 | var _instance = null;
43 | //单例
44 | function Single() {
45 | return{
46 | publicMethod:function() {
47 |
48 | },
49 | publicProperty:'1.0'
50 | }
51 | }
52 | //获取单例接口
53 | return function() {
54 | if(!_instance){
55 | _instance = Single();
56 | }
57 | //返回单例
58 | return _instance;
59 | }
60 | })()
61 | ```
--------------------------------------------------------------------------------
/doc/dataStructure/stack.md:
--------------------------------------------------------------------------------
1 | # JavaScript数据结构与算法--栈
2 |
3 | ## 定义
4 | 栈是一种遵从先进后出LIFO原则的有序集合。新添加的或待删除的元素都保存在栈的 末尾,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
5 |
6 | 而栈主要是在编程语言的编译器里用来保存变量和方法调用等。
7 |
8 | ## 创建栈
9 | ```javascript
10 | function Stack() {
11 | var _item = [];
12 | this.push = function(element) {
13 | this._item.push(element)
14 | };
15 | this.pop = function() {
16 | return _item.pop();
17 | };
18 | this.peek = function() {
19 | return _item[_item.length-1]
20 | };
21 | this.isEmpty = function() {
22 | return _item.length === 0;
23 | };
24 | this.size = function() {
25 | return _item.length;
26 | };
27 | this.clear = function() {
28 | _item = [];
29 | };
30 | this.print = function() {
31 | console.log(_item.toString())
32 | }
33 | }
34 | ```
35 |
36 | ### 实际应用场景
37 |
38 | 进制转换
39 |
40 | ```javascript
41 | function baseConverter(decNumber,base) {
42 | var remStack = new Stack(),
43 | rem,
44 | baseString = '',
45 | digits = '0123456789ABCDEF';
46 |
47 | while (decNumber>0){
48 | rem = Math.floor(decNumber % base);
49 | remStack.push(rem);
50 | decNumber = Math.floor(decNumber/base);
51 | }
52 | while(!remStack.isEmpty()){
53 | baseString+=digits[remStack.pop()];
54 | }
55 | return baseString;
56 | }
57 | ```
--------------------------------------------------------------------------------
/doc/design mode/桥接模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之桥接模式
2 |
3 | ### 概念
4 | 桥接模式:在系统沿着多个维度变化的时候,不增加起复杂度已达到解耦的目的
5 |
6 | ### 应用场景
7 |
8 | 在我们日常开发中,需要对相同的逻辑做抽象的处理。桥接模式就是为了解决这类的需求。
9 |
10 | 桥接模式最主要的特点就是将实现层和抽象层解耦分离,是两部分可以独立变化
11 |
12 | 比如我们写一个跑步游戏,对于游戏中的人和精灵都是动作单元。而他们的动作也是非常的统一。比如人和精灵和球运动都是x,y坐标的改变,球的颜色和精灵的颜色绘制方式也非常的类似。
13 | 我们就可以将这些方法给抽象出来。
14 |
15 | ### 代码演示
16 |
17 | ```javascript 1.6
18 | //运动单元
19 | function Speed(x,y) {
20 | this.x = x;
21 | this.y = y;
22 | }
23 | Speed.prototype.run = function() {
24 | console.log('动起来');
25 | }
26 | // 着色单元
27 | function Color(cl) {
28 | this.color = cl;
29 | }
30 | Color.prototype.draw = function() {
31 | console.log('绘制色彩')
32 | }
33 |
34 | // 变形单元
35 | function Shape(ap) {
36 | this.shape = ap;
37 | }
38 | Shape.prototype.change = function() {
39 | console.log('改变形状');
40 | }
41 | //说话单元
42 | function Speak(wd) {
43 | this.word = wd;
44 | }
45 | Speak.prototype.say = function() {
46 | console.log('请开始你的表演')
47 | }
48 |
49 |
50 | //创建球类,并且它可以运动可以着色
51 | function Ball(x,y,c) {
52 | this.speed = new Speed(x,y);
53 | this.color = new Color(c);
54 | }
55 | Ball.prototype.init = function() {
56 | //实现运动和着色
57 | this.speed.run();
58 | this.color.draw();
59 | }
60 |
61 | function People(x,y,f) {
62 | this.speed = new Speed(x,y);
63 | this.speak = new Speak(f);
64 | }
65 |
66 | People.prototype.init = function() {
67 | this.speed.run();
68 | this.speak.say();
69 | }
70 | //...
71 |
72 |
73 | //当我们实例化一个人物对象的时候,他就可以有对应的方法实现了
74 |
75 | var p =new People(10,12,'我是一个人');
76 | p.init();
77 | ```
--------------------------------------------------------------------------------
/doc/design mode/观察者模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之观察者模式
2 |
3 | ### 概念
4 | 观察者模式,又被称为发布订阅模式,或者消息机制。定义了一种依赖关系,解决了主体对象和观察者之间功能的耦合
5 |
6 | 其实他就是定义了一种一对多的关系,让多个观察者对象同时监听某一个主体对象,这个主体对象的状态发生变化时,就会通知所有的观察者对象,使得他们能够自动更新自己。
7 |
8 | 观察者对象的好处:
9 | - 支持简单的广播通信,自动通知所有已经订阅过的对象。
10 | - 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
11 | - 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
12 |
13 | ### 代码演示
14 | js通过对观察者模式的实现是通过回调函数的方式
15 |
16 | 我们来先定义一个pubsub对象,其内部包含了3个方法:订阅、退订、发布
17 |
18 | ```javascript
19 | // 创建一个观察者
20 | var Observe = (function() {
21 | var _message = {};
22 | return {
23 | register:function(type,fn) {
24 | //注册信息接口
25 | if(typeof _message[type] === 'undefined'){
26 | _message[type] = [fn];
27 | }else{
28 | _message[type].push(fn);
29 | }
30 | },
31 | fire:function(type,args) {
32 | //发布消息接口
33 | if(!_message[type]) return;
34 | //消息信息
35 | var event = {
36 | type:type,
37 | args:args||{}
38 | },
39 | i = 0,
40 | len = _message[type].length;
41 | for(;i-0;i--){
51 | _message[type][i] === fn && _message[type].splice(i,1);
52 | }
53 | }
54 | }
55 | }
56 | })();
57 |
58 |
59 | Observe.register('test',function(e) {
60 | console.log(e.type,e.args.msg);
61 | });
62 | Observe.register('test',function(e) {
63 | console.log(e.type,'Nealyang');
64 | });
65 |
66 | Observe.fire('test',{msg:'test test'});
67 | ```
68 |
--------------------------------------------------------------------------------
/doc/design mode/简单工厂设计模式.md:
--------------------------------------------------------------------------------
1 | # JavaScript设计模式之简单工厂设计模式
2 |
3 | > 作为创建性设计模式中的第一个介绍的,这个模式必然比较简单,所以不做过多介绍
4 |
5 | ## 使用场景
6 |
7 | 简单工厂模式的概念就是创建对象,不仅如此,简单工厂模式还可以用来创建相似的对象。
8 |
9 | 举个例子来说,如果你想创建一些书,那么这些书都有一些相似的地方,比如目录、页码等。也有很多不同的地方,
10 | 如书名、出版时间、书的类型等,对于创建的对象相似的属性当然好处理,对于不同的属性就要有针对的修改处理了。
11 |
12 |
13 | ## 代码
14 | ```javascript
15 | //工厂模式
16 | function createBook(name,time,type) {
17 | //创建一个对象,并且对对象拓展属性和方法
18 | var o = new Object();
19 | o.name = name;
20 | o.time = time;
21 | o.type = type;
22 | o.getName = function() {
23 | console.log(this.name);
24 | }
25 |
26 | //将对象返回
27 | return o;
28 | }
29 |
30 | var book1 = createBook('js book','2017/11/16','JS');
31 | var book2 = createBook('css book','2017/11/13','CSS');
32 |
33 | book1.getName();
34 | book2.getName();
35 | ```
36 |
37 | ```javascript
38 | var Basketball = function() {
39 | this.info='美国篮球'
40 | }
41 | Basketball.prototype = {
42 | constructor:Basketball,
43 | getMember:function() {
44 | console.log('每队需要5个成员');
45 | },
46 | getBallSize:function() {
47 | console.log('这个篮球还是很大的');
48 | }
49 | }
50 | var Football = function() {
51 | this.info = '这是足球'
52 | }
53 | Football.prototype = {
54 | constructor:Football,
55 | getMember:function() {
56 | console.log('足球每队需要十一个人')
57 | },
58 | getBallSize:function() {
59 | console.log('足球我不喜欢');
60 | }
61 | }
62 | var Tennis = function() {
63 | this.info = '网球貌似现在还是蛮流行的';
64 | }
65 | Tennis.prototype = {
66 | constructor:Tennis,
67 | getMember:function() {
68 | console.log('一对一,二对二咯');
69 | },
70 | getBallSize:function() {
71 | console.log('网球还是比较小的');
72 | }
73 | }
74 |
75 | //球类工厂
76 | var sportsFactory = function(name) {
77 | switch (name){
78 | case 'NBA':
79 | return new Basketball();
80 | break;
81 | case 'wordCup':
82 | return new Football();
83 | break;
84 | default :
85 | return new Tennis();
86 | }
87 | }
88 | ```
89 |
90 | ## 说明
91 |
92 | 这种简单工厂模式非常的像寄生式继承,只不过这里o没有继承任何对象和类。
93 |
94 | 简单工厂的用途就是创建对象,或者创建相似的对象。比较简单,比较常规,这里不再赘述
--------------------------------------------------------------------------------
/doc/design mode/工厂方法模式.md:
--------------------------------------------------------------------------------
1 | # JavaScript设计模式之工厂方法模式
2 |
3 | > 学习一个东西,一定要明白这个东西的概念是什么,这个东西被提出来的目的是什么
4 |
5 | ## 前言
6 | 在之前,我们介绍过简单工厂设计模式,简单工厂设计模式存在唯一的工厂类,它的优点是所有产品类的实例化集中管理,便于理解。当产品数量较少,且不会经常发生变化时,我们当然可以直接使用简单工厂模式,但是有的时候,需求是在时刻变化的,产品类也可能随之增加,如果使用简单工厂模式,就避免不了去修改工厂类的代码。要解决这个问题,就得使用今天所讲的,工厂方法模式。
7 |
8 | 工厂方法模式本意是将实际创建对象的工作推迟到子类当中。这样核心类就成为了抽象类。
9 |
10 | ## 基本概念
11 | 工厂方法模式:不再有一个唯一的工厂类就创建产品,而是将不同的产品交给对应的工厂子类去实现。每个产品由负责生产的子工厂来创造。如果添加新的产品,需要做的是添加新的子工厂和产品,而不需要修改其他的工厂代码。
12 |
13 | 工厂方法模式主要有三种类组成:
14 | - 抽象工厂类:负责定义创建产品的公共接口
15 | - 产品子工厂:继承抽象工厂类,实现抽象工厂类提供的接口
16 | - 每一种产品各自的产品类
17 |
18 | ## 安全模式类
19 | 安全模式类就是可以屏蔽对类的错误使用而造成的后果。说白了,就是在构造函数开始时先判断当前对象this指向是不是类。
20 | ```javascript
21 | var Demo = function() {
22 | if(!(this instanceof Demo)){
23 | return new Demo();
24 | }
25 | }
26 | Demo.prototype.show = function() {
27 | console.log('show')
28 | }
29 |
30 | var d = Demo();
31 | d.show();
32 | ```
33 | ## 工厂方法模式
34 | 简单工厂模式仅仅适用于创建比较少的对象,如果需要创建多个类,并且会经常修改,像我们之前说的简单工厂的方法就不是很实用了,因为如果我要多添加一个类,就需要修改两个地方,所以这里我们采用工厂方法模式
35 |
36 | ```javascript
37 | var Factory = function(type,content) {
38 | if(this instanceof Factory){
39 | var temp = new this[type](content);
40 | }else{
41 | return new Factory(type,content);
42 | }
43 | }
44 | //在工厂原型中设置创建所有类型数据对象的基类
45 | Factory.prototype = {
46 | constructor:Factory,
47 | Java:function(content) {
48 | //...
49 | },
50 | JavaScript:function(content) {
51 | //...
52 | },
53 | UI:function(content) {
54 | this.content = content;
55 | (function(content) {
56 | var div = document.createElement('div');
57 | div.innerHTML = content;
58 | div.style.border = '1px solid red';
59 | document.getElementById(content).appendChild(div);
60 |
61 | })(content)
62 | }
63 | }
64 | ```
65 |
66 | 如上,我们就可以创建多个类了
67 |
68 | ```javascript
69 | var data = [
70 | {type:'JavaScript',content:'Javascript还是很重要的'},
71 | {type:'Java',content:'Java培训哪家强'},
72 | {type:'UI',content:'UI...'}
73 | ];
74 |
75 | for(var i = 0,length=data.length;i++;i 学习一个东西,一定要明白这个东西的概念是什么,这个东西被提出来的目的是什么
4 |
5 | ## 概念
6 | 建造者模式:将一个复杂对象的构建层和表示层相分离,同样的构建过程可以采用不同的表示。
7 |
8 | ## 应用场景
9 |
10 | 工厂模式主要是用来创建对象的实例(简单工厂模式,工厂方法模式)或者是类簇(抽象工厂模式),关心的是最终的产出是什么,所以工厂模式我们得到的是对象的实例或者对象的类簇。然而建造者模式在创建对象的过程中则更为复杂一些。虽然目的也是为了创建对象,但是更关心的创建对象的整个过程或者说是每一个细节。
11 |
12 | 比如创建一个人,我们创建的结果不仅仅是得到一个人的实例,还要关注创建人的时候,这个人是男是女,穿什么衣服带什么帽子等等。
13 |
14 | ## 代码演示
15 | ```javascript
16 | var Human = function(param) {
17 | this.skill = param && param.skill || '保密';
18 | this.hobby = param && param.hobby || '保密';
19 | }
20 | Human.prototype = {
21 | constructor:Human,
22 | getSill:function() {
23 | return this.skill;
24 | },
25 | getHobby:function() {
26 | return this.hobby;
27 | }
28 | }
29 | var Name = function(name) {
30 | var that = this;
31 | (function(name,that) {
32 | this.wholeName = name;
33 | if(name.indexOf(' ')>-1){
34 | that.firstName = name.slice(0,name.indexOf(' '));
35 | that.secondName = name.slice(name.indexOf(' '));
36 | }
37 | })(name,that)
38 | }
39 | var Work = function(work) {
40 | var that = this;
41 | (function(work,that) {
42 | switch (work){
43 | case 'code' :
44 | that.work = '工程师';
45 | that.wordDesc = '代码是我快乐';
46 | break;
47 | case 'UE' :
48 | that.work = '设计师';
49 | that.wordDesc = '设计更似艺术';
50 | break;
51 | default :
52 | that.work = work;
53 | that.wordDesc = '对不起,我们还不清楚你所选择职位的相关描述';
54 | }
55 | })(work,that);
56 | }
57 |
58 | //更换期望职位以及描述
59 | Work.prototype.changeWork = function(work) {
60 | this.work = work;
61 | }
62 |
63 | Work.prototype.changeDesc = function(desc) {
64 | this.wordDesc = desc;
65 | }
66 |
67 | //创建一个应聘者
68 | var Person = function(name,work) {
69 | var _person = new Human();
70 | _person.name = new Name(name);
71 | _person.work = new Work(work);
72 | return _person;
73 | }
74 |
75 | var person = new Person('Neal yang','code');
76 | console.log(person.skill);
77 | console.log(person.hobby);
78 | console.info(person.work);
79 | person.work.changeDesc('一撸代码就疯狂');
80 | console.info(person.work);
81 | ```
--------------------------------------------------------------------------------
/doc/dataStructure/queue.md:
--------------------------------------------------------------------------------
1 | # JavaScript数据结构与算法--队列
2 |
3 | ## 定义
4 | 队列是遵循FIFO原则的有序的项。队列在尾部添加新元素,并在顶部移除元素。最新添加的元素必须排列在队列的尾部
5 |
6 | ## 代码实现
7 |
8 | 普通队列
9 | ```javascript
10 | function Queue() {
11 | var items = [];
12 | this.enqueue = function(element) {
13 | items.push(element)
14 | };
15 | this.dequeue = function() {
16 | return items.shift()
17 | };
18 | this.front = function() {
19 | return items[0]
20 | };
21 | this.isEmpty =function() {
22 | return items.length === 0
23 | };
24 | this.clear = function() {
25 | items = [];
26 | };
27 | this.size = function() {
28 | return items.length;
29 | };
30 | this.print = function() {
31 | console.log(items.toString())
32 | }
33 | }
34 | ```
35 | 优先队列
36 | ```javascript
37 | function PriorityQueue() {
38 | var items = [];
39 | function QueueElement (element, priority){
40 | this.element = element;
41 | this.priority = priority;
42 | };
43 | this.enqueue = function(element, priority){
44 | var queueElement = new QueueElement(element, priority);
45 | if (this.isEmpty()){
46 | items.push(queueElement); // {2}
47 | } else {
48 | var added = false;
49 | for (var i=0; i 1){
74 | for (var i=0; i otherSet.size()){
68 | return false;
69 | } else {
70 | var values = this.values();
71 | for (var i=0; i 学习一个东西,一定要明白这个东西的概念是什么,这个东西被提出来的目的是什么
4 |
5 | ## 概念
6 |
7 | 原型模式:用原型实例指向创建对象的类,适用于创建新的对象的类共享原型对象的属性和方法。
8 |
9 | 这种继承是一种基于对属性和方法的共享而不是复制。
10 |
11 | ## 应用场景
12 |
13 | 在创建的类中,存在基类,起定义的方法和属性能够被子类所继承和使用。
14 |
15 | 原型模式就是将可复用的、可共享的、消耗大的从基类中提取出来然后放到原型中,然后子类通过组合继承或者寄生组合式继承将方法和属性继承下来。子类中对于那些需要重写的方法进行重写,这样,子类
16 | 创建的对象既有子类的属性和方法也共享着基类的属性和方法。
17 |
18 | ## 代码演示
19 |
20 | 拿网页中轮播图距离。大概的样子就是轮播图,但是有的是渐变效果,有的是滚动,有的带有箭头。。。所以这里我们可以创建一个基类,轮播图,然后再根据不同的需求再去修改。
21 |
22 |
23 |
24 | ```javascript 1.6
25 | //图片轮播图类
26 | var LoopImages = function(imgArr,container) {
27 | this.imageArr = imgArr;//轮播图片数组
28 | this.container = container;//轮播图片容器
29 | this.createImage = function() {
30 |
31 | };//创建轮播图
32 | this.changeImage = function() {
33 |
34 | }//轮播图切换
35 | }
36 | //上下切换
37 | var SlideLoopImage = function(imgArr,container) {
38 | //构造函数继承图片轮播类
39 | LoopImages.call(this,imgArr,container);
40 |
41 | //重写继承的图片切换方法
42 | this.changeImage = function() {
43 | console.log('上下切换的方式')
44 | }
45 | }
46 | //渐隐切换
47 | var FadeLoopImg = function(imgArr,container,arrow) {
48 | LoopImages.call(this,imgArr,container);
49 | //切换箭头私有变量
50 | this.arrow = arrow;
51 | this.changeImage = function() {
52 | console.log('渐隐的切换方式');
53 | }
54 | }
55 |
56 | //实例化一个
57 | var fadeImg = new FadeLoopImg(['01.jpg','02.jpg','03.jpg'],'slide',['left.jpg','right.jpg']);
58 | ```
59 |
60 | 但是如上的写法 ,其实还有一种更加优化的方法。首先看基类,作为基类是要被子类继承的,那么此时将属性和方法都写到基类的构造函数里会有一些问题。
61 | 比如每一次创建子类继承都要创建一次父类,如果父类的构造函数的创建过程中存在很多耗时较长的逻辑,这样的话性能消耗还是蛮大的。为了提高性能,我们可以使用共享机制。
62 |
63 | 对于每一次创建的一些简单而又差异化的属性我们可以放到构造函数中,而把一些消耗资源比较大的方法放到基类的原型中。这样避免很多不必要的消耗。
64 |
65 | ```javascript 1.6
66 | var LoopImages = function(imgArr,container) {
67 | this.imageArr = imgArr;
68 | this.container = container;
69 | }
70 | LoopImages.prototype = {
71 | crateImage : function() {
72 | console.log('创建轮播图方法');
73 | },
74 | changeImage:function() {
75 | console.log('图片切换方法');
76 | }
77 | }
78 | //上下切换
79 | var SlideloopImg = function(imgArr,container) {
80 | LoopImages.call(this,imgArr,container);
81 | }
82 | SlideloopImg.prototype = new LoopImages();
83 |
84 | SlideloopImg.prototype.changeImage = function() {
85 | console.log('上下切换的方式');
86 | }
87 |
88 | var FadeLoopImg = function(imgArr,container,arrow) {
89 | this.arrow = arrow;
90 | LoopImages.call(this,imgArr,container);
91 | }
92 | FadeLoopImg.prototype = new LoopImages();
93 |
94 | FadeLoopImg.prototype.changeImage = function() {
95 | console.log('渐隐切换方式');
96 | }
97 | ```
--------------------------------------------------------------------------------
/doc/design mode/职责链模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之职责链模式
2 |
3 | ### 概念
4 | 职责链模式:解决请求发送者和接受者之间的耦合,通过职责链上多个对象分解请求流程,实现请求在多个对象之间的传递,直到最后一个对象完成请求的处理。
5 |
6 | 职责链模式的优点是:请求发送者只需要直到链中的第一个节点,从而解耦了发送者和一组接收者之间的强联系。此外,使用了职责链模式之后,链中的节点对象可以灵活地拆分重组,增加或者删除 一个节点,以及改变节点在链中的位置都是轻而易举的。
7 |
8 | 职责链模式的缺点是:首先不能保证某个请求一定会被链中的某个节点处理,这种情况系下可以在链尾增加一个保底的接受者节点来处理这种即将离开链尾的请求。其次,职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分的节点并没有起到实质性的作用,从性能的角度考虑,应当避免过长的职责链带来的性能损耗。
9 |
10 | ### 代码演示
11 | 假设有这么一种场景:一个售卖手机的电商网站,经过分别交纳500元定金和200元定金的两轮预订之后(订单在此时已经生成),现在进入了正式购买阶段。
12 |
13 | 公司针对支付过定金的客户有一定的优惠,正式购买之后,已经支付过500元定金的用户会收到100元优惠券,200元定金的用户可以收到50元优惠券,没有支付过定金的只能进入普通购买方式,也就是没有优惠券。相关的字段有这么几种:
14 |
15 | oederType:订单类型,为1代表500元定金用户,2代表200元定金用户,3为普通购买用户;
16 |
17 | ```javascript 1.6
18 | //职责链模式
19 | var order500=function(orderType,pay,stock){
20 | if(orderType==1&&pay===true){
21 | console.log('500元定金预约,得到100元优惠券');
22 | }else{
23 | return 'nextSuccessor';
24 | }
25 | };
26 | var order200=function(orderType,pay,stock){
27 | if(orderType===2&&pay===true){
28 | console.log('200元定金预约,得到50优惠券');
29 | }else{
30 | return 'nextSuccessor';
31 | }
32 | };
33 |
34 | var orderNormal=function(orderType,pay,stock){
35 | if(stock>0){
36 | console.log('普通购买,无优惠券');
37 | }else{
38 | return 'nextSuccessor';
39 | }
40 | };
41 | ```
42 |
43 | 接下来需要把函数包装进职责链节点:
44 |
45 | ```javascript 1.6
46 | //职责链包装
47 |
48 | var Chain=function(fn){
49 | this.fn=fn;
50 | this.nextSuccessor=null;
51 | };
52 |
53 | Chain.prototype.setNextSuccessor=function(successor){
54 | return this.nextSuccessor=successor;
55 | };
56 |
57 | Chain.prototype.passRequest=function(){
58 | var ret=this.fn.apply(this,arguments);
59 |
60 | if(ret=='nextSuccessor'){
61 | //console.log(this.nextSuccessor.fn.name);
62 | return this.nextSuccessor&&this.nextSuccessor.passRequest.apply(this.nextSuccessor,arguments);//启动这一步启动递归了
63 | }
64 | return ret;
65 | };
66 |
67 |
68 | //测试
69 | var chainOrder500=new Chain(order500);
70 | var chainOrder200=new Chain(order200);
71 | var chainOrderNormal=new Chain(orderNormal);
72 |
73 | chainOrder500.setNextSuccessor(chainOrder200);
74 | chainOrder200.setNextSuccessor(chainOrderNormal);
75 |
76 |
77 | //将请求传递给第一个节点即可
78 | chainOrder500.passRequest(1,true,100);//输出 500元定金,得到100元优惠券
79 | chainOrder500.passRequest(2,true,100);//输出200元定金,得到50元优惠券
80 | chainOrder500.passRequest(1,false,0); //输出 手机库存不足
81 | ```
82 |
--------------------------------------------------------------------------------
/doc/design mode/适配器模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之适配器模式
2 |
3 | ### 概念
4 | 适配器模式:将一个类的接口转换为另外一个类的接口以满足用户的需求,使类之间的接口不兼容问题通过适配器得以解决。
5 |
6 | ### 代码演示
7 | 书中这里说的比价没意思,这里我拿汤姆大叔的例子来说下
8 |
9 | 我们来举一个例子,鸭子(Dock)有飞(fly)和嘎嘎叫(quack)的行为,而火鸡虽然也有飞(fly)的行为,但是其叫声是咯咯的(gobble)。如果你非要火鸡也要实现嘎嘎叫(quack)这个动作,那我们可以复用鸭子的quack方法,但是具体的叫还应该是咯咯的,此时,我们就可以创建一个火鸡的适配器,以便让火鸡也支持quack方法,其内部还是要调用gobble。
10 |
11 |
12 | 首先要先定义鸭子和火鸡的抽象行为,也就是各自的方法函数:
13 | ```javascript 1.6
14 | //鸭子
15 | var Duck = function(){
16 |
17 | };
18 | Duck.prototype.fly = function(){
19 | throw new Error("该方法必须被重写!");
20 | };
21 | Duck.prototype.quack = function(){
22 | throw new Error("该方法必须被重写!");
23 | }
24 |
25 | //火鸡
26 | var Turkey = function(){
27 |
28 | };
29 | Turkey.prototype.fly = function(){
30 | throw new Error(" 该方法必须被重写 !");
31 | };
32 | Turkey.prototype.gobble = function(){
33 | throw new Error(" 该方法必须被重写 !");
34 | };
35 |
36 |
37 | //鸭子
38 | var MallardDuck = function () {
39 | Duck.apply(this);
40 | };
41 | MallardDuck.prototype = new Duck(); //原型是Duck
42 | MallardDuck.prototype.fly = function () {
43 | console.log("可以飞翔很长的距离!");
44 | };
45 | MallardDuck.prototype.quack = function () {
46 | console.log("嘎嘎!嘎嘎!");
47 | };
48 |
49 | //火鸡
50 | var WildTurkey = function () {
51 | Turkey.apply(this);
52 | };
53 | WildTurkey.prototype = new Turkey(); //原型是Turkey
54 | WildTurkey.prototype.fly = function () {
55 | console.log("飞翔的距离貌似有点短!");
56 | };
57 | WildTurkey.prototype.gobble = function () {
58 | console.log("咯咯!咯咯!");
59 | };
60 | ```
61 |
62 | 为了让火鸡也支持quack方法,我们创建了一个新的火鸡适配器TurkeyAdapter:
63 |
64 | ```javascript 1.6
65 | var TurkeyAdapter = function(oTurkey){
66 | Duck.apply(this);
67 | this.oTurkey = oTurkey;
68 | };
69 | TurkeyAdapter.prototype = new Duck();
70 | TurkeyAdapter.prototype.quack = function(){
71 | this.oTurkey.gobble();
72 | };
73 | TurkeyAdapter.prototype.fly = function(){
74 | var nFly = 0;
75 | var nLenFly = 5;
76 | for(; nFly < nLenFly;){
77 | this.oTurkey.fly();
78 | nFly = nFly + 1;
79 | }
80 | };
81 | ```
82 |
83 | 该构造函数接受一个火鸡的实例对象,然后使用Duck进行apply,其适配器原型是Duck,然后要重新修改其原型的quack方法,以便内部调用oTurkey.gobble()方法。其fly方法也做了一些改变,让火鸡连续飞5次(内部也是调用自身的oTurkey.fly()方法)。
84 |
85 | ```javascript 1.6
86 | var oMallardDuck = new MallardDuck();
87 | var oWildTurkey = new WildTurkey();
88 | var oTurkeyAdapter = new TurkeyAdapter(oWildTurkey);
89 |
90 | //原有的鸭子行为
91 | oMallardDuck.fly();
92 | oMallardDuck.quack();
93 |
94 | //原有的火鸡行为
95 | oWildTurkey.fly();
96 | oWildTurkey.gobble();
97 |
98 | //适配器火鸡的行为(火鸡调用鸭子的方法名称)
99 | oTurkeyAdapter.fly();
100 | oTurkeyAdapter.quack();
101 | ```
--------------------------------------------------------------------------------
/doc/design mode/抽象工厂模式.md:
--------------------------------------------------------------------------------
1 | # JavaScript设计模式之抽象工厂模式
2 | > 学习一个东西,一定要明白这个东西的概念是什么,这个东西被提出来的目的是什么
3 |
4 | ## 概念
5 | 通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。也就是说我们要创建一个抽象类。这也是面向对象的开发语言中一种很常见的开发模式。
6 |
7 | ## 应用场景
8 | ```javascript
9 | var Car = function() {};
10 | Car.prototype.getPrice = function() {
11 | return new Error('抽象方法不能调用,需自行实现');
12 | };
13 | Car.prototype.getSpeed = function() {
14 | return new Error('抽象方法不能调用,需自行实现');
15 | }
16 | ```
17 |
18 | 由于JavaScript在没有abstract的具体实现,所以我们需要如上手动实现,也即是在创建这个类的时候,要求使用这些方法,我们需要手动去重写它,而不能继承使用。因为
19 | 在大型的应用中,总有一些子类去继承一些父类,这些父类经常会定义一些必要的方法,但是不会具体的去实现,会去要求子类自行实现。如果子类没有重写这些方法而去调用他,就会报错。
20 |
21 |
22 | ## 代码演示
23 | ```javascript
24 | // 抽象工厂方法
25 | var VehicleFactory = function(subType,superType) {
26 | //判断抽象工厂中是否有该抽象类
27 | if(typeof VehicleFactory[superType] === 'function'){
28 | //缓存类
29 | function F() {};
30 | //继承父类属性和方法
31 | F.prototype = new VehicleFactory[superType]();
32 | //将子类的constructor指向子类
33 | subType.constructor = subType;
34 | //子类原型继承父类
35 | subType.prototype = new F();
36 | }else{
37 | return new Error('未创建该抽象类');
38 | }
39 | }
40 |
41 | //小汽车抽象类
42 | VehicleFactory.Car = function() {
43 | this.type = 'car';
44 | }
45 | VehicleFactory.Car.prototype = {
46 | getPrice:function() {
47 | return new Error('抽象方法不能调用');
48 | },
49 | getSpeed:function() {
50 | return new Error('抽象方法不能调用');
51 | }
52 | };
53 | //公共汽车抽象类
54 | VehicleFactory.Bus = function() {
55 | this.type = 'Bus';
56 | }
57 | VehicleFactory.Bus.prototype = {
58 | getPrice:function() {
59 | return new Error('抽象方法不能调用');
60 | },
61 | getSpeed:function() {
62 | return new Error('抽象方法不能调用');
63 | }
64 | };
65 | //大卡车抽象类
66 | VehicleFactory.Trunk = function() {
67 | this.type = 'Trunk';
68 | }
69 | VehicleFactory.Trunk.prototype = {
70 | getPrice:function() {
71 | return new Error('抽象方法不能调用');
72 | },
73 | getSpeed:function() {
74 | return new Error('抽象方法不能调用');
75 | }
76 | };
77 | //子类
78 | var BMW = function(price,speed) {
79 | this.price = price;
80 | this.speed = speed;
81 | }
82 | VehicleFactory(BMW,'Car');
83 | BMW.prototype.getPrice = function() {
84 | return this.price;
85 | }
86 | BMW.prototype.getSpeed = function() {
87 | return this.speed;
88 | }
89 |
90 | //...
91 |
92 | var three = new BMW('35w','200');
93 | three.getSpeed();
94 | console.log(three.getPrice())
95 | ```
96 |
97 | 所以从上,我们可以看出,抽象工厂其实是实现子类继承父类的方法,在这个方法里,我们需要传递子类以及需要被继承的父类的名称,并且在抽象工厂方法中,又增加了一次对抽象类存在性的一次判断,然后通过寄生式继承,在继承中我们是通过new关键字复制了父类的一个实例,因为我们不仅仅需要继承父类原型上的方法,还需要继承父类的属性。所以通过new关键字将父类的构造函数执行一遍来复制父类构造函数中的属性和方法。
98 |
99 | 通过抽象工厂,我们就能知道每一个子类到底是哪一种类别了。
100 |
101 | 同时注意,抽象类中定义的方法这是显示定义一些功能,但是没有具体的实现,而一个对象是应该具备一套完整的功能的。所以用抽象类创建的对象当然也是抽象的。所以我们还不能直接用它来创建类的实例。
--------------------------------------------------------------------------------
/doc/basic_js/JavaScript中的执行上下文和变量对象.md:
--------------------------------------------------------------------------------
1 | # JavaScript中的执行上下文和变量对象
2 |
3 | > 最近在整理JavaScript中的一些基础知识,文章总结与各种书籍、博客。ps:汤姆大叔的深入系列非常好,非常推荐。真大神
4 |
5 | ## EC执行上下文
6 |
7 | ### 定义
8 | 每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文。执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。
9 |
10 | 标准规范没有从技术实现的角度定义EC的准确类型和结构,这应该是具体实现ECMAScript引擎时要考虑的问题。
11 |
12 |
13 | 活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在EC类型进入和退出上下文的时候被修改(推入或弹出)。
14 |
15 | JavaScript中,EC分为三种
16 | - 全局级别的代码 –– 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境
17 | - 函数级别的代码 ––当执行一个函数时,运行函数体中的代码。
18 | - Eval的代码 –– 在Eval函数内运行的代码。
19 |
20 | EC建立分为两个阶段:进入执行上下文(创建阶段)和执行阶段(激活/执行代码)
21 |
22 | #### 进入上下文阶段
23 | 发生在函数调用时,但是在执行具体代码之前(比如,对函数参数进行具体化之前)
24 |
25 | 创建作用域链(Scope Chain)
26 |
27 | 创建变量,函数和参数。
28 |
29 | 求”this“的值。
30 |
31 | #### 执行代码阶段
32 | 变量赋值
33 |
34 | 函数引用
35 |
36 | 解释/执行其他代码。
37 |
38 | 我们可以将EC看做是一个对象。
39 | ```javascript
40 | EC={
41 | VO:{/*变量对象, 函数中的arguments对象, 参数, 内部的变量以及函数声明 */},
42 | this:{},
43 | Scope:{ /* VO以及所有父执行上下文中的VO */}
44 | }
45 | ```
46 | 
47 |
48 | ### 全局代码
49 | 这种类型的代码是在"程序"级处理的:例如加载外部的js文件或者本地标签内的代码。全局代码不包括任何function体内的代码。
50 |
51 | 在初始化(程序启动)阶段,ECStack是这样的:
52 | ```javascript
53 | ECStack = [
54 | globalContext
55 | ];
56 | ```
57 | ### 函数代码
58 | 当进入funtion函数代码(所有类型的funtions)的时候,ECStack被压入新元素。需要注意的是,具体的函数代码不包括内部函数(inner functions)代码。如下所示,我们使函数自己调自己的方式递归一次:
59 | ```javascript
60 | (function foo(bar) {
61 | if (bar) {
62 | return;
63 | }
64 | foo(true);
65 | })();
66 | ```
67 | 那么,ECStack以如下方式被改变:
68 | ```javascript
69 | // 第一次foo的激活调用
70 | ECStack = [
71 | functionContext
72 | globalContext
73 | ];
74 |
75 | // foo的递归激活调用
76 | ECStack = [
77 | functionContext – recursively
78 | functionContext
79 | globalContext
80 | ];
81 | ```
82 | 每次return的时候,都会退出当前执行上下文的,相应地ECStack就会弹出,栈指针会自动移动位置,这是一个典型的堆栈实现方式。一个抛出的异常如果没被截获的话也有可能从一个或多个执行上下文退出。相关代码执行完以后,ECStack只会包含全局上下文(global context),一直到整个应用程序结束。
83 | ### Eval代码
84 | 直接看代码
85 | ```javascript
86 | eval('var x = 10');
87 |
88 | (function foo() {
89 | eval('var y = 20');
90 | })();
91 |
92 | alert(x); // 10
93 | alert(y); // "y" 提示没有声明
94 | ```
95 | ECStack的变化过程:
96 | ```javascript
97 | ECStack = [
98 | globalContext
99 | ];
100 |
101 | // eval('var x = 10');
102 | ECStack.push(
103 | evalContext,
104 | callingContext: globalContext
105 | );
106 |
107 | // eval exited context
108 | ECStack.pop();
109 |
110 | // foo funciton call
111 | ECStack.push( functionContext);
112 |
113 | // eval('var y = 20');
114 | ECStack.push(
115 | evalContext,
116 | callingContext: functionContext
117 | );
118 |
119 | // return from eval
120 | ECStack.pop();
121 |
122 | // return from foo
123 | ECStack.pop();
124 | ```
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/doc/design mode/组合模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之组合模式
2 |
3 | ### 概念
4 | 组合模式:又称部分-整体模式,将对象组合成树形结构以表示成“部分整体”的层次结构。组合模式使得用户对单个对象以及组合对象的使用具有一致性
5 |
6 | ### 使用场景
7 | 我们平时开发过程中,一定会遇到这种情况:同时处理简单对象和由简单对象组成的复杂对象,这些简单对象和复杂对象会组合成树形结构,在客户端对其处理的时候要保持一致性。比如电商网站中的产品订单,每一张产品订单可能有多个子订单组合,比如操作系统的文件夹,每个文件夹有多个子文件夹或文件,我们作为用户对其进行复制,删除等操作时,不管是文件夹还是文件,对我们操作者来说是一样的。在这种场景下,就非常适合使用组合模式来实现。
8 |
9 | 组合模式主要有三个角色:
10 |
11 | (1)抽象组件(Component):抽象类,主要定义了参与组合的对象的公共接口
12 |
13 | (2)子对象(Leaf):组成组合对象的最基本对象
14 |
15 | (3)组合对象(Composite):由子对象组合起来的复杂对象
16 |
17 | 理解组合模式的关键是要理解组合模式对单个对象和组合对象使用的一致性,我们接下来说说组合模式的实现加深理解。
18 |
19 | ### 代码演示
20 | ```javascript 1.6
21 | // 抽象一个虚拟父类
22 | var News = function() {
23 | this.children = [];
24 | this.element = null;
25 | }
26 |
27 | News.prototype = {
28 | init:function() {
29 | throw new Error('请重写你的方法');
30 | },
31 | add:function() {
32 | throw new Error('请重写你的方法');
33 | },
34 | getElement:function() {
35 | throw new Error('请重写你的方法');
36 | },
37 | }
38 |
39 | function iniheritObject(o) {
40 | function F() {}
41 | F.prototype = o;
42 | return new F();
43 | }
44 |
45 | function inheritPrototype(subClass,superClass) {
46 | var p = iniheritObject(superClass.prototype);
47 | p.constructor = subClass;
48 | subClass.prototype = p;
49 | }
50 | //容器类
51 | var Container = function(id,parent) {
52 | News.call(this);
53 | this.id = id;
54 | this.parent = parent;
55 | this.init();
56 | }
57 |
58 | //寄生式继承父类原型方法
59 | inheritPrototype(Container,News);
60 |
61 | Container.prototype.init = function() {
62 | this.element = document.createElement('ul');
63 | this.element.id = this.id;
64 | this.element.className = 'new-container';
65 | }
66 |
67 | Container.prototype.add = function(child) {
68 | this.children.push(child);
69 | this.element.appendChild(child.getElement());
70 | return this;
71 | }
72 |
73 | Container.prototype.getElement = function() {
74 | return this.element;
75 | }
76 |
77 | Container.prototype.show = function() {
78 | this.parent.appendChild(this.element)
79 | }
80 | //同样下一层极的行成员集合类以及后面新闻组合体类
81 | var Item = function(classname) {
82 | News.call(this);
83 | this.classname = classname;
84 | this.init();
85 | }
86 | inheritPrototype(Item,News);
87 | Item.prototype.init = function() {
88 | this.element = document.createElement('li');
89 | this.element.className = this.classname;
90 | }
91 | Item.prototype.add = function(child) {
92 | this.children.push(child);
93 | this.element.appendChild(child.getElement());
94 | return this;
95 | }
96 | Item.prototype.getElement = function() {
97 | return this.element;
98 | }
99 |
100 | var NewsGroup = function(className) {
101 | News.call(this);
102 | this.classname = classname|| '';
103 | this.init();
104 | }
105 | inheritPrototype(NewsGroup,News);
106 | NewsGroup.prototype.init = function() {
107 | this.element = document.createElement('div');
108 | this.element.className = this.classname;
109 | }
110 | NewsGroup.prototype.add = function(child) {
111 | this.children.push(child);
112 | this.element.appendChild(child.getElement());
113 | return this;
114 | }
115 | NewsGroup.prototype.getElement = function() {
116 | return this.element;
117 | }
118 |
119 | ```
120 |
121 | 所以后面我们在使用的时候,创建新闻类,利用之前定义的组合元素去组合就可以了。
--------------------------------------------------------------------------------
/doc/design mode/享元模式.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式之享元模式
2 |
3 | ### 概念
4 | 享元模式:运用共享技术有效的支持大量细粒度对象,避免对象之间拥有相同内容造成的不必要开销
5 |
6 | 主要用来优化程序的性能,适合解决大量类似的对象产生的性能问题。享元模式通过分析应用程序的对象,将其解析为内在数据和外在数据,减少对象数量,从而提高程序的性能。
7 |
8 |
9 | ### 基础知识
10 | 享元模式通过共享大量的细粒度的对象,减少对象的数量,从而减少对象的内存,提高应用程序的性能。其基本思想就是分解现有类似对象的组成,将其展开为可以共享的内在数据和不可共享的外在数据,我们称内在数据的对象为享元对象。通常还需要一个工厂类来维护内在数据。
11 |
12 | 在JS中,享元模式主要有下面几个角色组成:
13 | - 客户端:用来调用享元工厂来获取内在数据的类,通常是应用程序所需的对象
14 | - 享元工厂:用来维护享元数据的类
15 | - 享元类:保持内在数据的类
16 |
17 |
18 | ### 基本实现
19 | 我们举个例子进行说明:苹果公司批量生产iphone,iphone的大部分数据比如型号,屏幕都是一样,少数部分数据比如内存有分16G,32G等。未使用享元模式前,我们写代码如下:
20 |
21 | function Iphone(model, screen, memory, SN) {
22 | this. model = model;
23 | this.screen = screen;
24 | this.memory = memory;
25 | this.SN = SN;
26 | }
27 | var phones = [];
28 | for (var i = 0; i < 1000000; i++) {
29 | var memory = i % 2 == 0 ? 16 : 32;
30 | phones.push(new Iphone("iphone6s", 5.0, memory, i));
31 | }
32 |
33 | 这段代码中,创建了一百万个iphone,每个iphone都独立申请一个内存。但是我们仔细观察可以看到,大部分iphone都是类似的,只是内存和序列号不一样,如果是一个对性能要求比较高的程序,我们就要考虑去优化它。
34 | 大量相似对象的程序,我们就可以考虑用享元模式去优化它,我们分析出大部分的iphone的型号,屏幕,内存都是一样的,那这部分数据就可以公用,就是享元模式中的内在数据,定义享元类如下:
35 |
36 | function IphoneFlyweight(model, screen, memory) {
37 | this.model = model;
38 | this.screen = screen;
39 | this.memory = memory;
40 | }
41 |
42 | 我们定义了iphone的享元类,其中包含型号,屏幕和内存三个数据。我们还需要一个享元工厂来维护这些数据:
43 |
44 | var flyweightFactory = (function () {
45 | var iphones = {};
46 | return {
47 | get: function (model, screen, memory) {
48 | var key = model + screen + memory;
49 | if (!iphones[key]) {
50 | iphones[key] = new IphoneFlyweight(model, screen, memory);
51 | }
52 | return iphones[key];
53 | }
54 | };
55 | })();
56 |
57 | 在这个工厂中,我们定义了一个字典来保存享元对象,提供一个方法根据参数来获取享元对象,如果字典中有则直接返回,没有则创建一个返回。
58 | 接着我们创建一个客户端类,这个客户端类就是修改自iphone类:
59 |
60 | function Iphone(model, screen, memory, SN) {
61 | this.flyweight = flyweightFactory.get(model, screen, memory);
62 | this.SN = SN;
63 | }
64 |
65 | 然后我们依旧像之间那样生成多个iphone
66 |
67 | var phones = [];
68 | for (var i = 0; i < 1000000; i++) {
69 | var memory = i % 2 == 0 ? 16 : 32;
70 | phones.push(new Iphone("iphone6s", 5.0, memory, i));
71 | }
72 | console.log(phones);
73 |
74 | 这里的关键就在于Iphone构造函数里面的this.flyweight = flyweightFactory.get(model, screen, memory)。这句代码通过享元工厂去获取享元数据,而在享元工厂里面,如果已经存在相同数据的对象则会直接返回对象,多个iphone对象共享这部分相同的数据,所以原本类似的数据已经大大减少,减少的内存的占用。
75 |
76 | ### 在DOM中的使用
77 |
78 |
86 |
87 | 点击菜单项,进行相应的操作,我们通过jQuery来绑定事件,一般会这么做:
88 |
89 | $(".item").on("click", function () {
90 | console.log($(this).text());
91 | })
92 |
93 | 给每个列表项绑定事件,点击输出相应的文本。这样看暂时没有什么问题,但是如果是一个很长的列表,尤其是在移动端特别长的列表时,就会有性能问题,因为每个项都绑定了事件,都占用了内存。但是这些事件处理程序其实都是很类似的,我们就要对其优化。
94 |
95 | $(".menu").on("click", ".item", function () {
96 | console.log($(this).text());
97 | })
98 |
99 | 通过这种方式进行事件绑定,可以减少事件处理程序的数量,这种方式叫做事件委托,也是运用了享元模式的原理。事件处理程序是公用的内在部分,每个菜单项各自的文本就是外在部分。我们简单说下事件委托的原理:点击菜单项,事件会从li元素冒泡到ul元素,我们绑定事件到ul上,实际上就绑定了一个事件,然后通过事件参数event里面的target来判断点击的具体是哪一个元素,比如低级第一个li元素,event.target就是li,这样就能拿到具体的点击元素了,就可以根据不同元素进行不同的处理。
100 |
101 |
102 | > 参考地址:http://luopq.com/2015/11/20/design-pattern-flyweight/
--------------------------------------------------------------------------------
/doc/basic_js/夯实JS系列--变量、作用域和内存问题.md:
--------------------------------------------------------------------------------
1 | # 夯实JS系列--变量、作用域和内存问题
2 | >最近在忙于写一个react+node的全栈博客demo,没有时间更新文章。但是还是觉得这样一忙起来不更新是不应该的。正好在空闲上下班地铁上都会再去细读js原生知识。所以打算整理、总结、系统性的分享给大家。
3 |
4 | ## 基本类型和引用类型
5 | 在ECMAScript中,变量分为基本类型和引用类型两种。
6 | 基本类型就是存储简单的数据段。而引用类型指的是那些可能由多个值构成的对象。
7 | 在ECMAScript中,基本类型包括:Undefined、Null、Boolean、Number和String。
8 | 这些基本类型的对象都是按值访问的。所以js中我们可以直接操作他们。
9 | 但是引用类型如Object等,是按照引用来操作的。并非直接操作其值。
10 | 并且我们可以动态的为引用类型变量添加属性和方法。而基本类型则不可以。
11 |
12 | ## 变量赋值和传参
13 | 这里其实对于基本类型来说没有什么需要重点说明的。这里就重点说下引用类型吧
14 |
15 | 对于赋值
16 | ```javascript
17 | function setName(obj) {
18 | obj.name = "Neal";
19 | obj = new Object();
20 | obj.name = "yang";
21 | }
22 | var person = new Object();
23 | setName(person);
24 | console.log(person.name);
25 | ```
26 | 如上代码,最后console出来的是Neal。
27 |
28 | 这段代码说明两点:
29 | - 引用类型在传参的时候,是按照引用传递的,不然不可能person.name为Neal
30 | - 即使在函数内部修改了参数的值。原始的引用依然不变。实际上,在重写obj的时候,这个变量的引用已经是一个局部变量了。只是在这儿函数运行完,这个对象被销毁了。
31 |
32 | 所以说到这,对于对象的赋值,一句以概之:引用的赋值。
33 |
34 | ## 执行环境及其作用域
35 | > 这大概是一个非常基础也是重要的部分,后续会在进阶里面详细展开。
36 |
37 | 执行环境定义了变量或者函数有权访问的其他数据,决定了他们的行为。每一个执行环境都有一个与之关联的变量对象(如global、window)。环境中定义的所有变量和函数都保存在这个对象中。
38 |
39 | 某一个执行环境执行完毕后,该环境会被销毁。其中的所有的变量和函数也将随之销毁。全局执行环境知道应用程序退出才被销毁(如关闭网页等)
40 |
41 | 当代码在一个环境中执行的时候,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的变量和函数的有序访问。作用域链的前端,始终是当前执行的代码所在的
42 | 环境的变量对象。全局执行环境始终是作用域链的最后一个对象。
43 |
44 | 标识符的解析也就是沿着作用域链一级一级的搜索的过程。搜索过程从作用域链的前端开始,然后逐级向后回溯。知道找到标识符为止。
45 |
46 | ```javascript
47 | var color = 'red';
48 | function changeColor() {
49 | var anotherColor = 'blue';
50 | function swapColors() {
51 | var tempColor = anotherColor;
52 | anotherColor = color;
53 | color = tempColor;
54 | //这个执行环境中可以访问到 tempColor color antherColor
55 | }
56 | //这里只能访问anotherColor color
57 | swapColors();
58 | }
59 | changeColor();//这里只能访问color
60 | ```
61 |
62 | 所以从上面代码我们可以感受到:内部环境可以通过作用域链访问到外部环境的变量。反之不可。这些环境之间的联系都是线性、有次序的。
63 |
64 | ### 延长作用域链
65 | 虽然执行环境的类型只有两种。局部的和全局的。但是还有一种方法可以延长作用域链。
66 |
67 | 这是因为有些语句可以在作用域链的前端临时添加一个变量对象,改变量对象会在代码执行后被移除。
68 |
69 | - try-catch 语句中的catch
70 | - with语句
71 |
72 | 对于width语句而言,会将指定的对象添加到作用域链中。对于catch语句而言,会创建一个新的变量对象,其中包含被抛出的错误对象的申明。
73 |
74 | 关于作用域、环境之类的话题后续会再细说。这里作为基础篇,就先介绍到这里。
75 |
76 | ## 垃圾收集
77 |
78 | 很开心~js不需要你来收拾垃圾!好~此篇完结!
79 |
80 | 好吧~虽然我们不收拾垃圾,但是也是要稍微了解下js是如何收拾垃圾的。
81 |
82 | 首先什么是垃圾:哪些不再被继续使用的变量都是垃圾。什么叫收拾?释放起垃圾所占用的空间即为释放。
83 |
84 | 局部变量只在函数执行过过程中存在。而在这个过程中,会为局部变量在栈或者堆中分配相应的内存空间(存值呗)。然后函数执行啦,用了这些变量,执行完啦。完啦!则这些变量就没有用了。没用了,则为垃圾,既需清理。
85 |
86 | 但是并非所有的情况下都这么容易的得出结论。垃圾收集器必须跟踪哪个变量用了哪个变量没用。对于不在利用的打上标记,已被将来收回其所占用的内存。
87 |
88 | ### 标记清除
89 | 这是最为常用一种清除方式。当一个变量进入到环境的时候,标记为‘进入环境’,这个基本是不会被清除的,因为执行流进入到相应的环境的时候可能会用到。当变量离开环境的时候,标记为‘离开环境’。
90 |
91 | 可以使用任何方式来标记。我们要知道是如何标记不重要,重要的是采用什么策略。
92 |
93 | 垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。他会去掉环境中的变量以及被环境中的变量所引用的变量的标记。剩下的,则视为嫌疑人,准备删除。因为环境中的变量已经无法访问到这些变量了。目前IE、ff 、 opera 、 chrome都是这种标记清除方式
94 |
95 | ### 引用计数
96 | > 因为不常用,简单说下
97 |
98 | 引用计数的意思就是跟踪记录每一个值被引用的次数。当一个引用类型的变量复制给一个变量的时候,这个引用次数则+1,如果有别复制给另一个变量,则再+1,如果包含对这个值的引用的变量又被赋值了别的值。则这个值-1.
99 |
100 | 当引用次数为0的时候,为垃圾~回收!
101 |
102 | 为什么不常用呢?看着也很清晰啊!
103 |
104 | look code:
105 | ```javascript
106 | function test() {
107 | var objectA = new Object();
108 | var objectB = new Object();
109 |
110 | objectA.someOtherObject = objectB;
111 | objectB.someOtherObject = objectA;
112 | }
113 | ```
114 | 如上,对象A和对象B的属性互相引用。也就是说,这两个对象的引用次数永远都是2.哪怕这个函数执行完咯,也没法清理的。对的,这就是bug~
115 |
116 | ## 节制点~你懂得
117 | 虽然垃圾回收机制帮我们做了很多事,但是电脑分配给浏览器的可用内存通常要比桌面应用的内存要小的多,毕竟是为了防止运行js的网页耗尽所有的内存而导致系统崩溃的问题发生。
118 |
119 | 所以我们确保用最少的内存可以让页面获取最好的性能,最佳的执行方案就是执行中的代码都是有必要的数据。就好比用最低的经济拿最多的人头一样,一旦经济不够,技术弥补!一旦数据不要用了,自己主动扫除。
120 |
121 | ```javascript
122 | function createPerson(name) {
123 | var localPerson = new Object();
124 | localPerson.name = name;
125 | return localPerson;
126 | }
127 | var neal = createPerson('Neal');
128 |
129 | //主动清理垃圾
130 | createPerson = null;
131 | ```
132 |
133 | 这里讲createPerson设置为null,并没有就把他给清除了,只是释放了他的引用。让其脱离其执行环境,以便于垃圾收集器更快的将其回收。
134 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YOU-SHOULD-KNOW-JS
2 | something about js that you should know or want to know
3 |
4 | # 移步隔壁:[Personal blog](https://github.com/Nealyang/PersonalBlog)
5 |
6 |
7 | #### | javascript重点总结
8 |
9 | - you-should-know:[编写高质量代码基本要点](./doc/basic_js/编写高质量代码基本要点.md)
10 | - you-should-know:[变量、作用域和内存问题](./doc/basic_js/夯实JS系列--变量、作用域和内存问题.md)
11 | - you-should-know:[面向对象最全总结](./doc/basic_js/prototype-based.md)
12 | - you-should-know:[忍者级别的操作函数](./doc/basic_js/忍者级别的操作函数.md)
13 | - you-should-know:[谈谈闭包](./doc/basic_js/谈谈闭包.md)
14 | - you-should-know:[This is this](./doc/basic_js/彻底明白this指向.md)
15 | - you-should-know:[原型与原型链](./doc/basic_js/原型和原型链.md)
16 | - you-should-know:[JavaScript中的执行上下文和变量对象](./doc/basic_js/JavaScript中的执行上下文和变量对象.md)
17 | - you-should-know:[JavaScript中的跨域总结](./doc/basic_js/JavaScript中的跨域总结.md)
18 | - you-should-know:JavaScript中的不老事件
19 | - you-should-know:关于DOM操作
20 | - you-should-know:解析JavaScript中的对象
21 | - you-should-know:解析JavaScript中的行为委托
22 | - you-should-know:别忘记了AJAX
23 |
24 | #### | javascript数据结构和算法
25 | - [x] [数组](doc/dataStructure/array.md)
26 | - [x] [栈](doc/dataStructure/stack.md)
27 | - [x] [队列](doc/dataStructure/queue.md)
28 | - [x] [链表](doc/dataStructure/linkedList.md)
29 | - [x] [集合](doc/dataStructure/Set.md)
30 | - [x] [字典和散列表](doc/dataStructure/Map&HashMap.md)
31 | - [x] [树](doc/dataStructure/tree.md)
32 | - [ ] 图
33 |
34 | - [ ] 排序和搜索算法
35 |
36 |
37 | #### | javascript设计模式
38 |
39 | - [x] [面向对象](./doc/design%20mode/面向对象.md)
40 | ###### | 创建型设计模式
41 | - [x] [简单工厂模式](./doc/design%20mode/简单工厂设计模式.md)
42 | - [x] [工厂方法模式](./doc/design%20mode/工厂方法模式.md)
43 | - [x] [抽象工厂模式](./doc/design%20mode/抽象工厂模式.md)
44 | - [x] [建造者模式](./doc/design%20mode/建造者模式.md)
45 | - [x] [原型模式](./doc/design%20mode/原型模式.md)
46 | - [x] [单例模式](./doc/design%20mode/单例模式.md)
47 | ###### | 结构型设计模式
48 | - [x] [外观模式](./doc/design%20mode/外观模式.md)
49 | - [x] [适配器模式](./doc/design%20mode/适配器模式.md)
50 | - [x] [代理模式](./doc/design%20mode/代理模式.md)
51 | - [x] [装饰者模式](./doc/design%20mode/装饰着模式.md)
52 | - [x] [桥接模式](./doc/design%20mode/桥接模式.md)
53 | - [x] [组合模式](./doc/design%20mode/组合模式.md)
54 | - [x] [享元模式](./doc/design%20mode/享元模式.md)
55 | ###### | 行为型设计模式
56 | - [x] [模板方法模式](./doc/design%20mode/模板方法模式.md)
57 | - [x] [观察者模式](./doc/design%20mode/观察者模式.md)
58 | - [x] [状态模式](./doc/design%20mode/状态模式.md)
59 | - [x] [策略模式](./doc/design%20mode/策略模式.md)
60 | - [x] [职责链模式](./doc/design%20mode/职责链模式.md)
61 | - [x] [命令模式](./doc/design%20mode/命令模式.md)
62 | - [x] [访问者模式](./doc/design%20mode/访问者模式.md)
63 | - [x] [中介者模式](./doc/design%20mode/中介者模式.md)
64 | - [x] [备忘录模式](./doc/design%20mode/备忘录模式.md)
65 | - [x] [迭代器模式](./doc/design%20mode/迭代器模式.md)
66 | - [x] [解释器模式](./doc/design%20mode/解释器模式.md)
67 | ###### | 技巧型设计模式
68 | - [ ] 链模式
69 | - [ ] 委托模式
70 | - [ ] 数据访问对象模式
71 | - [ ] 节流模式
72 | - [ ] 简单模板模式
73 | - [ ] 惰性模式
74 | - [ ] 参与者模式
75 | - [ ] 等待者模式
76 | ###### | 架构型设计模式
77 | - [ ] 同步模块模式
78 | - [ ] 异步模块模式
79 | - [ ] Widget模式
80 | - [ ] MVC模式
81 | - [ ] MVP模式
82 | - [ ] MVVM模式
83 |
84 |
85 |
86 | #### | 是的,我也搞了ES6
87 |
88 | - [x] [let、const命令](https://github.com/Nealyang/ES6_pratice)
89 | - [x] [变量的解构赋值](https://github.com/Nealyang/ES6_pratice)
90 | - [x] [字符串的扩展](https://github.com/Nealyang/ES6_pratice)
91 | - [x] [数值的扩展](https://github.com/Nealyang/ES6_pratice)
92 | - [x] [数组的扩展](https://github.com/Nealyang/ES6_pratice)
93 | - [x] [函数的扩展](https://github.com/Nealyang/ES6_pratice)
94 | - [x] [对象的扩展](https://github.com/Nealyang/ES6_pratice)
95 | - [x] [.Symbol](https://github.com/Nealyang/ES6_pratice)
96 | - [x] [.Proxy 和 Reflect](https://github.com/Nealyang/ES6_pratice)
97 | - [x] [二进制数组](https://github.com/Nealyang/ES6_pratice)
98 | - [x] [.Set 和 Map 数据结构](https://github.com/Nealyang/ES6_pratice)
99 | - [x] [.Iterator和for...of循环](https://github.com/Nealyang/ES6_pratice)
100 | - [x] [.Promise对象](https://github.com/Nealyang/ES6_pratice)
101 | - [x] [.异步操作和Async函数](https://github.com/Nealyang/ES6_pratice)
102 | - [x] [.Class](https://github.com/Nealyang/ES6_pratice)
103 | - [x] [.修饰器](https://github.com/Nealyang/ES6_pratice)
104 | - [x] [.Module](https://github.com/Nealyang/ES6_pratice)
105 | - [x] [.编程风格](https://github.com/Nealyang/ES6_pratice)
106 | - [x] [读懂 ECMAScript 规格](https://github.com/Nealyang/ES6_pratice)
107 |
108 |
109 | ## 交流(QQ群)
110 |
111 | 前端技术杂谈: 604953717
112 |
113 | React技术栈:398240621
114 |
115 | nodejs技术1群:209530601
116 |
117 |
--------------------------------------------------------------------------------
/doc/basic_js/原型和原型链.md:
--------------------------------------------------------------------------------
1 |
2 | # 原型与原型链
3 |
4 |
5 | ## 原型
6 | 原型其实是一个比较简单比较容易理解的东西。如果你看过我之前写的面向对象的文章的话,我想你对原型肯定有了一个比价清晰的认识。
7 | 但是这里,我们还是拿出来说一下。
8 |
9 | 原型其实就是一个对象,其他的对象可以通过它来实现属性的继承。而且,任何一个对象,都可以成为原型。而且所有的对象,默认情况下都有一个原型。
10 | 毕竟原型本身也就是对象,所以每一个原型自身也有其自身的原型。出了一个例外,那就是原型链上顶端的男人。也就是null。
11 |
12 | 一个对象的真正原型是被对象内部[[Prototype]]所持有,ECMA引入了标准对象原型访问器,Object.getPropertyOf(object)
13 | ```javascript
14 | var proto = {};
15 | var obj = Object.create(proto);
16 | Object.getPrototypeOf(obj) === proto; // true
17 | ```
18 | 当然,我们也可以通过非标准的访问器__proto__来获取实例的原型。
19 |
20 | 原型的真正魅力在于多个实例之间公用一个通用原型的时候,原型对象的属性一旦被定义,就可以被多个引用他的实例所继承。这种操作在性能和维护方面的意义也是不言而喻的。
21 |
22 | ### 不使用原型的例子
23 | ```javascript
24 | var decimalDigits = 2,
25 | tax = 5;
26 |
27 | function add(x, y) {
28 | return x + y;
29 | }
30 |
31 | function subtract(x, y) {
32 | return x - y;
33 | }
34 |
35 | //alert(add(1, 3));
36 | ```
37 | 下面我们来通过原型美化一下
38 |
39 | ### 原型使用方式一
40 | ```javascript
41 | var Calculator = function (decimalDigits, tax) {
42 | this.decimalDigits = decimalDigits;
43 | this.tax = tax;
44 | };
45 | Calculator.prototype = {
46 | add: function (x, y) {
47 | return x + y;
48 | },
49 |
50 | subtract: function (x, y) {
51 | return x - y;
52 | }
53 | };
54 | //alert((new Calculator()).add(1, 3));
55 | ```
56 | ### 原型使用方式二
57 | ```javascript
58 | Calculator.prototype = function () {
59 | add = function (x, y) {
60 | return x + y;
61 | },
62 |
63 | subtract = function (x, y) {
64 | return x - y;
65 | }
66 | return {
67 | add: add,
68 | subtract: subtract
69 | }
70 | } ();
71 |
72 | //alert((new Calculator()).add(11, 3));
73 | ```
74 | 很明显方式二的写法更好一点,因为它封装了function,通过return形式暴露出简单的使用名称,打到了private/public的效果
75 |
76 | 当然,我们也可以分步声明。其实说白了,原型式对象,所以我们当然可以像给对象添加属性的方式那样来添加原型上的方法
77 |
78 | ```javascript
79 | var BaseCalculator = function () {
80 | //为每个实例都声明一个小数位数
81 | this.decimalDigits = 2;
82 | };
83 |
84 | //使用原型给BaseCalculator扩展2个对象方法
85 | BaseCalculator.prototype.add = function (x, y) {
86 | return x + y;
87 | };
88 |
89 | BaseCalculator.prototype.subtract = function (x, y) {
90 | return x - y;
91 | };
92 | ```
93 | ### 重写原型
94 | 在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过继续声明的同样的add代码的形式来达到覆盖重写前面的add功能,代码如下:
95 | ```javascript
96 | //覆盖前面Calculator的add() function
97 | Calculator.prototype.add = function (x, y) {
98 | return x + y + this.tax;
99 | };
100 |
101 | var calc = new Calculator();
102 | alert(calc.add(1, 1));
103 | ```
104 | 但是有一点需要注意:那就是重写的代码需要放在最后,这样才能覆盖前面的代码。
105 |
106 | ## 原型链
107 | ```javascript
108 | function Foo() {
109 | this.value = [1,2,4];
110 | }
111 | Foo.prototype = {
112 | method: function() {}
113 | };
114 |
115 | function Bar() {}
116 |
117 | // 设置Bar的prototype属性为Foo的实例对象
118 | Bar.prototype = new Foo();
119 | Bar.prototype.foo = 'Hello World';
120 |
121 | // 修正Bar.prototype.constructor为Bar本身
122 | Bar.prototype.constructor = Bar;
123 |
124 | var test = new Bar() // 创建Bar的一个新实例
125 |
126 | // 原型链
127 | test [Bar的实例]
128 | Bar.prototype [Foo的实例]
129 | { foo: 'Hello World' }
130 | Foo.prototype
131 | {method: ...};
132 | Object.prototype
133 | {toString: ... /* etc. */};
134 | ```
135 | 上面的例子中,test 对象从 Bar.prototype 和 Foo.prototype 继承下来;因此,它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性。
136 |
137 | ### hasOwnProperty
138 |
139 | hasOwnProperty是Object.prototype的一个方法,它可是个好东西,他能判断一个对象是否包含自定义属性而不是原型链上的属性,因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
140 | ```javascript
141 | // 修改Object.prototype
142 | Object.prototype.bar = 1;
143 | var foo = {goo: undefined};
144 |
145 | foo.bar; // 1
146 | 'bar' in foo; // true
147 |
148 | foo.hasOwnProperty('bar'); // false
149 | foo.hasOwnProperty('goo'); // true
150 | ```
151 |
152 | 只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。
153 |
154 | 但有个恶心的地方是:JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果。
155 | ```javascript
156 | var foo = {
157 | hasOwnProperty: function() {
158 | return false;
159 | },
160 | bar: 'Here be dragons'
161 | };
162 |
163 | foo.hasOwnProperty('bar'); // 总是返回 false
164 |
165 | // 使用{}对象的 hasOwnProperty,并将其上下为设置为foo
166 | {}.hasOwnProperty.call(foo, 'bar'); // true
167 |
168 | ```
169 |
170 | 当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰。例子比较多,这里就不举例了。
--------------------------------------------------------------------------------
/doc/dataStructure/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @authors Nealyang(nealyang231@gmail.com)
4 | * @date 2017/12/26
5 | * @version 1.0.0
6 | */
7 |
8 | function BinarySearchTree() {
9 | var Node = function (key) {
10 | this.key = key;
11 | this.left = null;
12 | this.right = null;
13 | };
14 |
15 | function insertNode(node, newNode) {
16 | if (newNode.key < node.key) {
17 | if (node.left) {
18 | node.left = newNode;
19 | } else {
20 | insertNode(node.left, newNode);
21 | }
22 | } else {
23 | node.right ? insertNode(node.right, newNode) : node.right = newNode;
24 | }
25 | }
26 |
27 |
28 | var root = null;
29 |
30 | this.insert = function (key) {
31 | var newNode = new Node(key);
32 | if (root == null) {
33 | root = newNode;
34 | } else {
35 | insertNode(root, newNode);
36 | }
37 | };
38 |
39 | //遍历二叉树,中序遍历(inOrderTraverse) 先序遍历(preOrderTraverse) 后序遍历(postOrderTraverse)
40 | function inOrderTraverseNode(root, callBack) {
41 | if (!root) {
42 | inOrderTraverseNode(root.left, callBack);
43 | callBack(root.key);
44 | inOrderTraverseNode(root.right, callBack);
45 | }
46 | };
47 |
48 | function preOrderTraverseNode(root, callBack) {
49 | if (!root) {
50 | callBack(root.key);
51 | inOrderTraverseNode(root.left, callBack);
52 | inOrderTraverseNode(root.right, callBack);
53 | }
54 | };
55 |
56 | function postOrderTraverseNode(root, callBack) {
57 | if (!root) {
58 | inOrderTraverseNode(root.left, callBack);
59 | inOrderTraverseNode(root.right, callBack);
60 | callBack(root.key);
61 | }
62 | };
63 | this.inOrderTraverse = function (callBack) {
64 | inOrderTraverseNode(root, callBack);
65 | };
66 |
67 | function preOrderTraverse(callBack) {
68 | preOrderTraverseNode(root, callBack);
69 | };
70 |
71 | function postOrderTraverse(callBack) {
72 | postOrderTraverseNode(root, callBack);
73 | };
74 |
75 | var minNode = function (node) {
76 | if (node) {
77 | while (node && node.left !== null) {
78 | node = node.left;
79 | return node.key;
80 | }
81 | return null;
82 | }
83 | };
84 | var findMinNode = function (node) {
85 | if (node) {
86 | while (node && node.left !== null) {
87 | node = node.left;
88 | return node;
89 | }
90 | return null;
91 | }
92 | };
93 | this.min = function () {
94 | return minNode(root);
95 | };
96 | var maxNode = function (node) {
97 | if (node) {
98 | while (node && node.right !== null) {
99 | node = node.right;
100 | }
101 | return node.key;
102 | }
103 | return null;
104 | };
105 | this.max = function () {
106 | return maxNode(root);
107 | };
108 | var searchNode = function (node, key) {
109 | if (node === null) {
110 | return false;
111 | }
112 | if (key < node.key) {
113 | return searchNode(node.left, key);
114 | } else if (key > node.key) {
115 | return searchNode(node.right, key);
116 | } else {
117 | return true;
118 | }
119 | };
120 | this.search = function (key) {
121 | return searchNode(root, key);
122 | };
123 |
124 | this.remove = function (key) {
125 | root = removeNode(root, key);
126 | };
127 | var removeNode = function (node, key) {
128 | if (node) {
129 | if (key < node.key) {
130 | node.left = removeNode(node.left, key);
131 | return node;
132 | } else if (key > node.right) {
133 | node.right = removeNode(node.right, key);
134 | return node;
135 | }else {
136 | //叶节点
137 | if(node.left === null && node.right === null){
138 | node = null;
139 | return node;
140 | }else if(node.left === null){
141 | node = node.right;
142 | return node;
143 | }else if(node.right == null){
144 | node = node.left;
145 | return node;
146 | }
147 | //有两个子节点
148 | var aux = findMinNode(node.right);
149 | node.key = aux.key;
150 | node.right = removeNode(node.right, aux.key);
151 | return node;
152 | }
153 |
154 | } else {
155 | return null;
156 | }
157 | }
158 | }
--------------------------------------------------------------------------------
/doc/dataStructure/array.md:
--------------------------------------------------------------------------------
1 | # JavaScript数据结构与算法--数组
2 | ## 创建和初始化数组
3 | ```javascript
4 | var daysOfWeek = new Array(); //{1}
5 | var daysOfWeek = new Array(7); //{2}
6 | var daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); //{3}
7 | var daysOfWeek = [];
8 | var daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
9 | 'Thursday', 'Friday', 'Saturday'];
10 | ```
11 | 因为数组这东西都用烂了,所以简单直接过例子。求斐波拉契数列前二十个数字
12 | ```javascript
13 | var fibonacci = []; //{1}
14 | fibonacci[1] = 1; //{2}
15 | fibonacci[2] = 1; //{3}
16 | for(var i = 3; i < 20; i++){
17 | fibonacci[i] = fibonacci[i-1] + fibonacci[i-2]; ////{4}
18 | }
19 | for(var i = 1; i b.age){
159 | return 1
160 | }
161 | return 0;
162 | }
163 | console.log(friends.sort(comparePerson));
164 |
165 | ```
166 | ## 结束语
167 |
168 | 数组这块比较简单,都是大家常用的东西,这里对于方法就不做过多演示了。
169 |
170 |
171 |
--------------------------------------------------------------------------------
/doc/dataStructure/tree.md:
--------------------------------------------------------------------------------
1 | ## JavaScript设计模式与算法--树
2 |
3 | ### 概念
4 | 非顺序数据结构我们之前学习的有散列表,现在,我们接着学习另一个非顺序数据结构,树。树是一种分层数据的抽象模型。现实生活中最常见的树的例子是家谱,或是公司的组织架构图
5 |
6 | 位于树顶部的节点叫做根节点,它没有父节点。节点分为内部节点和外部节点,至少有一个子节点的节点成为内部节点,一个子节点也没有的成为外部节点也叫做叶节点。
7 |
8 | 一个节点可以有祖先和后代,一个节点(除了根节点)的祖先包括父节点、祖父节点、曾祖 父节点等。一个节点的后代包括子节点、孙子节点、曾孙节点等。
9 |
10 | 节点的一个属性是深度,节点的深度取决于它的祖先节点的数量。树的高度取决于所有节点深度的最大值。
11 |
12 | ### 二叉树和二叉搜索树
13 | 二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这些定 义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。二叉树在计算机科学中的 应用非常广泛。
14 |
15 | 二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值, 在右侧节点存储(比父节点)大(或者等于)的值。
16 |
17 | 而我们今天主要研究的就是二叉搜索树
18 |
19 | ### 代码实现
20 |
21 | ```javascript
22 | function BinarySearchTree() {
23 | var Node = function (key) {
24 | this.key = key;
25 | this.left = null;
26 | this.right = null;
27 | };
28 |
29 | function insertNode(node, newNode) {
30 | if (newNode.key < node.key) {
31 | if (node.left) {
32 | node.left = newNode;
33 | } else {
34 | insertNode(node.left, newNode);
35 | }
36 | } else {
37 | node.right ? insertNode(node.right, newNode) : node.right = newNode;
38 | }
39 | }
40 |
41 |
42 | var root = null;
43 |
44 | this.insert = function (key) {
45 | var newNode = new Node(key);
46 | if (root == null) {
47 | root = newNode;
48 | } else {
49 | insertNode(root, newNode);
50 | }
51 | };
52 |
53 | //遍历二叉树,中序遍历(inOrderTraverse) 先序遍历(preOrderTraverse) 后序遍历(postOrderTraverse)
54 | function inOrderTraverseNode(root, callBack) {
55 | if (!root) {
56 | inOrderTraverseNode(root.left, callBack);
57 | callBack(root.key);
58 | inOrderTraverseNode(root.right, callBack);
59 | }
60 | };
61 |
62 | function preOrderTraverseNode(root, callBack) {
63 | if (!root) {
64 | callBack(root.key);
65 | inOrderTraverseNode(root.left, callBack);
66 | inOrderTraverseNode(root.right, callBack);
67 | }
68 | };
69 |
70 | function postOrderTraverseNode(root, callBack) {
71 | if (!root) {
72 | inOrderTraverseNode(root.left, callBack);
73 | inOrderTraverseNode(root.right, callBack);
74 | callBack(root.key);
75 | }
76 | };
77 | this.inOrderTraverse = function (callBack) {
78 | inOrderTraverseNode(root, callBack);
79 | };
80 |
81 | function preOrderTraverse(callBack) {
82 | preOrderTraverseNode(root, callBack);
83 | };
84 |
85 | function postOrderTraverse(callBack) {
86 | postOrderTraverseNode(root, callBack);
87 | };
88 |
89 | var minNode = function (node) {
90 | if (node) {
91 | while (node && node.left !== null) {
92 | node = node.left;
93 | return node.key;
94 | }
95 | return null;
96 | }
97 | };
98 | var findMinNode = function (node) {
99 | if (node) {
100 | while (node && node.left !== null) {
101 | node = node.left;
102 | return node;
103 | }
104 | return null;
105 | }
106 | };
107 | this.min = function () {
108 | return minNode(root);
109 | };
110 | var maxNode = function (node) {
111 | if (node) {
112 | while (node && node.right !== null) {
113 | node = node.right;
114 | }
115 | return node.key;
116 | }
117 | return null;
118 | };
119 | this.max = function () {
120 | return maxNode(root);
121 | };
122 | var searchNode = function (node, key) {
123 | if (node === null) {
124 | return false;
125 | }
126 | if (key < node.key) {
127 | return searchNode(node.left, key);
128 | } else if (key > node.key) {
129 | return searchNode(node.right, key);
130 | } else {
131 | return true;
132 | }
133 | };
134 | this.search = function (key) {
135 | return searchNode(root, key);
136 | };
137 |
138 | this.remove = function (key) {
139 | root = removeNode(root, key);
140 | };
141 | var removeNode = function (node, key) {
142 | if (node) {
143 | if (key < node.key) {
144 | node.left = removeNode(node.left, key);
145 | return node;
146 | } else if (key > node.right) {
147 | node.right = removeNode(node.right, key);
148 | return node;
149 | }else {
150 | //叶节点
151 | if(node.left === null && node.right === null){
152 | node = null;
153 | return node;
154 | }else if(node.left === null){
155 | node = node.right;
156 | return node;
157 | }else if(node.right == null){
158 | node = node.left;
159 | return node;
160 | }
161 | //有两个子节点
162 | var aux = findMinNode(node.right);
163 | node.key = aux.key;
164 | node.right = removeNode(node.right, aux.key);
165 | return node;
166 | }
167 |
168 | } else {
169 | return null;
170 | }
171 | }
172 | }
173 | ```
174 |
175 |
--------------------------------------------------------------------------------
/doc/basic_js/谈谈闭包.md:
--------------------------------------------------------------------------------
1 | # 妈妈再也不用担心面试官问我闭包了
2 |
3 | > 网上总结闭包的文章已经烂大街了,不敢说笔者这篇文章多么多么xxx,只是个人理解总结。各位看官瞅瞅就好,大神还希望多多指正。此篇文章总结与《JavaScript忍者秘籍》 《你不知道的JavaScript上卷》
4 |
5 | > 安利个人react技术栈+express+mongoose实战个人博客教程 [React-Express-Blog-Demo](https://github.com/Nealyang/React-Express-Blog-Demo)
6 |
7 | ## 前言
8 | 为什么我们需要理解并且掌握闭包,且不说大道理,就问你要不要成为JavaScript高手?不要?那你要不要面试找工作嘛。。。
9 |
10 | 再者,对于任何一个前端er或者JavaScript开发者来说,理解闭包可以看做是另一种意义上的重生。闭包是纯函数编程语言的一个特性,因为他大大简化复杂的操作,所以很容易在一些JavaScript库以及其他高级代码中找到闭包的使用。
11 |
12 | 一言以蔽之,闭包,你就得掌握。
13 |
14 | ## 谈谈闭包之前,我们先说说作用域
15 | 这里我们要说的作用域值得是词法作用域。词法作用域即为定义在词法阶段的作用域。换句话说,就是你写代码时将变量和块作用域写在哪里所决定的。因此在词法解析的时会保持作用域不变。(JavaScript引擎在运行JavaScript代码的时候大致经过分词/词法分析、解析/语法分析、代码生成三个步骤)。
16 |
17 | 老规矩,看代码(就是代码多~~)
18 |
19 | ```javascript
20 | function foo(a) {
21 | var b = a*2;
22 | function bar(c) {
23 | console.log(a,b,c);
24 | }
25 | bar(b*3);
26 | }
27 | foo(2);//2 4 6
28 | ```
29 | 这个例子中有三个逐级嵌套的作用域,如图:
30 | 
31 | ***截图来自《你不知道的JavaScript》***
32 |
33 | 部分一包含整个作用域也就是全局作用域。其中包含标识符:foo
34 |
35 | 部分二包含foo所创建的作用域,其中包含:a,bar和b
36 |
37 | 部分三包含bar所创建的作用域,其中包含:c
38 |
39 | 这些作用域气泡的包含关系给引擎提供了足够多的位置信息。在上面的代码中,引擎执行console.log的时候,并查找a,b,c。他首先在最里面的作用域,也就是bar(...)函数的作用域。引擎无法在这一层作用域中找到变量a,因此引擎会去上一级嵌套作用域foo(...)中查找,如果找到了,则即使用。
40 |
41 | 如果a,c 都存在作用域bar(...),foo(...)作用域中,console.log(...)即不需要到foo的外部作用域中去查找变量。
42 |
43 | 无论函数在哪里被调用,且无论他们如何被调用,他的词法作用域都只由函数被声明的位置决定的。词法作用域查找只会查找一级标识符,比如a,b和c。
44 |
45 | 简单理解词法作用域的概念,其实也就是我们常说的作用域,关于JavaScript中欺骗词法以及更多关于词法作用域的介绍,请翻阅《你不知道的JavaScript》。
46 |
47 | ## 闭包的概念
48 |
49 | 说到闭包的概念,这里还真的比较模糊,我们且看下各种经典书籍给出的概念
50 |
51 | ### 《JavaScript权威指南》中的概念
52 | 函数对象可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中成为闭包
53 |
54 | ### 《JavaScript权威指南》中的概念
55 | 闭包是指有权访问另一个函数作用域中的变量的函数。
56 |
57 | ### 《JavaScript忍者秘籍》中的概念
58 | 闭包是一个函数在创建时允许该自身函数访问并操作该自身函数以外的变量时所创建的作用域。
59 |
60 | ### 《你不知道的JavaScript》中的概念
61 | 闭包是基于词法作用域书写代码时所产生的自然结果。当函数记住并访问所在的词法作用域,闭包就产生了。
62 |
63 | ### 个人理解
64 | 闭包就是一个函数,一个可以访问并操作其他函数内部变量的函数。也可以说是一个定义在函数内部的函数。因为JavaScript没有动态作用域,而闭包的本质是静态作用域(静态作用域规则查找一个变量声明时依赖的是源程序中块之间的静态关系),所以函数访问的都是我们定义时候的作用域,也就是词法作用域。所以闭包才会得以实现。
65 |
66 | 我们常见的闭包形式就是a 函数套 b 函数,然后 a 函数返回 b 函数,这样 b 函数在 a 函数以外的地方执行时,依然能访问 a 函数的作用域。其中“b 函数在 a 函数以外的地方执行时”这一点,才体现了闭包的真正的强大之处。
67 |
68 |
69 |
70 | ## 实质性的问题
71 |
72 | ```javascript
73 | function outer() {
74 | var a = 2;
75 | function inner() {
76 | console.log(a);//2
77 | }
78 | inner();
79 | }
80 | outer();
81 | ```
82 | 基于词法作用域和查找规则,inner函数是可以访问到outer内部定义的变量a的。从技术上讲,这就是闭包。但是也可以说不是,因为用来解释inner对a的引用方法是词法作用域的查找规则,而这些规则只是闭包中的一部分而已。
83 |
84 | 下面我们将上面的代码修改下,让我们能够清晰的看到闭包
85 | ```javascript
86 | function outer() {
87 | var a = 2;
88 | function inner() {
89 | console.log(a);
90 | }
91 | return inner;
92 | }
93 | var neal = outer();
94 | neal();//2
95 | ```
96 | 可能是所有讲解闭包的博客中都用烂了的例子了。这里inner函数被正常调用执行,并且可以访问到outer函数里定义的变量a。讲道理,在outer函数运行后,通常函数整个内部作用域都会被销毁。
97 |
98 | 而闭包的神奇之处正是如此可以阻止垃圾回收这种事情的发生,事实上,内部作用域已然存在且拿着a变量,所以没有被回收。inner函数拥有outer函数内部作用域的闭包,使得该作用域能够一直存活,以供inner函数在之后的任何时间可以访问。
99 |
100 | inner()已然持有对该作用域的引用,而这个引用就被叫做闭包。
101 |
102 | 函数在定义时的词法作用域以外的地方被调用,闭包使得函数可以继续访问定义时的词法作用域。
103 |
104 | ***无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包***
105 | ```javascript
106 | var fn;
107 | function foo() {
108 | var a = 2;
109 | function baz() {
110 | console.log(a);
111 | }
112 | fn = baz;
113 | }
114 |
115 | function bar() {
116 | fn();
117 | }
118 | foo();
119 | bar();
120 | ```
121 |
122 | 上面的代码不做过多解释,挺简单,通过下面的代码,我们再说下闭包的三个有趣的概念
123 |
124 | ```javascript
125 | var outerValue = 'ninja';
126 | var later;
127 | function outFunction() {
128 | var innerValue = 'Neal';
129 | function innerFunction(param){
130 | console.log(outerValue,innerValue,param,tooLate);
131 | }
132 | later = innerFunction;
133 | }
134 | console.log('tooLate is',tooLate);
135 | outFunction();
136 | later('Nealyang');
137 | var tooLate = 'Haha';
138 | later('Neal_yang');
139 |
140 | //tooLate is undefined
141 | //ninja Neal Nealyang undefined
142 | //ninja Neal Neal_yang Haha
143 | ```
144 | 上面代码运行结果大家可以自行尝试。总之,从上面的代码中,我们可以看到闭包的有趣的三个概念
145 | - 内部函数的参数包含在闭包中
146 | - 作用域之外的所有变量、即便是函数声明之后的那些声明,也都包含在闭包中.
147 | - 相同作用域内,尚未声明的变量,不能进行提前引用
148 |
149 | ## 代码处处有闭包
150 |
151 | ```javascript
152 | function wait(message) {
153 | setTimeout( function timer() {
154 | console.log( message ); }, 1000 ); }
155 | wait( "Hello, closure!" );
156 | ```
157 | 如上的代码,一个很常见的定时器,但是timer函数具有涵盖wait作用域的闭包,因为此还保留对变量Message的引用。
158 |
159 | wait执行1s后,他的内部作用域并不会消失,timer函数依然保持有wait作用域的闭包。
160 |
161 | 深入到引擎内部原理中,内置的g工具函数setTimeout持有对一个参数的引用,引擎调用这个函数,在例子中就是内部的timer函数,而词法作用域在这个过程中保持完整。这就是闭包。
162 |
163 | 无论何时何地,如果将函数作为第一级值类型并到处传递,你就会看到闭包在这些函数中的使用。在定时器、事件监听、Ajax请求、跨窗口通信或者其他异步任务中,只要使用回调函数,就在使用闭包。
164 |
165 | ## 在经典的for循环中使用闭包
166 |
167 | ```javascript
168 | for (var i=1; i<=5; i++) {
169 | setTimeout( function timer() {
170 | console.log( i ); }, i*1000 ); }
171 | ```
172 |
173 | 如上for循环,大家都知道输出6,毕竟这个作用域中,我们只有一个i,所有的回调函数都是在这个for循环结束以后才执行的。
174 |
175 | 如果我们试图假设循环中的每一个迭代在运行时都会给自己捕获一个i的副本,但是根据作用域的工作原理,尽管循环中五个函数是在各个迭代中分别定义,但是他们都被封闭在共享的作用域中,因此还是只有一个i。
176 |
177 | 所以回到正题,我们需要使用闭包,在每一个循环中每一个迭代都让他产生一个闭包作用域。
178 |
179 | 所以我们代码修改如下:
180 | ```javascript
181 | for (var i=1; i<=5; i++) { (function() {
182 | setTimeout( function timer() {
183 | console.log( i ); }, i*1000 ); })(); }
184 | ```
185 |
186 | but!!!你也发现了,这样并不姓,不是IIFE会产生一个闭包的么?是的没错,但是如果这个IIFE产生的闭包作用域是可空的,那么将它封装起来又有什么意义呢?所以它需要点实质性的东西,让我们去使用。
187 |
188 | ```javascript
189 | for (var i=1; i<=5; i++) { (function(j) {
190 | setTimeout( function timer() {
191 | console.log( j ); }, j*1000 ); })( i ); }
192 | ```
193 |
194 | 当然,如上问题我们可以使用es6中的let来解决。但是这里就不做过多说明了。大家可以自行Google。
195 |
196 | ## 模块
197 | 这个部分比较简单好理解,因为闭包可以很好形成块级作用域,对内部变量有很好的隐藏。所以自然我们可以将其作为模块开发的手段。撇开如今的export、import不谈
198 |
199 | 直接看例子就好,操作比较常规
200 |
201 | ````javascript
202 | function foo() {
203 | var something = "cool";
204 | var another = [1, 2, 3];
205 | function doSomething() {
206 | console.log( something );
207 | }
208 | function doAnother() {
209 | console.log( another.join( " ! " ) );
210 | }
211 | return {
212 | doSomething:doSomething,
213 | doAnother:doAnother
214 | }
215 | }
216 | ````
217 |
218 | 简单说明下,doSomething和doAnother函数具有涵盖模块实例内部作用域的闭包。当通过返回一个含有属性引用的对象的方式来将函数传递到词法作用域外部,我们已经创造了可以观察和实践的 闭包条件。
219 |
220 | - 必须有外部的封闭函数,该函数必须至少被调用一次
221 | - 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有的状态。
222 |
223 | 当然,上面的代码我们还可以写成IIFE的形式。但是毕竟市场上讲解闭包的好文是在太多,这里我们就点到为止。
224 |
225 |
--------------------------------------------------------------------------------
/doc/dataStructure/Map&HashMap.md:
--------------------------------------------------------------------------------
1 | ## JavaScript数据结构与算法--字典和散列表
2 |
3 | ### 字典
4 |
5 | 集合表示一组互不相同的元素(不重复的元素)。在字典中,存储的是[键,值] 对,其中键名是用来查询特定元素的。字典和集合很相似,集合以[值,值]的形式存储元素,字 典则是以[键,值]的形式来存储元素。字典也称作映射。
6 |
7 | 当我们Set内部使用对象去实现的时候,其实和字典也就非常的类似了。
8 |
9 | ### 代码演示
10 | ```javascript
11 | function Dictionary() {
12 | var items = {};
13 | this.has = function (key) {
14 | return items.hasOwnProperty(key);
15 | };
16 | this.set = function (key,value) {
17 | if(this.has(key)) return false; //确保唯一性
18 | items[key] = value;
19 | return true
20 | };
21 | this.remove = function (key) {
22 | if(this.has(key)){
23 | delete items[key];
24 | return true;
25 | }else{
26 | return false
27 | }
28 | };
29 | function get(key) {
30 | return this.has(key) ? items[key] : undefined;
31 | }
32 | this.clear = function(){
33 | items = {};
34 | };
35 | this.size = function(){
36 | return Object.keys(items).length;
37 | };
38 | this.values = function(){
39 | var values = [];
40 | for (var k in items) {
41 | if (this.has(k)) {
42 | values.push(items[k]);
43 | }
44 | }
45 | return values;
46 | };
47 | }
48 | ```
49 |
50 | ### 散列表
51 |
52 | 散列算法的作用是尽可能快地在数据结构中找到一个值。在之前的章节中,你已经知道如果 要在数据结构中获得一个值(使用get方法),需要遍历整个数据结构来找到它。如果使用散列 函数,就知道值的具体位置,因此能够快速检索到该值。散列函数的作用是给定一个键值,然后 返回值在表中的地址。
53 |
54 | #### 代码演示
55 |
56 | ```javascript
57 | function HashTable() {
58 | var table = [];
59 | var loseloseHashCode = function (key) {
60 | var hash = 0;
61 | for(var i = 0,length = key.lenght;i-1 && position<=length){
37 | var node = new Node(element),
38 | current = head,
39 | previous,
40 | index = 0;
41 | if(position === 0){
42 | head = node;
43 | node.next = head;
44 | }else{
45 | while (index++ < position){
46 | previous = current;
47 | current = current.next;
48 | }
49 | node.next = current;
50 | previous.next = node;
51 | }
52 | }else{
53 | return false;
54 | }
55 | };
56 | this.removeAt = function (position) {
57 | if(position>-1 && position-1 && position<=length){
144 | var node = new Node(element),
145 | current = head,
146 | previous,
147 | index = 0;
148 | if(position === 0){
149 | if(!head){
150 | head = node;
151 | tail = node;
152 | }else{
153 | node.next = current;
154 | current.prev = node;
155 | head = node;
156 | }
157 | }else if(position === length){
158 | current = tail;
159 | current.next = node;
160 | node.prev = current;
161 | tail = node;
162 | }else{
163 | while (index++-1 && position 汤姆大叔真大牛
4 |
5 | 在我们日常编码中,编写可维护代码是非常重要的,编写可维护代码,意味着:
6 | - 可读性
7 | - 一致性
8 | - 可预测性
9 | - 团队代码风格一致
10 |
11 | ## 最小全局变量
12 | JavaScript是通过函数来管理作用域的。在函数内部申明的变量只能在函数内部使用,在函数外部不能使用。每一个JavaScript都有一个全局对象,当你在任意的函数外部访问this的时候可以访问到,你创建的每一个全局变量都成为这个全局对象的属性。浏览器中,这个全局对象便是window。
13 |
14 | ```javascript
15 | myglobal = 'Hello';
16 | console.log(myglobal);
17 | console.log(window.myglobal);
18 | console.log(window['myglobal']);
19 | console.log(this.myglobal);
20 | ```
21 | ## 全局变量的问题
22 | 全局变量最大的问题就是变量名冲突,造成不可以预计的后果。而在web页面中包含不是开发者缩写的代码也很常见
23 | - 第三方JavaScript库
24 | - 广告或者统计脚本
25 | - 不同类型的组件
26 |
27 | 所以一旦出现明明冲突了,那么这就gg了。所以尽可能的少用全局变量是非常有必要的。当然,我们有很多减少全局变量的策略,但是我觉得最主要的就是使用使用var来声明变量。当然,es6中的let和const出来后,var基本可以拜拜了,但是这里我们还是要讨论下var的
28 |
29 | 由于JavaScript的两个特征,不自觉的创建变量是出乎意料的容易,首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。
30 | ```javascript
31 | function sum(x, y) {
32 | // 不推荐写法: 隐式全局变量
33 | result = x + y;
34 | return result;
35 | }
36 | ```
37 |
38 | 另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。下面的片段中,a是本地变量但是b确实全局变量,这可能不是你希望发生的
39 |
40 | ```javascript
41 | // 反例,勿使用
42 | function foo() {
43 | var a = b = 0;
44 | // ...
45 | }
46 | ```
47 |
48 | ## 忘记var的副作用
49 | 隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。
50 | - 通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的
51 | - 无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的
52 |
53 | 这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的
54 |
55 | ## 单var形式
56 | 在函数顶部使用单var语句是比较有用的一种形式,其好处在于
57 |
58 | - 提供了一个单一的地方去寻找功能所需要的所有局部变量
59 | - 防止变量在定义之前使用的逻辑错误
60 | - 帮助你记住声明的全局变量,因此较少了全局变量
61 | - 少代码(类型啊传值啊单线完成)
62 |
63 | ```javascript
64 | function func() {
65 | var a = 1,
66 | b = 2,
67 | sum = a + b,
68 | myobject = {},
69 | i,
70 | j;
71 | // function body...
72 | }
73 | ```
74 |
75 | ## 预解析:var散布的问题
76 | JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于JavaScript,只 要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。
77 |
78 | ```javascript
79 | // 反例
80 | myname = "global"; // 全局变量
81 | function func() {
82 | alert(myname); // "undefined"
83 | var myname = "local";
84 | alert(myname); // "local"
85 | }
86 | func();
87 | ```
88 | 毕竟由于函数申明提升,也就是预解析。为了避免这种混 乱,最好是预先声明你想使用的全部变量。
89 | >为了完整,我们再提一提执行层面的稍微复杂点的东西。代码处理分两个阶段,第一阶段是变量,函数声明,以及正常格式的参数创建,这是一个解析和进入上下文 的阶段。第二个阶段是代码执行,函数表达式和不合格的标识符(为声明的变量)被创建。但是,出于实用的目的,我们就采用了”hoisting”这个概念, 这种ECMAScript标准中并未定义,通常用来描述行为。
90 |
91 | ## for循环
92 | 推荐写法
93 |
94 | ```javascript
95 | for (var i = 0, max = myarray.length; i < max; i++) {
96 | // 使用myarray[i]做点什么
97 | }
98 | ```
99 | 伴随着单var形式,你可以把变量从循环中提出来,就像下面这样:
100 | ```javascript
101 | function looper() {
102 | var i = 0,
103 | max,
104 | myarray = [];
105 | // ...
106 | for (i = 0, max = myarray.length; i < max; i++) {
107 | // 使用myarray[i]做点什么
108 | }
109 | }
110 | ```
111 | 最后一个需要对循环进行调整的是使用下面表达式之一来替换i++。
112 | ```javascript
113 | i = i + 1
114 | i += 1
115 | ```
116 | 原因是++和–-促进了“过分棘手(excessive trickiness)”。
117 |
118 | 还有两种变化的形式,其又有了些微改进,因为:
119 | - 少了一个变量(无max)
120 | - 向下数到0,通常更快,因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率
121 |
122 | ```javascript
123 | //第一种变化的形式:
124 |
125 | var i, myarray = [];
126 | for (i = myarray.length; i–-;) {
127 | // 使用myarray[i]做点什么
128 | }
129 |
130 | //第二种使用while循环:
131 |
132 | var myarray = [],
133 | i = myarray.length;
134 | while (i–-) {
135 | // 使用myarray[i]做点什么
136 | }
137 | ```
138 |
139 | ## for-in Loops
140 | for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”
141 |
142 | 从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在for-in中,属性列表的顺序(序列)是不能保证的。所以最好数组使用正常的for循环,对象使用for-in循环。
143 |
144 | 有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉从原型链上下来的属性。
145 |
146 | ```javascript
147 | // 对象
148 | var man = {
149 | hands: 2,
150 | legs: 2,
151 | heads: 1
152 | };
153 |
154 | // 在代码的某个地方
155 | // 一个方法添加给了所有对象
156 | if (typeof Object.prototype.clone === "undefined") {
157 | Object.prototype.clone = function () {};
158 | }
159 | ```
160 | 在这个例子中,我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方,在对象原型上增加了一个很有用的名叫 clone()的方法。此原型链是实时的,这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法,你需要应用hasOwnProperty()方法过滤原型属性。如果不做过滤,会导致clone()函数显示出来,在大多数情况下这是不希望出现的。
161 |
162 | ```javascript
163 | // 1.
164 | // for-in 循环
165 | for (var i in man) {
166 | if (man.hasOwnProperty(i)) { // 过滤
167 | console.log(i, ":", man[i]);
168 | }
169 | }
170 | /* 控制台显示结果
171 | hands : 2
172 | legs : 2
173 | heads : 1
174 | */
175 | // 2.
176 | // 反面例子:
177 | // for-in loop without checking hasOwnProperty()
178 | for (var i in man) {
179 | console.log(i, ":", man[i]);
180 | }
181 | /*
182 | 控制台显示结果
183 | hands : 2
184 | legs : 2
185 | heads : 1
186 | clone: function()
187 | */
188 | ```
189 | 另外一种使用hasOwnProperty()的形式是取消Object.prototype上的方法。像是:
190 | ```javascript
191 | for (var i in man) {
192 | if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
193 | console.log(i, ":", man[i]);
194 | }
195 | }
196 | ```
197 | 其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法,你可以使用局部变量“缓存”它。
198 | ```javascript
199 | var i, hasOwn = Object.prototype.hasOwnProperty;
200 | for (i in man) {
201 | if (hasOwn.call(man, i)) { // 过滤
202 | console.log(i, ":", man[i]);
203 | }
204 | }
205 | ```
206 | ## (不)扩展内置原型
207 | 增加内置的构造函数原型(如Object(), Array(), 或Function())挺诱人的,但是这严重降低了可维护性,因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的 JavaScript方法来持续不断地工作,而不是你另加的方法。
208 |
209 | ## switch模式
210 | ```javascript
211 | var inspect_me = 0,
212 | result = '';
213 | switch (inspect_me) {
214 | case 0:
215 | result = "zero";
216 | break;
217 | case 1:
218 | result = "one";
219 | break;
220 | default:
221 | result = "unknown";
222 | }
223 | ```
224 | - 每个case和switch对齐(花括号缩进规则除外)
225 | - 每个case中代码缩进
226 | - 每个case以break清除结束
227 | - 避免贯穿(故意忽略break)。如果你非常确信贯穿是最好的方法,务必记录此情况,因为对于有些阅读人而言,它们可能看起来是错误的
228 | - 以default结束switch:确保总有健全的结果,即使无情况匹配。
229 |
230 | ## 避免隐式类型转换
231 | JavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如:false == 0 或 “” == 0 返回的结果是true。为避免引起混乱的隐含类型转换,在你比较值和表达式类型的时候始终使用===和!==操作符。
232 |
233 | ## 避免 eval()
234 | 如果代码是在运行时动态生成,有一个更好的方式不使用eval而达到同样的目 标。例如,用方括号表示法来访问动态属性会更好更简单:
235 | ```javascript
236 | // 反面示例
237 | var property = "name";
238 | alert(eval("obj." + property));
239 |
240 | // 更好的
241 | var property = "name";
242 | alert(obj[property]);
243 | ```
244 |
245 | 使用eval()也带来了安全隐患,因为被执行的代码(例如从网络来)可能已被篡改。这是个很常见的反面教材,当处理Ajax请求得到的JSON 相应的时候。在这些情况下,最好使用JavaScript内置方法来解析JSON相应,以确保安全和有效。若浏览器不支持JSON.parse(),你可 以使用来自JSON.org的库。
246 |
247 | 同样重要的是要记住,给setInterval(), setTimeout()和Function()构造函数传递字符串,大部分情况下,与使用eval()是类似的,因此要避免。在幕后,JavaScript仍需要评估和执行你给程序传递的字符串:
248 | ```javascript
249 | // 反面示例
250 | setTimeout("myFunc()", 1000);
251 | setTimeout("myFunc(1, 2, 3)", 1000);
252 |
253 | // 更好的
254 | setTimeout(myFunc, 1000);
255 | setTimeout(function () {
256 | myFunc(1, 2, 3);
257 | }, 1000);
258 | ```
259 | 如果你绝对必须使用eval(),你 可以考虑使用new Function()代替。有一个小的潜在好处,因为在新Function()中作代码评估是在局部函数作用域中运行,所以代码中任何被评估的通过var 定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。
260 | ```javascript
261 | console.log(typeof un); // "undefined"
262 | console.log(typeof deux); // "undefined"
263 | console.log(typeof trois); // "undefined"
264 |
265 | var jsstring = "var un = 1; console.log(un);";
266 | eval(jsstring); // logs "1"
267 |
268 | jsstring = "var deux = 2; console.log(deux);";
269 | new Function(jsstring)(); // logs "2"
270 |
271 | jsstring = "var trois = 3; console.log(trois);";
272 | (function () {
273 | eval(jsstring);
274 | }()); // logs "3"
275 |
276 | console.log(typeof un); // number
277 | console.log(typeof deux); // "undefined"
278 | console.log(typeof trois); // "undefined"
279 | ```
280 | 另一间eval()和Function构造不同的是eval()可以干扰作用域链,而Function()更安分守己些。不管你在哪里执行 Function(),它只看到全局作用域。所以其能很好的避免本地变量污染。
281 |
282 | ## parseInt()下的数值转换
283 | 使用parseInt()你可以从字符串中获取数值,该方法接受另一个基数参数,这经常省略,但不应该。当字符串以”0″开头的时候就有可能会出问 题,例如,部分时间进入表单域,在ECMAScript 3中,开头为”0″的字符串被当做8进制处理了,但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果,总是指定基数参数。
284 |
285 | ## 空格
286 | 空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候,在逗号和句号后面会使用间隔。在JavaScript中,你可以按照同样的逻辑在列表模样表达式(相当于逗号)和结束语句(相对于完成了“想法”)后面添加间隔。
287 |
288 | 适合使用空格的地方包括:
289 |
290 | - for循环分号分开后的的部分:如for (var i = 0; i < 10; i += 1) {...}
291 | - for循环中初始化的多变量(i和max):for (var i = 0, max = 10; i < max; i += 1) {...}
292 | - 分隔数组项的逗号的后面:var a = [1, 2, 3];
293 | - 对象属性逗号的后面以及分隔属性名和属性值的冒号的后面:var o = {a: 1, b: 2};
294 | - 限定函数参数:myFunc(a, b, c)
295 | - 函数声明的花括号的前面:function myFunc() {}
296 | - 匿名函数表达式function的后面:var myFunc = function () {};
297 |
298 | 使用空格分开所有的操作符和操作对象是另一个不错的使用,这意味着在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=等前后都需要空格。
299 |
300 | ## 注释、 驼峰命名
301 | 不再多说
302 |
--------------------------------------------------------------------------------
/doc/basic_js/彻底明白this指向.md:
--------------------------------------------------------------------------------
1 | # this 其实很简单
2 |
3 | > 此文主要总结于《你不知道的JavaScript 上卷》,虽然讲解this的文章已经烂大街了,但是依旧希望,这篇文章可以帮助到那些还是对this有些疑惑的哥们
4 |
5 | ## 前言
6 | this关键字是JavaScript中最复杂的机制之一,它不是一个特殊的关键字,被自动定义在所有的函数作用域中。
7 |
8 | 老规矩,我们直接看例子:
9 | ```javascript
10 | function identify(){
11 | console.log(this.name)
12 | return this.name.toUpperCase();
13 | }
14 |
15 | function speak() {
16 | var gretting = 'Hello I am '+identify.call(this)
17 | console.log(gretting);
18 | }
19 |
20 | var me = {
21 | name:'Neal'
22 | }
23 |
24 | var you = {
25 | name:'Nealyang'
26 | }
27 | identify.call(me);
28 | identify.call(you);
29 |
30 | speak.call(me);
31 | speak.call(you);
32 | ```
33 | 关于运行结果大家可以自行运行查看,如果对于this是如何工作的这里我们还是存在疑惑,那么别急,我们后面当然会继续深入探讨下,这里,先说下关于this
34 | 的一些误解的地方
35 |
36 | ## 关于this的误解
37 | ### this 值得是它自己
38 | 通常新手都会认为this就是指向函数本身,至于为什么在函数中引用他自己呢,可能就是因为递归这种情况的存在吧。但是这里,我想说,this并不是指向函数本身的
39 | ```javascript
40 | function foo(num) {
41 | console.log("foo:"+num);
42 | this.count++;
43 | }
44 |
45 | foo.count = 0;
46 |
47 | for(var i = 0;i<10;i++){
48 | foo(i);
49 | }
50 |
51 | console.log(foo.count);
52 | ```
53 | 通过运行上面的代码我们可以看到,foo函数的确是被调用了十次,但是this.count似乎并没有加到foo.count上。也就是说,函数中的this.count并不是foo.count。
54 |
55 | 所以,这里我们一定要记住一个,就是函数中的this并不是指向函数本身的。上面的代码修改如下:
56 |
57 | ```javascript
58 | function foo(num) {
59 | console.log("foo:"+num);
60 | this.count++;
61 | }
62 |
63 | foo.count = 0;
64 |
65 | for(var i = 0;i<10;i++){
66 | foo.call(foo,i);
67 | }
68 |
69 | console.log(foo.count);
70 | ```
71 | 运行如上代码,此时我们就可以看到foo函数中的count的确已经变成10了
72 |
73 | ### this值得是他的作用域
74 | 另一种对this的误解是它不知怎么的指向函数的作用域,其实从某种意义上来说他是正确的,但是从另一种意义上来说,这的确是一种误解。
75 |
76 | 明确的说,this不会以任何方式指向函数的词法作用域,作用域好像是一个将所有可用标识符作为属性的对象,这从内部来说他是对的,但是JavaScript代码不能访问这个作用域“对象”,因为它是引擎内部的实现。
77 |
78 | ```javascript
79 | function foo() {
80 | var a = 2;
81 | this.bar();
82 | }
83 |
84 | function bar() {
85 | console.log( this.a );
86 | }
87 |
88 | foo(); //undefined
89 | ```
90 | 上面的代码不止一处错误,这里不做讨论,仅仅用于看代码,首先,视图this.bar()来视图访问bar函数,的确他做到了。虽然只是碰巧而已。然而,写下这段代码的开发者视图使用this在foo和bar的词法作用域中建立一座桥,是的bar可以访问foo内部变量作用域a。当然,这是不可能的,不可能使用this引用在词法作用域中查找东西。
91 |
92 | ## 什么是this
93 | 所以说了这么coder对this的误解,那么究竟什么是this呢。记住,this不是在编写时候绑定的,而是在运行时候绑定的上下文执行环境。this绑定和函数申明无关,反而和函数被调用的方式有关系。
94 |
95 | 当一个函数被调用的时候,会建立一个活动记录,也成为执行环境。这个记录包含函数是从何处(call-stack)被调用的,函数是 如何 被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的this引用。
96 |
97 | ## 彻底明白this到底值得什么鬼
98 |
99 | ### 调用点
100 | 为了彻底弄明白this的指向问题,我们还必须明白什么是调用点,即一个函数被调用的位置。考虑调用栈(即使我们到达当前执行位置而被d调用的所有方法堆栈)是非常重要的,我们关心的调用点就是当前执行函数的之前的调用
101 |
102 | ```javascript
103 | function baz() {
104 | // 调用栈是: `baz`
105 | // 我们的调用点是global scope(全局作用域)
106 |
107 | console.log( "baz" );
108 | bar(); // <-- `bar`的调用点
109 | }
110 |
111 | function bar() {
112 | // 调用栈是: `baz` -> `bar`
113 | // 我们的调用点位于`baz`
114 |
115 | console.log( "bar" );
116 | foo(); // <-- `foo`的call-site
117 | }
118 |
119 | function foo() {
120 | // 调用栈是: `baz` -> `bar` -> `foo`
121 | // 我们的调用点位于`bar`
122 |
123 | console.log( "foo" );
124 | }
125 |
126 | baz(); // <-- `baz`的调用点
127 | ```
128 | 上面代码大家简单感受下什么是调用栈和调用点,比较简单的东西。
129 |
130 | ### 来点规则,有规可寻
131 | 我们必须考察调用点,来判断下面即将要说的四中规则哪一种适用。先独立解释下四中规则的每一种,然后再来说明下如果多种规则适用调用点时他们的优先级。
132 |
133 | #### 默认绑定
134 | 所谓的默认绑定,就是独立函数的调用形式。
135 |
136 | ```javascript
137 | function foo() {
138 | console.log( this.a );
139 | }
140 |
141 | var a = 2;
142 |
143 | foo(); // 2
144 | ```
145 | 为什么会是2呢,因为在调用foo的时候,JavaScript对this实施了默认绑定,所以this就指向了全局对象。
146 |
147 | 我们怎么知道这里适用 默认绑定 ?我们考察调用点来看看foo()是如何被调用的。在我们的代码段中,foo()是被一个直白的,毫无修饰的函数引用调用的。没有其他的我们将要展示的规则适用于这里,所以 默认绑定 在这里适用。
148 |
149 | 需要注意的是,对于严格模式来说,默认绑定全局对象是不合法的,this被置为undefined。但是一个很微妙的事情是,即便是所有的this绑定规则都是基于调用点的,如果foo的内容没有严格模式下,默认绑定也是合法的。
150 |
151 | #### 隐含绑定
152 | 调用点是否有一个环境对象,也成为拥有者和容器对象。
153 | ```javascript
154 | function foo() {
155 | console.log( this.a );
156 | }
157 |
158 | var obj = {
159 | a: 2,
160 | foo: foo
161 | };
162 |
163 | obj.foo(); // 2
164 | ```
165 | foo被申明,然后被obj添加到其属性上,无论foo()是否一开始就在obj上被声明,还是后来作为引用添加(如上面代码所示),都是这个 函数 被obj所“拥有”或“包含”。
166 |
167 | 这里需要注意的是,只有对象属性引用链的最后一层才影响调用点
168 |
169 | ```javascript
170 | function foo() {
171 | console.log( this.a );
172 | }
173 |
174 | var obj2 = {
175 | a: 42,
176 | foo: foo
177 | };
178 |
179 | var obj1 = {
180 | a: 2,
181 | obj2: obj2
182 | };
183 |
184 | obj1.obj2.foo(); // 42
185 | ```
186 | ##### 隐含绑定丢死
187 | this绑定最让人头疼的地方就是隐含绑定丢失了他的绑定,其实明确了调用位置,这个也不是难点。直接看代码
188 | ```javascript
189 | function foo() {
190 | console.log( this.a );
191 | }
192 |
193 | var obj = {
194 | a: 2,
195 | foo: foo
196 | };
197 |
198 | var bar = obj.foo; // 函数引用!
199 |
200 | var a = "oops, global"; // `a`也是一个全局对象的属性
201 |
202 | bar(); // "oops, global"
203 | ```
204 | 所以如上的调用模式,我们又退回到了默认绑定模式。
205 |
206 | 还能hold住,那么接着看代码:
207 | ```javascript
208 | function foo() {
209 | console.log( this.a );
210 | }
211 |
212 | function doFoo(fn) {
213 | // `fn` 只不过`foo`的另一个引用
214 |
215 | fn(); // <-- 调用点!
216 | }
217 |
218 | var obj = {
219 | a: 2,
220 | foo: foo
221 | };
222 |
223 | var a = "oops, global"; // `a`也是一个全局对象的属性
224 |
225 | doFoo( obj.foo ); // "oops, global"
226 | ```
227 | 参数传递,仅仅是一种隐含的赋值,而且因为我们是传递一个函数,他是一个隐含的引用赋值,所以最终结果和我们前一段代码一样。
228 |
229 | 所以,在回调函数中丢失this绑定是一件很常见的事情,但是还有另一种情况,接受我们回调的函数故意改变this的值。那些很受欢迎的事件处理JavaScript包就十分喜欢强制你的回调的this指向触发事件的DOM元素。
230 |
231 | 不管哪一种意外改变this的方式,你都不能真正地控制你的回调函数引用将如何被执行,所以你(还)没有办法控制调用点给你一个故意的绑定。我们很快就会看到一个方法,通过 固定 this来解决这个问题。
232 |
233 | ***如上,我们一定要清除的是引用和调用。记住,找this,我们只看调用,别被引用所迷惑***
234 |
235 | #### 明确绑定
236 |
237 | 在JavaScript中,我们可以强制制定一个函数在运行时候的this值。是的,call和apply,他们的作用就是扩充函数赖以生存的作用域。
238 |
239 | ```javascript
240 | function foo() {
241 | console.log( this.a );
242 | }
243 |
244 | var obj = {
245 | a: 2
246 | };
247 |
248 | foo.call( obj ); // 2
249 | ```
250 | 上面代码,我们使用foo,强制将foo的this指定为obj
251 |
252 | 如果你传递一个简单原始类型值(string,boolean,或 number类型)作为this绑定,那么这个原始类型值会被包装在它的对象类型中(分别是new String(..),new Boolean(..),或new Number(..))。这通常称为“boxing(封箱)”。
253 |
254 | 但是,单独的依靠明确绑定仍然不能为我们先前提到的问题,提供很好的解决方案,也就是函数丢失自己原本的this绑定。
255 |
256 | ##### 硬性绑定
257 | ```javascript
258 | function foo() {
259 | console.log( this.a );
260 | }
261 |
262 | var obj = {
263 | a: 2
264 | };
265 |
266 | var bar = function() {
267 | foo.call( obj );
268 | };
269 |
270 | bar(); // 2
271 | setTimeout( bar, 100 ); // 2
272 |
273 | // `bar`将`foo`的`this`硬绑定到`obj`
274 | // 所以它不可以被覆盖
275 | bar.call( window ); // 2
276 | ```
277 | 我们创建了一个函数bar(),在它的内部手动调用foo.call(obj),由此强制this绑定到obj并调用foo。无论你过后怎样调用函数bar,它总是手动使用obj调用foo。这种绑定即明确又坚定,所以我们称之为 硬绑定(hard binding)
278 |
279 | #### new 绑定
280 |
281 | 这个比较简单,当函数前面加入new关键字调用的时候,其实就是当做构造函数调用的。其内部其实完成了如下事情:
282 | - 一个新的对象会被创建
283 | - 这个新创建的对象会被接入原型链
284 | - 这个新创建的对象会被设置为函数调用的this绑定
285 | - 除非函数返回一个他自己的其他对象,这个被new调用的函数将自动返回一个新创建的对象
286 |
287 | ### 总结性来一波
288 | - 函数是否在new中调用,如果是的话this绑定的是新创建的对象
289 | ```javascript
290 | var bar = new Foo();
291 | ```
292 | - 函数是否通过call、apply或者其他硬性调用,如果是的话,this绑定的是指定的对象
293 | ```javascript
294 | var bar = foo.call(obj);
295 | ```
296 | - 函数是否在某一个上下文对象中调用,如果是的话,this绑定的是那个上下文对象
297 | ```javascript
298 | var bar = obj.foo();
299 | ```
300 | - 如果都不是的话,使用默认绑定,如果在严格模式下,就绑定到undefined,注意这里是方法里面的严格声明。否则绑定到全局对象
301 | ```javascript
302 | var bar = foo();
303 | ```
304 |
305 | ## 绑定例外
306 | 第一种情况就是将null和undefined传给call、apply、bind等函数,然后此时this采用的绑定规则是默认绑定
307 |
308 | 第二种情况这里举个例子,也是面试中常常会出现的例子
309 | ```javascript
310 | function foo() {
311 | console.log(this.a);
312 | }
313 | var a = 2;
314 | var o = {
315 | a:3,
316 | foo:foo
317 | }
318 | var p = {a:4};
319 | (p.foo = o.foo)();
320 | ```
321 | 如上调用,其实foo采用的也是默认绑定,这里我们需要知道的是,p.foo = o.foo的返回值是目标函数的引用,所以最后一句其实就是foo()
322 |
323 | ### es6中的箭头函数
324 |
325 | es6中的箭头函数比较简单,由于箭头函数并不是function关键字定义的,所以箭头函数不适用this的这四中规则,而是根据外层函数或者全局作用域来决定this
326 | ```javascript
327 | function foo() {
328 | // 返回一个arrow function
329 | return (a) => {
330 | // 这里的`this`是词法上从`foo()`采用
331 | console.log( this.a );
332 | };
333 | }
334 |
335 | var obj1 = {
336 | a: 2
337 | };
338 |
339 | var obj2 = {
340 | a: 3
341 | };
342 |
343 | var bar = foo.call( obj1 );
344 | bar.call( obj2 ); // 2, 不是3!
345 | ```
346 | 这里foo内部创建的箭头函数会自动获取foo的this。
347 |
348 | ### 来一道经典面试题吧
349 | - 第一题
350 | ```javascript
351 | var a=10;
352 | var foo={
353 | a:20,
354 | bar:function(){
355 | var a=30;
356 | console.log(this)
357 | return this.a;
358 | }
359 | };
360 | foo.bar()
361 | (foo.bar)()
362 | (foo.bar=foo.bar)()
363 | (foo.bar,foo.bar)()
364 | ```
365 | - 第二题
366 | ```javascript
367 | function t(){
368 | this.x=2;
369 | }
370 | t();
371 | console.log(window.x);
372 | ```
373 |
374 | - 第三题
375 | ```javascript
376 | var obj = {
377 | x: 1,
378 | y: 2,
379 | t: function() {
380 | console.log(this.x)
381 | }
382 | }
383 | obj.t();
384 |
385 | var dog={x:11};
386 | dog.t=obj.t;
387 | dog.t();
388 |
389 |
390 | show=function(){
391 | console.log('show'+this.x);
392 |
393 | }
394 |
395 | dog.t=show;
396 | dog.t();
397 | ```
398 | - 第四题
399 | ```javascript
400 | name = 'this is window';
401 | var obj1 = {
402 | name: 'php',
403 | t: function() {
404 | console.log(this.name)
405 | }
406 | };
407 | var dog1 = {
408 | name: 'huzi'
409 | };
410 |
411 | obj1.t();
412 |
413 | dog1.t = obj1.t;
414 |
415 | var tmp = dog1.t;
416 | tmp(); //this本来指向window
417 |
418 | (dog1.t = obj1.t)();
419 | dog1.t.call(obj1);
420 | ```
421 | - 第五题
422 |
423 | ```javascript
424 | var number=2;
425 | var obj={
426 | number:4,
427 | /*匿名函数自调*/
428 | fn1:(function(){
429 | var number;
430 | this.number*=2;//4
431 |
432 | number=number*2;//NaN
433 | number=3;
434 | return function(){
435 | var num=this.number;
436 | this.number*=2;//6
437 | console.log(num);
438 | number*=3;//9
439 | alert(number);
440 | }
441 | })(),
442 |
443 | db2:function(){
444 | this.number*=2;
445 | }
446 | }
447 |
448 | var fn1=obj.fn1;
449 |
450 | alert(number);
451 |
452 | fn1();
453 |
454 | obj.fn1();
455 |
456 | alert(window.number);
457 |
458 | alert(obj.number);
459 | ```
460 |
461 | ## 交流
462 |
463 | ***扫码关注我的个人微信公众号,分享更多原创文章。点击交流学习加我微信、qq群。一起学习,一起进步。共同交流上面的题目吧***
464 |
465 | 
466 |
467 | ---
468 |
469 | 欢迎兄弟们加入:
470 |
471 | Node.js技术交流群:209530601
472 |
473 | React技术栈:398240621
474 |
475 | 前端技术杂谈:604953717 (新建)
476 |
477 | ---
--------------------------------------------------------------------------------
/doc/basic_js/忍者级别的操作函数.md:
--------------------------------------------------------------------------------
1 | # 忍者级别的操作JavaScript函数
2 | > 从名字即可看书,此篇博客总结与《JavaScript忍者秘籍》。对于JavaScript来说,函数为第一类型对象。所以这里,我们主要是介绍JavaScript中函数的运用。
3 |
4 | ## 匿名函数
5 |
6 | 对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于JavaScript而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是:创建一个供以后使用的函数。
7 |
8 | 简单的举个例子如下:
9 |
10 | ```javascript
11 | window.onload = function() {
12 | alert('hello');
13 | }
14 | var templateObj = {
15 | shout:function() {
16 | alert('作为方法的匿名函数')
17 | }
18 | }
19 | templateObj.shout();
20 |
21 | setTimeout(function() {
22 | alert('这也是一个匿名函数');
23 | },1000)
24 | ```
25 |
26 | 上面的一个代码片段我就不做过多无用解释了,比较常规。
27 |
28 | ## 递归
29 |
30 | 递归,说白了,就是自己调用自己,或者调用另外一个函数,但是这个函数的调用树的某一个地方又调用了自己。所以递归,就产生了。
31 |
32 | ### 普通命名函数的递归
33 |
34 | 拿普通命名函数的递归最好的举例就是用最简单的递归需求:检测回文。
35 |
36 | 回文的定义如下:一个短语,不管从哪一个方向读,都是一样的。检测的工作当然方法多样,我们可以创建一个函数,用待检测的回文字符逆序生成出一个字符,然后检测二者是否相同,如果相同,则为回文字符。
37 |
38 | 但是这种方法并不是很有逼格,确切的说,代价比较大,因为我们需要分配并创建新的字符。
39 |
40 | 所以,我们可以整理出如下简洁的办法:
41 | - 单个和零个字符都是回文
42 | - 如果字符串的第一个字符和最后一个字符相同,并且除了两个字符以外,别的字符也满足该要求,那么我们就可以检测出来了这个是回文了
43 |
44 | ```javascript
45 | function isPalindrome(txt) {
46 | if(txt.length<=1){
47 | return true;
48 | }
49 | if(txt.charAt(0)!= txt.charAt(txt.length-1)) return false;
50 | return isPalindrome(txt.substr(1,txt.length-2));
51 | }
52 | ```
53 |
54 | 上面的代码我们并没有做txt的一些类型检测,undefined、null等。
55 |
56 | ### 方法中的递归
57 |
58 | 所谓的方法,自然离不开对象,直接看例子:
59 |
60 | ```javascript
61 | var ninja = {
62 | chirp:function(n) {
63 | return n>1?ninja.chirp(n-1)+'-chirp':'chirp';
64 | }
65 | }
66 | console.log(ninja.chirp(3))//chirp-chirp-chirp
67 | ```
68 |
69 | 在上述代码中,我们通过对象ninja.chirp方法的递归调用了自己。但是,因为我们在函数上s会用了非直接引用,也就是ninja对象的chirp属性,所以才能够实现递归,这也就引出来一个问题:引用丢失
70 |
71 | ### 引用丢失的问题
72 |
73 | 上面的示例代码,依赖于一个进行递归调用的对象属性引用。与函数的实际名称不同,因为这种引用可能是暂时的。
74 |
75 | ```javascript
76 | var ninja = {
77 | chirp:function(n) {
78 | return n>1?ninja.chirp(n-1)+'-chirp':'chirp';
79 | }
80 | }
81 | var samurai = {chirp:ninja.chirp};
82 | ninja = {};
83 |
84 | try{
85 | console.log(samurai.chirp(3) === 'chirp-chirp-chirp')
86 | }catch (err){
87 | if(err) alert(false);
88 | }
89 | ```
90 |
91 | 如上,我们把ninja属性上的方法赋值给了samurai,然后置空ninja,然后你懂得~这就是引用丢失的问题。
92 | 
93 | ***截图自《JavaScript忍者秘籍》***
94 |
95 | 通过完善之前对匿名函数的粗略定义,我们可以修复解决这个问题。在匿名函数中,我们不在使用显示的ninja引用。这里我们使用this(关于this的使用详解,请关注我的个人微信公众号:前端的全栈之路)。
96 |
97 | ```javascript
98 | var ninja = {
99 | chirp:function(n) {
100 | return n>1?this.chirp(n-1)+'-chirp':'chirp';
101 | }
102 | }
103 | ```
104 | 当函数作为方法被调用的时候,函数的上下文指的是该方法的对象。
105 |
106 | 使用this调用,可以让我们的匿名函数更加的强大且灵活。但是。。。
107 |
108 | ### 内联命名函数
109 |
110 | 上面我们解决了作为函数方法作为递归时候的一个完美操作。但实际上,不管是否进行方法递归,巧妙使用this都是我们应该所掌握的(关注微信公众号,早晚都给你说到)。
111 |
112 | 话说回来,其实这样写也还是有问题的,问题在于给对象定义方法的时候,方法名称是写死的,如果属性名称不一样,岂不是一样会丢失引用?
113 |
114 | 所以,这里我们采用另一种解决方案,给匿名函数起个名字吧!对的,肯定又人会说,我擦!那还是匿名函数么?嗯。。。好吧,那就不叫匿名函数了吧,叫内联函数~
115 |
116 | ```javascript
117 | var ninja = {
118 | chirp:function signal(n) {
119 | return n>1?signal(n-1)+'-chirp':'chirp';
120 | }
121 | }
122 | var samurai = {chirps:ninja.chirp};
123 | ninja = {};
124 |
125 | try{
126 | console.log(samurai.chirps(3) === 'chirp-chirp-chirp')
127 | }catch (err){
128 | if(err) alert(false);
129 | }
130 | ```
131 |
132 | 所以如上的解决办法,就完美解决了我们之前说到所有问题。内联函数还有一个很重要的一点,就是尽管可以给内联函数进行命名,但是这些名称只能在自身函数内部才可见。
133 |
134 | ## 将函数视为对象
135 |
136 | JavaScript中的函数和其他语言中的函数有所不同,JavaScript赋予了函数很多的特性,其中最重要的特性之一就是函数作为第一类型对象。是的,对象!
137 |
138 | 所以,我们可以给函数添加属性,甚至可以添加方法。
139 |
140 | ### 函数存储
141 | 有时候,我们可能需要存储一组相关但又独立的函数,事件回调管理是最为明显的例子。向这个集合添加函数时候,我们得知道哪些函数在集合中存在,否则不添加。
142 |
143 | ```javascript
144 | var store = {
145 | nextId:1,
146 | cache:{},
147 | add:function(fn) {
148 | if(!fn.id){
149 | fn.id = store.nextId++;
150 | return !!(store.cache[fn.id] = fn);
151 | }
152 | }
153 | }
154 |
155 | function ninja() {}
156 |
157 | console.log(store.add(ninja));
158 | console.log(store.add(ninja));
159 | ```
160 | 上述代码比较简单常规,也就不做过多解释。
161 |
162 | ### 自记忆函数
163 |
164 | 缓存记忆是构造函数的过程,这种函数能够记住先前计算的结果。通过避免重复的计算,极大地提高性能。
165 |
166 | #### 缓存记忆昂贵的计算结果
167 |
168 | 作为一个简单的例子,这里我来判断一个数字是否为素数。
169 |
170 | ```javascript
171 | function isPrime(value) {
172 | if(!isPrime.answers) isPrime.answers = {};
173 | if(isPrime.answers[value]!=null){
174 | return isPrime.answers[value]
175 | }
176 | var prime = value != 1;//1 不是素数
177 | for(var i = 2;i
223 |
224 |
225 |
226 |
227 |
242 |
243 |