大前端Web前端之路前端开发那些事

JavaScript常用设计模式

2017-03-18  本文已影响238人  oldSix_Zhu

设计模式有很多,不定时更新
这只是在下的一点浅见,若有不妥,请多指教
目录:
1.沙箱模式(模块模式/Module模式)
1.2 立即执行函数表达式(IIFE)
2.监听者模式
3.严格模式

1.沙箱模式(模块模式/Module模式)

什么是沙箱模式?
做iOS开发的都知道,每一个app都有一个沙盒,保存的是这个app的数据,外界其他的app是访问不到的,这样就很安全,隐私.
沙箱模式就是为了达到类似这样的目的产生的
----沙箱模式不会在外界暴露任何的全局变量,也就减少了全局变量重名的情况,不会造成全局变量污染
----沙箱中的所有数据,都是和外界完全隔离的,外界无法对其进行修改,达到安全的目的

如何实现沙箱模式?
利用函数可以构建作用域,其上级作用域不能直接访问下级作用域中的数据的特点
具体是用立即执行函数表达式(IIFE: Immediately Invoked Function Expression)

先看一个简单的例子:

<script>
    //普通模式1
    var num1 = 0;
    for(var i = 1;i <= 100; i++)
    {
        num1 += i;
    }
    console.log(num1);//5050

    //普通模式2
    function test() {
        var num2 = 0;
        for(var i = 1;i <= 100; i++)
        {
            num2 += i;
        }
        console.log(num2);//5050
    }
    test();

    //沙箱模式
    (function () {
        var num3 = 0;
        for(var i = 1;i <= 100; i++)
        {
            num3 += i;
        }
        console.log(num3);//5050
    })();

    console.log(num1);//还可以打印5050
    console.log(num2);//爆红,num2 is not defined
    console.log(num3);//爆红,num3 is not defined
</script>

----从这个例子可以看出沙箱模式的简单写法就是用一个IIFE把代码包起来即可
----相比于第一种写法,沙箱模式写法省了两个全局变量名( num1 与 i )和一个全局函数名( test )
----相比于第二种写法,功能是相同的,但省了一个全局函数名( test )

沙箱模式一般用在哪里?
1.我们自己可以书写功能独立的一些组件
(例如自己封装的js函数,在一个.js文件中,按照沙箱模式用一个立即执行函数包起来即可)
2.还可以应用在书写第三方框架,或者为第三方框架书写插件
(例如把jQuery对象代替window传进去就可以)
可以保证自己内部的成员变量不被外界修改

jQuery中的沙箱模式

1 2

jQuery中的沙箱模式是这样实现的:

    //jQuery中的沙箱模式
    (function (w) {
        var Cat = {
            eat:function () {
                console.log("吃");
            }
        }
        w.cat = w.$ = Cat;
    })(window)

    window.cat.eat();//吃
    window.$.eat();//吃
    cat.eat();//吃
    $.eat();//吃

还有一种写法:

    //第二种写法
    (function () {
        var Cat = {
            eat:function () {
                console.log("吃");
            }
        }
        window.cat = window.$ = Cat;
    })()

    window.cat.eat();//吃
    window.$.eat();//吃
    cat.eat();//吃
    $.eat();//吃

这两种写法都是给window增加了一个cat和$的属性,并指向同一个对象Cat

实际上第二种写法与第一种写法没有用法上的区别,只是逻辑上,概念上的区别,
意思是我们没有直接用外界的window全局对象,而是使用自己的形参w,
只不过在调用的时候把window作为实参传了进来,
实现了沙箱模式逻辑上与外界隔绝

在我们使用的时候,
如果我们需要对外界暴露一些属性或者方法,
就可以将这些属性或者方法添加到window全局对象上,
但是这个全局对象不可直接引用,因为引用会破坏沙箱原则,
所以我们用传参的形式把window对象传入沙箱内,
此时沙箱内使用window对象的时候,不会再去全局搜索window对象,
而是使用的沙箱内部定义的形参

1.2 立即执行函数表达式(IIFE: Immediately Invoked Function Expression)

稍微写一点IIFE
立即执行函数表达式也叫自调用匿名函数
特点是没有函数名,在页面加载完成时就执行一次,且内部的变量不会被外界访问到,正好符合沙箱模式

(function(){})();
(function(){}());
是两种JavaScript立即执行函数的常见写法

    <script>
        
        (function (w) {
            console.log(w);
        })("哈哈哈");
        
        (function (w) {
            console.log(w);
        }("哈哈哈"));
        
        //不用括号()也可以
        //比如+ - ! 等等其他奇葩符号,不过没人会这么用吧...
        !function (w) {
            console.log(w);
        }("哈哈哈");

        //三者都是打印哈哈哈
    </script>

不多写了,可以看看其他人的文章
<a href="http://www.jianshu.com/p/05ab2eacd11b">js中(function(){…})()立即执行函数写法理解</a>
<a href="http://www.jianshu.com/p/5b5de313015c">JavaScript IIFE</a>


2.监听者模式

和OC中的通知一样,类似于广播,通常用来处理一对多的关系

主要实现方式为:
设置一个监听者(对象),维护一个听众队列(对象数组),监听一个事件(函数)
当事件发生时,告知所有的听众,听众各自做出反应,
一个听众可以理解为是一个回调,告知听众即执行回调

先看个基本的实现:

<script>
    // 监听者
    var jianTingZhe = {
        // 听众列表
        listeners: [],
        // 此事件触发时,告知所有的听众执行方法
        sing: function() {
            this.listeners.forEach( function( listen ) {
                listen();
            });
        }
    }

    jianTingZhe.listeners.push( function() {
        console.log( '你问~我爱~你有~多深~' );
    } );
    jianTingZhe.listeners.push( function() {
        console.log( '我爱~你有~几分~' );
    } );
    jianTingZhe.listeners.push( function() {
        console.log( '月亮代表我的心' );
    } );
    // 触发事件,开始唱歌
    jianTingZhe.sing();

    //打印
    //你问~我爱~你有~多深~
    //我爱~你有~几分~
    //月亮代表我的心
</script>

再看一个与沙箱模式结合的例子:

<script>
    // 监听者
    var jianTingZhe = {
        // 听众列表
        listeners: {
            listener1: [],
            listener2: [],
            listener3: []
        },

        // sing1触发时,告知所有监听sing1的听众
        sing1: function() {
            this.listeners.listener1.forEach( function( listen ) {
                listen();
            });
        },

        // sing2触发时,告知所有监听sing2的听众
        sing2: function() {
            this.listeners.listener2.forEach( function( listen ) {
                listen();
            });
        },

        // sing3触发时,告知所有监听sing3的听众
        sing3: function() {
            this.listeners.listener3.forEach( function( listen ) {
                listen();
            });
        }
    };

    // 这是一个模块,整体可以认为是一个听众监听三个事件
    (function( w ) {
        // sing1听众
        jianTingZhe.listeners.listener1.push( function() {
            console.log( '你问~我爱~你有~多深~' );
        } );
        jianTingZhe.listeners.listener1.push( function() {
            console.log( '我爱~你有~几分~' );
        } );
        jianTingZhe.listeners.listener1.push( function() {
            console.log( '月亮代表我的心~' );
        } );

        // sing2听众
        jianTingZhe.listeners.listener2.push( function() {
            console.log( '今夜还吹着风~想起你好温柔~' );
        });
        jianTingZhe.listeners.listener2.push( function() {
            console.log( '有你的日子分外~的轻松~' );
        });

        // sing3听众
        jianTingZhe.listeners.listener3.push( function() {
            console.log( '因为爱情~不会轻易悲伤~' );
        });
    }( window ));

    // 这是另一个模块,整体可以认为是一个听众
    (function( w ) {
        // sing3听众
        jianTingZhe.listeners.listener3.push( function() {
            console.log( '所以一切都是幸福的模样~' );
        });
    }( window ));

    jianTingZhe.sing1();
    jianTingZhe.sing2();
    jianTingZhe.sing3();
    
    //打印
    //你问~我爱~你有~多深~
    //我爱~你有~几分~
    //月亮代表我的心~
    //今夜还吹着风~想起你好温柔~
    //有你的日子分外~的轻松~
    //因为爱情~不会轻易悲伤~
    //所以一切都是幸福的模样~
</script>

3.严格模式

我们都知道JS的语法是很松散的,怎么写都对😂,
比如函数后面没有;也可以执行
而严格模式是在ES5出现的,只要启用了严格模式,JS的语法就会变得规范,写得不规范会警告报错等

严格模式可以消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
增加运行速度;

用法:
在.js文件中,若要使用严格模式,则写一行'use strict'
就会告诉解析器下面的代码就必须规范了,要按照ES5以上来解析

例如保留字:
一旦出现Unexpected strict mode reserved word这样的错误说明你用保留字做了参数名了
es6-->implements, interface, let, package, private, protected, public, static, yield
es5-->class, enum, export, extends, import, super

再例如.js中以下代码:

'use strict'
a=123;

运行后会报错a没有被定义,而没有声明严格模式则不会报错


其他设计模式

比如工厂模式在我之前文章里写过了,就不再写了
JS函数的4种调用模式
还有很多不定时更新吧(∩_∩)

上一篇下一篇

猜你喜欢

热点阅读