65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 设计模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (16投票s)

2013年8月13日

CPOL

3分钟阅读

viewsIcon

60223

JavaScript 现已成为客户端 Web 开发的明显语言。从一开始,浏览器就引入了各种支持访问和修改文档对象模型组件的功能。jQuery 已经将大多数原生功能抽象成新的结构,现在像 KnockOUT 和 BackBone 这样的渐进式库正在更进一步。

然而,如果原生 JavaScript 没有被正确处理,就会出现一些问题。

但是,如果原生 JavaScript 没有得到适当处理,就会存在一些问题。

  • 对于变量声明,如果我们忘记了 "var",它就会变成一个全局变量。
  • 如果两个函数在不同的 JavaScript 文件中具有相同的名称,则在页面中最后一个包含的文件中的函数将保留下来,而其他函数将丢失。
这些问题在较小的项目中可以忽略,但在有多个开发人员参与的大型应用程序中,它们可能会导致问题。
  • 没有 "var" 的变量将在其他 JavaScript 文件的范围内,混合了这些文件中的变量。如果存在大量这样的变量分散在多个文件中,将很难找到用于特定用途的精确变量,通常一个变量会覆盖所有其他变量并崩溃整个状态。
  • 具有类似功能的函数在不同的文件(用于不同的目的)中具有相同的名称会发生冲突。代码审查只能识别同一文件中的问题,但如果存在许多此类文件,则查找所有同名函数会造成问题。
因此,为了解决此类问题,JavaScript 中引入了某些设计模式。

模拟类

将所有全局函数名称和变量更改为标签,并将它们置于单个函数范围内。这创建了一个逻辑类;将属性和功能绑定在一起。

        // Class wrapping
        var anchorChange = {
            config : {
                    colors: ["#F12", "#999"]
            },
             
            alterColor:  function (link, newColor) {
                link.style.backgroundColor = newColor;
            },

            init : function() {                
                var self = this;  // assign reference to current object to 'self'

                var anchors = document.getElementsByTagName("a");
                var size = anchors.length;
                
                for (var i = 0; i < size; i++) {
                    anchors[i].color = self.config.colors[i];
                    anchors[i].onclick  = function () {
                        self.alterColor(this, this.color); // this is bound to anchor
                        return false;     // prevent navigation
                    };
                }
            }
        };

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>

            <!-- Now call the function, not in the head but below the controls -->
            <script type="text/javascript">
                anchorChange.init();
            </script>
        </div>
    </form>

这种包装器虽然提供了防止函数和变量混淆的安全性,但它并不能区分公共和私有函数。

模块模式

明确区分公共和私有项目。在直接访问中定义的任何内容都是私有的,在 'return' 块中定义的任何内容都是公共的。

        /*  Module Pattern  */
        var anchorChangeMod = function () {
            // private property
            config = {
                    colors: ["#F12", "#999"]
            }

            // private method, available within 'anchorChange' class not outside
            function alterColor(link, newColor) {
                link.style.backgroundColor = newColor;
            }

            return {
                // all public code inside 'return' block
                changeColor: function(link, newColor) {
                    // Calls private method inside public method
                    alterColor(link, newColor);
                },

                init : function() {
                    var self = this;  // assign reference to current object to 'self'

                    var anchors = document.getElementsByTagName("a");
                    var size = anchors.length;

                    for (var i = 0; i < size; i++) {
                        anchors[i].color = config.colors[i];
                        anchors[i].onclick = function () {
                            // this is bound to anchor.. 
                            anchorChangeMod.changeColor(this, this.color); 
                            return false;     // prevent navigation
                        }
                    }
                }
            };
        }();

 

<form id="form1" runat="server">
        <div>
            <div>
                <ul>
                    <li><a href="www.nitinsingh.com">My Website</a></li>
                    <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
                </ul>
            </div>
        </div>
        <script type="text/javascript">
            // No constructor, directly call object
            anchorChangeMod.init();
        </script>
    </form>

揭示模块模式

在这种模式中,整个结构在类块中声明,然后在类的末尾,有一个 'return' 块,其中函数被添加到公开的委托中。

/*  Revealing Module  */
        function anchorChangeRev() {

            // private property
            config = {
                colors: ["#F12", "#999"]
            }

            // private method, available within 'anchorChange' class not outside
            function alterColor(link, newColor) {
                link.style.backgroundColor = newColor;
            }

            var changeColor = function (link, newColor) {
                alterColor(link, newColor);
            }

            var init = function () {
                var self = this;  // assign reference to current object to 'self'
                var anchors = document.getElementsByTagName("a");
                var size = anchors.length;

                for (var i = 0; i < size; i++) {
                    anchors[i].color = config.colors[i];

                    anchors[i].onclick = function () {
                        anchorChangeRev().cc(this, this.color); // this is bound to anchor
                        return false;     // prevent navigation
                    }
                }
            }

            return {
                // delegates to actual methods are marked as public
                cc: changeColor,
                init: init
            }
        };

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>
            <script type="text/javascript">
                anchorChangeRev().init();
            </script>
        </div>
    </form>

这里第一个是通过其调用函数的变量,第二个是函数委托。这两个函数都将在最终对象中获得公共范围。

懒函数

如果我们有一些功能只需要运行一次(通常用于初始化),而其他功能则需要重复运行,我们应用懒函数。

我们创建一个类,并在其中声明仅执行一次的代码。然后,在相同的范围内,使用要多次运行的函数声明相同的类。

/*  Lazy loading   */
        var anchorChangeLazy = function () {

            // private property
            var config = {
                colors: ["#F12", "#999"]
            };

            var anchors = document.getElementsByTagName("a");
            var size = anchors.length;

            for (var i = 0; i < size; i++) {
                anchors[i].color = config.colors[i];

                anchors[i].onclick = function () {
                    anchorChangeLazy().changeColor(this, this.color); // this is bound to anchor
                    return false;     // prevent navigation
                }
            }

            // Redefine function to hold just the changeColor function
            // which will be invoked multiple times
            anchorChangeLazy = function () {
                return {
                    changeColor: function (link, newColor) {
                        link.style.backgroundColor = newColor;
                    }
                };
            };
        };

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>
            <script type="text/javascript">
                anchorChangeLazy();         // just the main function, no subfunction
            </script>
        </div>
    </form>

现在,每当调用类对象时,初始代码中的绑定只执行一次,而 changeColor 处理程序(在新类代码中)可供永久使用。

自定义对象构造函数

我们可以将函数添加到任何对象的 'prototype' 属性。这会将定义注入到已经声明的对象中。因此,初始化函数可以包装在构造函数中,而其他函数则添加到原型绑定中。

/*  Custom object constructor  */
var anchorChangeCusCon = function() {
    this.init();
};

// 'prototype' means class definition
//  any properties can be added to this
anchorChangeCusCon.prototype.config = {
    colors: ["#F12", "#999" ]
};

anchorChangeCusCon.prototype.changeColor = function(link, newColor) {
    link.style.backgroundColor = newColor;
};

anchorChangeCusCon.prototype.init = function() {
    var self = this;

    var anchors = document.getElementsByTagName("a");
    var size = anchors.length;

    for (var i = 0; i < size; i++) {
        anchors[i].color = self.config.colors[i];

        anchors[i].onclick   = function() {
            self.changeColor(this, this.color);
            return false;
        }
    }
}


// Call of this function
var anchor = new anchorChangeCusCon();
// anchor.init();  Not needed, since this is invoked within the constructor

因此,我们已经发现了如何在自己的 JavaScript 类中为抽象和数据隐藏提供一些 OOP 原则。

享受美化你的客户端代码吧 :)

© . All rights reserved.