在面向对象编程中,封装是一种重要的概念,它允许一个对象将私有和公有成员组织在单一名称下。所有面向对象的编程语言都支持封装,JavaScript作为其中之一,也不例外。本文将展示如何在JavaScript中通过创建一个名为CssManager的简单对象来实现封装,该对象有助于动态地添加、移除或交换样式表。
将按照以下步骤实现:
首先,将创建一个名为Managers的命名空间,它将包装对象。在JavaScript中,创建一个命名空间就像创建一个对象字面量一样简单。
        var Managers = {};
    
现在,可以将所有与Managers相关的类和对象包装在一个单一的命名空间中。
接下来,将创建一个名为CssManager的单例对象。
        Managers.CssManager = {};
    
在这对花括号之间的代码形成了对象的一部分。
然后,将为对象添加公有方法,用于添加、移除和交换样式表。在JavaScript中,对象的行为很像关联数组,即键值对数组。键是属性或方法。
创建一个名为addStyleSheet的方法,它接受两个参数:样式表元素ID和引用URL。
        Managers.CssManager = {
            addStyleSheet: function(id, url) {
                // id - link's id, url - link's href
            }
        };
    
在addStyleSheet()方法中,将执行以下操作:动态创建一个链接元素,设置其属性,然后将其添加到头部区域。
        Managers.CssManager = {
            addStyleSheet: function(id, url) {
                var newStyleSheet = document.createElement("link");
                newStyleSheet.setAttribute("rel", "stylesheet");
                newStyleSheet.setAttribute("type", "text/css");
                newStyleSheet.setAttribute("id", id);
                newStyleSheet.setAttribute("href", url);
                document.getElementsByTagName("head")[0].appendChild(newStyleSheet);
            }
        };
    
接下来,将添加另外两个方法。
        Managers.CssManager = {
            addStyleSheet: function(id, url) {
                var newStyleSheet = document.createElement("link");
                newStyleSheet.setAttribute("rel", "stylesheet");
                newStyleSheet.setAttribute("type", "text/css");
                newStyleSheet.setAttribute("id", id);
                newStyleSheet.setAttribute("href", url);
                document.getElementsByTagName("head")[0].appendChild(newStyleSheet);
            },
            removeStyleSheet: function(id) {
                var currentStyleSheet = document.getElementById(id);
                if (currentStyleSheet) {
                    currentStyleSheet.parentNode.removeChild(currentStyleSheet);
                }
            },
            swapStyleSheet: function(id, url) {
                this.removeStyleSheet(id);
                this.addStyleSheet(id, url);
            }
        };
    
在removeStyleSheet()方法中,查询特定的样式表元素(链接),并将其完全从DOM中移除。而在swapStyleSheet()方法中,调用另外两个方法来用新的样式表替换现有的样式表。
到目前为止,创建的所有方法都是公有的。为了本文的需要,想添加两个私有成员。第一个是一个变量,它引用document对象。
        var doc = document;
    
第二个是一个方法,它一次设置链接元素的多个属性,而不是多次调用setAttribute()。
        var setAttributes = function(attributes) {
            // ...
        };
    
与其他语言不同,不能轻易地在JavaScript中将成员标记为公有或私有。要创建私有成员,必须使用闭包。闭包有助于创建一个私有空间。这里不会看到闭包是什么,但让看看它们如何帮助创建私有成员。
让修改CssManager来应用一个闭包:
        Managers.CssManager = (function() {
            /*
            private space
            */
            return {
                // Public members
                addStyleSheet: function(id, url) {
                    var newStyleSheet = doc.createElement("link");
                    setAttributes(newStyleSheet, {
                        rel: "stylesheet",
                        type: "text/css",
                        id: id,
                        href: url
                    });
                    doc.getElementsByTagName("head")[0].appendChild(newStyleSheet);
                },
                removeStyleSheet: function(id) {
                    var currentStyleSheet = doc.getElementById(id);
                    if (currentStyleSheet) {
                        currentStyleSheet.parentNode.removeChild(currentStyleSheet);
                    }
                },
                swapStyleSheet: function(id, url) {
                    this.removeStyleSheet(id);
                    this.addStyleSheet(id, url);
                }
            };
        })();
    
所做的是创建一个看起来像自执行匿名函数的闭包,它返回之前添加的公有方法。结尾的一对括号使代码自动执行。return语句返回的所有成员都是公有的,而之前的成员则是私有的。
现在,让在私有空间中添加两个新成员:
        Managers.CssManager = (function() {
            var doc = document;
            var setAttributes = function(element, attributes) {
                for (var attribute in attributes) {
                    element[attribute] = attributes[attribute];
                }
            };
            return {
                // Public members
                addStyleSheet: function(id, url) {
                    var newStyleSheet = doc.createElement("link");
                    setAttributes(newStyleSheet, {
                        rel: "stylesheet",
                        type: "text/css",
                        id: id,
                        href: url
                    });
                    doc.getElementsByTagName("head")[0].appendChild(newStyleSheet);
                },
                removeStyleSheet: function(id) {
                    var currentStyleSheet = doc.getElementById(id);
                    if (currentStyleSheet) {
                        currentStyleSheet.parentNode.removeChild(currentStyleSheet);
                    }
                },
                swapStyleSheet: function(id, url) {
                    this.removeStyleSheet(id);
                    this.addStyleSheet(id, url);
                }
            };
        })();
    
CssManager已经准备好了!如果想动态地向页面添加一个样式表,调用addStyleSheet方法,传递一个唯一的ID和外部CSS文件的HREF,如下所示:
        Managers.CssManager.addStyleSheet("myStyleSheet", "Default.css");
    
同样,甚至可以移除它或用一个新的替换它:
        Managers.CssManager.removeStyleSheet("myStyleSheet");
        Managers.CssManager.swapStyleSheet("myStyleSheet", "Advanced.css");