文章标签 ‘JavaScript脚本’

没有评论讨论闭包传入参数:window & undefined

2010年5月21日

引言

最常见的闭包 (Closure) 范式大家都很熟悉了:

(function() {
// ...
})();

很简单,大家都在用。但是,我们需要了解更多。
首先,闭包是一个匿名函数 (Anonymous function), 即是 (function() {}) 这部分。之所以要给 function 添加括弧是为了让它形成一个表达式 (expression), 有了表达式,并且确定它的类型是个函数 (Function 实例), 就可以直接调用它。所以,后面的一对括弧是可以工作的,它的意义是:我要调用 (call) 这个函数。

既然是函数调用,那就可以像一般的函数那样,在调用时传入参数。这就是本次讨论的话题。

传入 window 参数

 

(function(win) {
// ...
})(window);

这样做最直观的好处是书写便利:少写几个字。你可以在闭包内任何地方使用 win, 它都会指向 window 对象。另外,它有利于压缩减少最终代码的体积,经过压缩后 (如 Google Closure Complier), 所有的 win 都会被替换成形如 a 这样的简单变量。win 用得越多,减少的字节数也越多。

不过,便利的同时也会带来陷阱。在 IE 上,window 总是指向当前窗口对象,这个没有问题,但是在某些场景下,使用闭包内的 win 变量会导致拒绝访问错误 (Access denied). 重现方式大致是这样的:当页面引用其他域名的脚本,并且该脚本调用了闭包内的 window.document, 而且这个闭包代码是来自另一个域名的脚本。在这种情况下,使用 win 会保持对 window 最早的引用,通过另一个域的脚本访问 win 会导致 IE 认为脚本产生了跨越冲突,从而拒绝了对 win.document 的访问。解决办法是不使用形参 win, 而是直接使用 window. 需要说明的是,给闭包传入 document 也会导致 IE 出现同样的问题。

传入 undefined

其实把 undefined 作为形参就,实参就可以不用传了,因为 JavaScript 中访问未传入的参数就会得到 undefined. 因此,你可以这样写:

(function(undefined) {
// ...
})();

和上面的讨论一样,你可以在闭包内任何地方使用 undefined, 可以少写几个字(如果把 undefined 换成更短的名字),也可以在减少压缩后体积。

另一个的优势是,你可以认为它是个变量,把它当变量来使用,它的值恒等于 (===) 真正的 undefined. 当外部代码意外地定义了 undefined 的时候——不常见,但确实可能会发生——你可以正常地使用真正的 undefined, 而不会被外部的 undefined 意外影响. 这是由 JavaScript 作用域规则决定的。
无论是否使用这个 undefined 参数,都应该避免使用 undefined 的字符串常量,如:

if(typeof myVar === 'undefined') {
// bad part...
}

因为如果你把字符串写错了,机器不会告诉你,而且会产生一个难以检查出来的bug. 幸运的是,对于 JavaScript 来说,JsLint 可以帮你做这个校验。当 myVar 已定义的时候(通过形参或 var 声明),上面的代码改成这样会更易于调试:

if(myVar === undefined) {
// good part...
}

结论

从上面两个例子来看,我们建议不要传入 window, 但是可以安全地使用第二种方式 (写 undefined 形参);我们还要尽量避免使用字符串常量。
最后,最重要的是,这只是两个特定对象和类型的讨论,举一反三,你会更了解 JavaScript

转自阿里UED:http://ued.alipay.com/2010/05/using-window-and-undefined-as-parameter-in-closure/

6 条评论最近写日期操作的几个JS函数

2010年4月23日
//格式化函数 Date原型扩展
Date.prototype.format = function(format)
{
 var o = {

 "M+" : this.getMonth()+1, // month
 "d+" : this.getDate(),// day
 "h+" : this.getHours(), // hour
 "m+" : this.getMinutes(), // minute
 "s+" : this.getSeconds(), // second
 "q+" : Math.floor((this.getMonth()+3)/3), // quarter
 "S" : this.getMilliseconds() // millisecond
 }

 if(/(y+)/.test(format))
 format = format.replace(RegExp.$1,(this.getFullYear()+"").substr(4 - RegExp.$1.length));
 for(var k in o)
 if(new RegExp("("+ k +")").test(format))
 format = format.replace(RegExp.$1,RegExp.$1.length==1 ? o[k] :( "00"+ o[k]).substr((""+ o[k]).length));
 return format;
}
// 日期比较

function datecompare(startDate, endDate) {
 var re = /^(\d{4})(\-)(\d{1,2})(\-)(\d{1,2})$/;
 var d1 = new Date(startDate.replace(/-/g, "/"));
 var d2 = new Date(endDate.replace(/-/g, "/"));
 if (Date.parse(d1) - Date.parse(d2) == 0) {
 // window.alert("两个日期相等");
 return "1";
 }
 if (Date.parse(d1) - Date.parse(d2) < 0) {
 // window.alert("结束日期 大于 开始日期");
 return "2";
 }
 if (Date.parse(d1) - Date.parse(d2) > 0) {
 // window.alert("结束日期 小于 开始日期");
 return "3";
 }
}
//添加一天
function DateAddOneDay(now){
 return new Date(Date.parse(now) + (86400000 * 1))
}

没有评论即拷即用动画函数:透明度渐变、位置移动、尺寸变化

2010年3月31日

首先感谢一下 51js的 abeet 同学 给我们提取出这么简洁的函数
和大家分享一下吧 查看Demo
这三个函数适用于以下情况
1、在做前端页面时,只需要简单的动画效果,不想引入整个动画类,使用本函数。
2、在写js类时,需要用到简单的动画,为了降低藕合性,使用本函数。

使用方法如下:

var fade = function(element, transparency, speed, callback){……}
透明度渐变:transparency:透明度 0(全透)-100(不透);speed:速度1-100,默认为1
例<input onclick="fade('testDiv', 40)" type="button" value="透明度变化" />

var move = function(element, position, speed, callback){……}
//移动到指定位置,position:移动到指定left及top 格式{left:120, top:340}或{left:120}或{top:340};speed:速度 1-100,默认为10
例<input onclick="move('testDiv', {top:400})" type="button" value="垂直移动" />

var resize = function(element, size, speed, callback){……}
//长宽渐变:size:要改变到的尺寸 格式 {width:400, height:250}或{width:400}或{height:250};speed:速度 1-100,默认为10
例<input onclick="resize('testDiv', {height:300})" type="button" value="改变高度" />

核心代码分析
1、获取一个元素的透明度

如果是IE浏览器,则攻取最终样式的滤镜filter的值,再通过正则表达式获取到透明度滤镜alpha的值,如果不存在透明度滤镜值,则透明度为100。
如果是非IE浏览器,那么可以获取最终样式的opacity属性,

function getOpacity(elem){
var alpha;
if(navigator.userAgent.toLowerCase().indexOf('msie') != -1){
alpha=elem.currentStyle.filter.indexOf("opacity=") &gt;= 0?(parseFloat( elem.currentStyle.filter.match(/opacity=([^)]*)/)[1] )) + '':
'100';
}else{
alpha=100*elem.ownerDocument.defaultView.getComputedStyle(elem,null)['opacity'];
}
return alpha;
};

2、设置透明度
这个就简单了,不用管浏览器,通通的设置opacity及filter,
element.style.opacity = start / 100;
element.style.filter = ‘alpha(opacity=’ + start + ‘)’;
实际上这儿我偷了个懒,真正无懈可击的写法,应该是判断一下是否IE浏览器,因为在一些情况下可能使用多个滤镜,这时应该用正则替换alpha滤镜的值,而不是直接设置style.filter = ‘alpha(opacity=’ + start + ‘)’;

3、element.offsetWidth、element.offsetHeight与element.style.width、element.style.height的关系
只有在IE的border-content式盒模型情况下,这两个值才是相等的,即elementoffsetWidth==element.style.width+element.style.paddingLeft+element.style.paddingRight+element.style.borderLeftWidth+element.style.borderRightWidth
而我认为border-content式盒模型是符合修改元素尺寸时的心理预期的,对非border-content式盒模型情况下要作一个尺寸的修正。
在其它情况下应该对该值加以修正

if(!(navigator.userAgent.toLowerCase().indexOf('msie') != -1&&document.compatMode == 'BackCompat')){
//除了ie下border-content式盒模型情况外,需要对size加以修正
var CStyle=document.defaultView?document.defaultView.getComputedStyle(element,null):element.currentStyle;
if(typeof(size.width)=='number'){
size.width=size.width-CStyle.paddingLeft.replace(/D/g,'')-CStyle.paddingRight.replace(/D/g,'');
}
if(typeof(size.height)=='number'){
size.height=size.height-CStyle.paddingTop.replace(/D/g,'')-CStyle.paddingBottom.replace(/D/g,'');
}
}

有人认为以上的修正中默认了padding值的单位是px并且为整数,是不对的,
实际上经由我在ie及firefox下的测试,引用最终样式的padding值,必然是整数,并以px为单位。

4、关于把三个函数合并成一个通用动画函数
以上三个函数的主要逻辑都是一样的:
通过setInterval每隔一定时间修改元素的style属性,以达到动画效果。
所以,把三个函数合并成一个函数是可以的
只是需要在修改特定的属性时出于兼容性,必须作些处理,
因为不同的属性其值类型及单位不一样,不同浏览器下也有区别
如透明度 在非ie下为小数,在ie下是一个特别的滤镜设置
如颜色 一般是用16进制,并有#号前缀
预计这个通用的动画函数的参数会有点多,接口类下
function animator(element, interval, start, end, style, speed, tmp, callback){……}
说明
function animator(元素或元素ID, 计时器句柄,起始值,目标值,需修改的属性, 步进值(增量),值转换过滤方法(用于修改非普通递增递减形的数据,比如16进制颜色递增、前缀符号后缀单位修正), 回调函数)
function animator(‘testdiv’,'bgcolor’,'#336699′,’#aabbcc’,'backgroundColor’,2,changeColor,callback)
颜色修改方法示例(仅供参考,未测试)

changeColor=function(start,end,speed){
var _10to16 = function(color){
function tmp(index){
var tmp = color[index].toString(16);
return tmp.length == 1 ? "0" + tmp : tmp;
};
return "#" + tmp(0) + tmp(1) + tmp(2);
};
var x16to10 = function(color){
if(!/^#[0-9A-Fa-f]{3,6}$/i.test(color))retun color;
function tmp(index){
return color.charAt(index);
};
color = color.substring(1);
if(color.length == 3)
color = tmp(0) + tmp(0) + tmp(1) + tmp(1) + tmp(2) + tmp(2);
return [parseInt(tmp(0) + tmp(1), 16), parseInt(tmp(2) + tmp(3), 16), parseInt(tmp(4) + tmp(5), 16)];
};
var color = x16to10(start),
var end = x16to10(end),
var index = 3;
while(index--)
color[index] = color[index] < end[index] ? min(color[index] + speed, end[index]) : max(color[index] - speed, end[index]);
reutn color;
}

原文见:http://bbs.51js.com/viewthread.php?tid=86901

没有评论各浏览器中cookie限制

2010年3月19日
  Firefox Opera chrome IE6 IE7/IE8
最多key个数 50个 30个 53个 20个 50个
key个数超出时 随机删除旧的 先进先出方式删除旧的
单key字节数 4097 4051 4051 4096 5072
单key字节超出时 不进行写入操作
总字节数限制 204850 4997 本地无cookie 4096 10239
总字节超出后 不写入新的 - cookie无法读写
  • 注①:包括key及value,以及分号、等号
  • 注②:总字节数也受HTTP Server的设置影响;
    Apache用这2个参数调整:
    LimitRequestFieldSize 限制客户端发送的请求头的字节数 【默认 8190】
    LimitRequestLine 限制接受客户端发送的HTTP请求行的字节数【默认 8190】
    当cookie超出Server的设置大小后会出现400 Bad Request

结论:我们应该保证cookie key数量<=20,单key字节<=4000b,总字节数<=4000b

感谢vfresh同学整理:)

3 条评论在分享一个Javascript测试题

2010年2月23日

http://adamlu.googlecode.com/svn/trunk/js_quiz.html

继上次受挫折之后 又看了看相关js测试题

找到这个

再次分享一下