Javascript 没有 private , public 访问权限设置的关键字,但是可以通过一定的技巧来模拟出相同的结果.
首先我们来看下面一行代码:
var i = (1, 2, 3, 4, 5);
变量 i 最后的结果为 5.
这是逗号操作符的结果,也就是说返回最后的一个值,小括号改变了这行代码的优先级,否则 var i = 1, 2, 3, 4, 5; 会报错缺少标识符.
var i = (1, 2, 3, 4, function(){ return 5 * 5;});
变量 i 最后的结果为 一个函数, 返回结果 25.
这就是Javascript 的灵活之处,能够赋值任意类型而不必提前声明.现在我们完全可以进行如下调用:
i();
alert( i() );
来获得返回25的一次方法调用.
我们继续, 变量 i 是通过赋值符来获取函数的引用的, 也就是说在等号右边的小括号运算完后返回的最后一个结果的引用还在,虽然我们无法显示调用,但它确实存在,如果要不通过变量的引用而调用呢?
(1, 2, 3, 4, function(){ alert(5 * 5);})()
上面的代码执行后,弹出一个消息框,显示25.
为了显示方便,我将上个例子的函数改为弹出消息框了.
两对小括号 () (); 前面一对表示返回一个结果,如果该结果为一个函数,由第二对小括号发生调用.
也就是通过前面一对括号发生匿名函数的引用,以便在下面进行引用.这就是对匿名函数的调用.
关于更多匿名函数的使用可以参考文尾的引用连接.
闭包产生的原因是因为作用域的不同,子作用域引用了父作用域的变量,而返回子作用域,父作用域按理来说执行完毕后该销毁掉了,只是子作用域一直存在,且一直握有父作用域的引用,所以才一直保留.
来看下面的代码
function parent() {
var a = 1;
function child(){
var b = 2;
alert(a);
alert(b);
}
}
父函数 parent 中包含了一个 child 子函数,在子函数中有一个对父函数中 a 变量的引用(输出其值).
我们来让父函数执行完后返回其声明的子函数
function parent() {
var a = 1;
function child(){
var b = 2;
alert(a);
alert(b);
}
return child;
}
var t = parent();
t();
在10行中, 我们执行了parent 函数,返回了在函数内部声明的函数 child,这时变量 t 持有该返回对象(此时是一个可以执行的函数)的引用,在11行代码中我们调用了它.结果分别输出了 1 和 2.
注意,输出 2, 是因为我们在子函数体内声明了一个变量,而输出 1, 我们在该函数体内并没有相应的定义变量 a ,而是发生了对父函数里的变量的引用,也就是说引用了父作用域的变量.
而此时又能能够完成输出的,也就是说变量 a 还存在.可是我们无法直接对其引用 (比如 parent.a),因为函数已经执行完毕,没有了其相应的引用,我们只能通过所返回的子函数的引用来进行访问.
假如我又在父函数中声明了其他的变量呢? 结果是一样的,子函数能够访问,而如果子函数并不返回相应的引用的话,我们根本无法从外部访问到.这就形成了闭包.
闭包能够干些什么呢?如果你有一个不想让外部随意修改的变量该怎么做?那就去使用闭包.
myObj = {}; //声明一个全局变量,它是一个window对象的属性(window.myObj)
(function(){
var i = 4;
myObj = { //引用全局变量,对其进行赋值
getI : function() { //get方法,一个函数
return i;
},
setI : function(val) { //set方法,限制值的设定
if(val > 100) {
alert("i connt > 100");
return;
}
i = val;
}
}
})(); //匿名函数的调用,由于也是一个函数,所以作为一个子作用域,在执行完之后销毁,避免代码污染
myObj.setI(5); //成功
myObj.setI(101); //失败
alert(myObj.getI());
alert(myObj.i); //错误,没有该属性
至此我们简单的实现了public 访问权限以及 private 访问权限 (也就给你想给你的,不给你不想给你的)
在页面中,我们通常使用 document.cookie 属性来访问,对其赋新值就会创建一个新的Cookie,一个Cookie通常具有五个属性:value (存储的值), date (UTC格式的时间,代表什么时间过期, domain (域,Cookie的所有者), Path (子目录).
而在平常的开发中,如果仅仅使用 document.cookie 属性进行访问,会很麻烦,因为只能向其赋值字符串,并且在读取后还要进行字符串切割,才能获取指定变量名称的值.document.cookie 读取时,返回的是所有赋值的值,而不包括过期时间,域之类的信息,只能再次独设置.
下面就附上代码,全部封装到Cookie全局对象中,暴露出几个方法.
Get : 放回指定所有cookie字符串.
Set : 设置cookie 字符串.
Clear : 清除所有cookie对象.
GetDayTime : 获取指定距今val天的Date对象.
Write : 写cookie.已重载.详见代码.
Query : 查询cookie. 已重载.详见代码.
Update : 修改cookie.
Delete : 删除cookie.
代码1-7
Cookie = {};
/*
* Date对象的setTime方法是设置距离1970-01-01以来的毫秒数,设置到对象里去,返回的是据那以后的毫秒数而不是原对象.
* 如果Cookie 不设置 expires 属性,或者expires 时间比本地时间少,那么将会在下一次浏览时过期.
* document.cookie 对象返回的是所有值的字符串形式,不包含 expires 或者其他.
*
*/
(function() {
var nDay = 24 * 60 * 60 * 1000;
var isString = function(v) {
return typeof v === "string";
}
var isArray = function(v) {
return Object.prototype.toString.apply(v) == "[object Array]";
}
var isObject = function(v) {
return v && typeof v == "object";
}
var isDate = function(v) {
return Object.prototype.toString.apply(v) == "[object Date]";
}
var getTime = function() {
return new Date().getTime();
}
var trim = function(val) {
return (val || "").replace(/^\s+|\s+$/g, "");
}
var tryEval = function(val) {
var Obj, e;
try {
Obj = eval(val);
} catch (e) {
Obj = val;
}
return Obj;
}
var ObjectToString = function(o) {
var tstr = "{";
for (var v in o) {
if (isArray(o[v])) {
tstr += v + ":" + ArrayToString(o[v]) + ",";
} else if (isObject(o[v])) {
tstr += v + ":" + ObjectToString(o[v]) + ",";
} else if (isString(o[v])) {
tstr += v + ":\"" + o[v].toString() + "\",";
} else {
tstr += v + ":" + o[v].toString() + ",";
}
}
return tstr.slice(0, -1) + "}";
}
var ArrayToString = function(o) {
var tstr = "[";
for (var v in o) {
if (isArray(o[v])) {
tstr += ArrayToString(o[v]) + ",";
} else if (isObject(o[v])) {
tstr += ObjectToString(o[v]) + ",";
} else {
tstr += o[v].toString() + ",";
}
}
return tstr.slice(0, -1) + "]"; ;
}
Cookie = {
Get: function() {
return document.cookie;
},
Set: function(val) {
document.cookie = val;
},
Clear: function() {
var temp = this.Query();
var o;
for (o in temp) {
this.Delete(o);
}
},
GetDayTime: function(val) {
var texpires = new Date();
texpires.setTime(texpires.getTime() + val * nDay);
return texpires;
},
Write: function() {
/*
* Cookie.Write(Object); 写入对象,名称为main;
* Cookie.Write(varname, Object); varname:变量名, Object:写入对象;
* Cookie.Write(Object, Date); Object:写入对象, Date:过期时间;
* Cookie.Write(varname, Object, Date); varname:变量名, Object:写入对象, Date:过期时间;
* Cookie.Write(varname, Object, Date, Domain, Path); varname:变量名, Object:写入对象, Date:过期时间, Domain:域, Path: 子目录;
*/
if (arguments.length == 1) {
var tvalue = arguments[0];
var tstr = "";
var texpires = new Date(); texpires.setTime(texpires.getTime() + 1 * nDay);
if (isArray(tvalue)) {
tstr = ArrayToString(tvalue);
} else if (isObject(tvalue)) {
tstr = ObjectToString(tvalue);
} else {
tstr = tvalue.toString();
}
tstr = "main=" + escape(tstr) + ";expires=" + texpires.toGMTString() + ";";
} else if (arguments.length == 2) {
var tname, tvalue, texpires, tstr = "";
if (isDate(arguments[1])) {
tname = "main";
tvalue = arguments[0];
texpires = arguments[1];
} else {
tname = arguments[0];
tvalue = arguments[1];
texpires = new Date(); texpires.setTime(texpires.getTime() + 1 * nDay);
}
if (isArray(tvalue)) {
tstr += ArrayToString(tvalue);
} else if (isObject(tvalue)) {
tstr += ObjectToString(tvalue);
} else {
tstr = tvalue.toString();
}
tstr = tname + "=" + escape(tvalue) + ";expires=" + texpires.toGMTString() + ";";
} else if (arguments.length == 3) {
var tname = arguments[0], tvalue = arguments[1], texpires = arguments[2], tstr = "";
if (isArray(tvalue)) {
tstr = ArrayToString(tvalue);
} else if (isObject(tvalue)) {
tstr = ObjectToString(tvalue);
} else {
tstr = tvalue.toString();
}
tstr = tname + "=" + escape(tvalue) + ";expires=" + texpires.toGMTString() + ";";
} else if (arguments.length == 5) {
var tname = arguments[0], tvalue = arguments[1], texpires = arguments[2], tdomain = arguments[3], tpath = arguments[4], tstr = "";
if (isArray(tvalue)) {
tstr = ArrayToString(tvalue);
} else if (isObject(tvalue)) {
tstr = ObjectToString(tvalue);
} else {
tstr = tvalue.toString();
}
tstr = tname + "=" + escape(tvalue) + ";expires=" + texpires.toGMTString() + ";domain=" + tdomain + ";path=" + tpath + ";";
}
alert(tstr);
this.Set(tstr);
},
Query: function() {
/*
* Cookie.Query(); 返回所有Cookie值组成的Object;
* Cookie.Query(string); 返回指定名称的Object; 失败则返回 undefined;
* Cookie.Query(string, Object); 为指定对象写入指定名称的Object,并返回; 失败则返回 undefined;
*/
var tname = tvalue = "", tright = -1;
var tstr = this.Get();
var tObj = {};
if (arguments.length == 0) {
var i = 0;
do {
tname = trim(tstr.slice(i, tstr.indexOf("=", i)));
tright = tstr.indexOf(";", i);
if (tright == -1) {
tvalue = unescape(tstr.slice(tstr.indexOf("=", i) + 1, tstr.length));
} else {
tvalue = unescape(tstr.slice(tstr.indexOf("=", i) + 1, tright));
}
tObj[tname] = tryEval(tvalue);
i = tstr.indexOf(";", i) == -1 ? -1 : tstr.indexOf(";", i) + 1;
} while (i != -1);
} else {
tname = arguments[0];
if (tstr.indexOf(tname) == -1) return undefined;
var i = tstr.indexOf(tname);
tname = trim(tstr.slice(i, tstr.indexOf("=", i)));
tright = tstr.indexOf(";", tstr.indexOf(tname)) == -1 ? tstr.length : tstr.indexOf(";", tstr.indexOf(tname));
tvalue = unescape(tstr.slice(tstr.indexOf(tname) + tname.length + 1, tright));
if (arguments.length == 1) {
tObj = tryEval(tvalue);
} else if (arguments.length == 2) {
tObj = arguments[1];
tObj[tname] = tryEval(tvalue);
}
}
return tObj;
},
Update: function() {
return this.Write.apply(this, arguments);
},
Delete: function() {
if (arguments.length == 1) {
var varname = arguments[0];
if (this.Query(varname)) {
this.Update(varname, "", new Date(1970, 01, 01));
}
}
}
}
其中有一个从字符串eval 成对象的执行,以及从Object 或者 Array 对象获得对应字符串形式的功能函数,模拟了一些JSON的操作.当然,并不能存储所有的JavaScript 对象,仅仅满足一部分,我已经感觉够用了.
个人理解有限,请各位多多指教.
Javascript的匿名函数 :
http://dancewithnet.com/2008/05/07/javascript-anonymous-function/
Javascript的闭包 :
http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html
Cookie 文件的格式 :
http://www.cnblogs.com/sephil/archive/2008/05/06/cookiefmt.html