在面向对象编程中,封装是一种重要的概念,它允许一个对象将私有和公有成员组织在单一名称下。所有面向对象的编程语言都支持封装,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");