js函数作用域和对象作用域里变量的不同

2013-3-3 评论(8) 分类:技术文章 Tags:

写js时碰到一个坑,查了半天简化后问题是这样的:

<script>
  alert(document) //[object HTMLDocument]
  var document
</script>

<script>
  (function(){
    alert(document) //undefined
    var document
  })()
</script>

在全局作用域上document没变,但在函数作用域上执行后document变undefined了。来看看这两种情况下都发生了什么事。

函数作用域

在函数作用域上这样的结果虽然难发现,但容易理解,var是声明局部变量,js在函数执行之前会把作用域内所有用var声明抽到头部统一生成,就是说

(function(){
  alert(document)
  var document
})
//等价于
(function(){
  var document
  alert(document)
})

在执行alert(document)访问document变量时,先从当前作用域寻找这个变量,如果当前作用域没有这个变量,会通过作用域链一层层往上寻找。若没有var document,就会找到上层的window.document。在这里当前作用域声明了document这个变量,优先访问这个变量,因为这个变量只声明未赋值,此时document==undefined。

在函数作用域下是这样的,但全局作用域下就不同了。

对象作用域

先看看raphelguo提供的的例子:

var obj = {a : 1}
with (obj) {
  var a = 2
}
alert(obj.a) //2
with (obj) {
  a = 5
}
alert(obj.a) //5

可以看到在obj作用域下,var变量声明已经失去了作用,无论有没有用var声明变量,对变量赋值都是等价于对当前obj的属性赋值。换句话说var是用来声明局部变量的,但在对象作用域内并没有地方存储局部变量,所以var是无效的。

再回来看看最初的例子,就很容易理解了,浏览器上在全局作用域下等价于在window这个对象下执行代码:

with(window) {
  alert(document)
  var document
}

这时var document完全无作用,alert出来的自然是window.document了。

看来,js的变量不是存在函数里,就是存在对象(包括宿主对象如window)里,要区分这两种情况的不同避免遇到坑。

iOS5 innerHTML插入内联touch事件的问题

2011-10-19 评论(2) 分类:技术文章 Tags:

iOS5一出来,很多对safari的溢美之声,那些新增的特性确实好,但这个版本的safari很有问题。

document.getElementById("test").innerHTML = '<div ontouchstart="alert(\'touchstart\')"></div>'

一般,这样的代码是没问题的,一段HTML字符串赋给DOM的innerHTML后,自动生成DOM,并且上面的内联事件都是有效的。在至今所有浏览器都可以这样,但现在在iOS5的Safari不行,生成的DOM中ontouchstart事件无效,目前测试ontouchstart/ontouchmove/ontouchend都不行,若改成onmousedown,onclick等都是可以的。这个问题导致了jquerysencha的一些应用都挂了。

目前的解决办法就是,先把HTML加入一个新建的DOM里,再用cloneNode把创建好的DOM取出来加入目标位置,这样才可以。如果不用cloneNode而是直接把创建好的元素塞进去还是不行的。

var dom = document.createElement("div");                                    
dom.innerHTML = '<span ontouchstart="alert(\'touchstart\')">touchstart dom create</span>    ';                                                                          
document.getElementById("testTSdom") .appendChild(dom.childNodes[0].cloneNode(true));

可以在这里 http://bangswork.googlecode.com/svn/trunk/lab/ios5touchstart/index.html 看到效果

这应该属于一个bug,居然就带着这么明显的bug发布出来了,要折腾死做移动版的前端了。

此外,还发现iOS5下Safari的一些其他问题,例如缓存很奇怪js excution timeout,还有一些已经碰到但未明原因的,总之,这个safari很有问题。

解析豆瓣前端轻量框架Do

2010-11-8 评论(6) 分类:技术文章 Tags:

豆瓣使用了jquery框架,但jquery没有提供大型网站所需要的模块化管理,所以豆瓣前端团队就在jquery之前再包裹一层轻量级的框架,用来组织js模块并管理模块之间的依赖关系,按依赖关系自动加载js模块。这个keynote有相关信息。

do的源码压缩后基本都是单字符的变量,读起来相当费劲,整理了一下,给各个变量加上适当的名字,也是理解和学习这个轻量框架的过程。

源码很简单,主要思路就是:通过Do.add()全局存储各个模块的信息,在执行do时把各模块的依赖和执行函数整理成一个有先后顺序的队列,依次加载/执行这个队列。

例:

//uibase模块地址是ui.js
//dialog依赖ui模块
Do.add('uibase', {path: 'ui.js', type: 'js'});
Do.add('dialog', {path: 'dialog.js', type: 'js', requires: ["uibase"]});

Do("dialog", function(){
//使用dialog
});

do里的makeQueue方法会生成这样一个队列:[“jquery.js”, “uibase”, “dialog”, function()]

jquery.js是core_lib里的。接着队列执行器Execute就会依次加载jquery.js ui.js dialog.js,最后执行那个依赖dialog模块的function()

整理后的源码:
(更多…)

iQuery实现CSS3 transition动画接口

2010-8-24 评论(2) 分类:技术文章 Tags:

给iquery加了跟jquery差不多的动画接口animate,不过实现上是通过CSS3的transition。

效果点这里 (桌面浏览器只支持webkit内核,如chrome/safari)

iQuery源码

(更多…)

简化代码学习jquery动画源码

2010-8-18 评论(4) 分类:技术文章 Tags:

效果见这里:http://bangswork.googlecode.com/svn/trunk/lab/effect/index.html

jquery的动画总体思路是:

有一个fx类专门处理动画,fx的各个实例共享一个timers数组和一个setInterval。对每个传进来的dom的每个属性值都新建一个fx实例去处理,一个fx实例对应一个dom的一个属性的变化。fx里有个step函数,可以计算出当前这个时刻这个属性要达到什么值。

这个step会通过共用的一个setInterval每13毫秒执行一次,这就可以使得它行成动画。另外如果浏览器速度太慢无法达到13毫秒执行一次step,动画也会按时完成,因为是根据当前系统时间计算属性要达到的值的。

每一个fx实例的step都会放进timers数组,实际上setInterval是持续执行timers里的每一个函数,这样只用一个setInterval就让众多属性“一起动”了。step里判断到超过了动画运行的时间,就会返回false让它从timers里移除,timers为空时clearInterval。

下面简单实现这整个过程:
(更多…)

javascript关于数组的几个常用伎俩

2010-8-16 评论(1) 分类:技术文章 Tags:

1.把对象变成类数组

var a = { name : "jquery" },
b = ["elem1", "elem2", "elem3"];
Array.prototype.push.apply(a, b);
alert(a)	//[Object object]

//可以像遍历数组一样遍历这个对象
for ( var i = 0; i &lt; a.length; i ++ ) {
	alert(a[i])
}

jquery就是用这种方法把jquery对象变成类数组的,效率还很高。

2.去掉一层嵌套数组

var a = [[1,2], 3, [4,5,6]];
var b = Array.prototype.concat.apply([], a);
console.info(b) 	//1,2,3,4,5,6

concat方法:

arrayObject.concat(arrayX,arrayX,……,arrayX)
concat() 方法用于连接两个或多个数组。arrayX为n个数组or元素参数
console.info([1].concat([2,3], 4)) //1,2,3,4

apply方法:

fn.apply(obj, args)
在obj的作用域下调用函数fn,并把数组args里的第i个元素作为第i个参数传给fn
例如
fn.apply(window, [1,2,3]) 等于 fn(1,2,3) (直接调用一个函数作用域就是window)

合起来就有上面那个效果了。

3.复制数组

利用Array.splice(0)可以快速复制一个数组

var a = [1,2,3],
b = a.splice(0);
alert(b)//1,2,3
alert(a==b)//false

4.高效合并字符串

var a = [“<div>”,”<span>”,”</span>”,”</div>”];
alert(a.join(“”)) //”<div><span></span></div>”

通过这种方式合并字符串效率比 + 号高了近七八倍,因为js的机制导致每次给字符串追加内容都是抛弃原有字符串新建另一个追加后的字符串,所以效率会低。

在firefox下测试:

var a = []
for (var i = 0; i &lt; 100000; i++) {
	a.push("&lt;div&gt;");
}
console.time("arr");
a.join("");
console.timeEnd("arr");console.time("str");

var b = "";
for (var i = 0; i &lt; 100000; i++) {
	b += "&lt;div&gt;";
}
console.timeEnd("str");
//arr:19ms
//str:141ms

jQuery for Iphone: iQuery

2010-6-16 评论(2) 分类:作品 Tags:

img_0015 img_0017

仿照jquery的API写了专用于iphone的jquery,索性叫iquery。虽然API很多一样,但里面的实现大多不一样,sizzle引擎改成了现代浏览器自带的queryselector,其他API很多实现上是简化了,很多并不常用的方法和功能没加上,增加了iphone特有的touch等事件,详细看文档。因为内部实现是自己写的,又没有经过很多测试,所以还不能像jquery那样随心所欲地用不怕出错,还拿不出手,只能自己用,打算边用边测试,先放上源码和文档。

文档同时也算是iquery的使用演示。建议在iphone/itouch下查看,同时还可以挺好地支持电脑上的chrome和safari,其他浏览器不行~

P.S 目前文档还有很多方法没有加上详细说明

文档:http://tiny.cc/iquery

源码:http://code.google.com/p/bangswork/source/browse/trunk/iquery/iquery.js

目前压缩前17.8K,压缩后9.4K

JS模仿AS3事件机制

2010-4-18 评论(3) 分类:技术文章 Tags:

练习下自定义事件的实现,想让JS可以用AS3的语法使用自定义事件,实现很简单,因为没考虑复杂的情况吧。试过后发现有些地方还是模仿不了,最主要不一样的地方是:

1.侦听函数没法自动绑定那个函数所在的object,必须手动把object传进去,像这样:dispatcher.addEventListener(SampleDispatcher.ACTION, e.action, e);
最后还要传“e”这个obj进去,否则函数action里的this不指向e

2.使用的时候继承十分麻烦,还得小心翼翼,看demo就知道了。

(更多…)

YUI3的沙箱机制

2010-4-10 评论(2) 分类:技术文章 Tags:

YUI2

在YUI2.x里,每一个模块功能的引入都会直接添加在全局的YAHOO里,例如dom.js里:

var Y = YAHOO.util;
Y.Dom = {...}

这样在整个页面范围内,YAHOO.util里就多了一个功能Dom。

YUI3-add()

在YUI3里,每一个模块引入时并没有把功能直接添加到全局的YUI里,看看YUI3里的dom.js:

YUI.add('dom-base', function(Y) {
    //在这里添加dom方法
    Y.DOM = {
        ...
    }
...
}, '3.0.0' );

再看看yui.js里add的源码:

add: function(name, fn, version, details) {

    YUI.Env.mods[name] = {
        name: name,
        fn: fn,
        version: version,
        details: details || {}
    };

    return this; // chain support
}

所以YUI3里引入每个模块时只是把这个模块的内容储存在YUI.Evn.mods里。

YUI3-use()

需要使用某个模块时,在创建YUI实例以后,用use取出来执行模块里的程序,为这个YUI实例添加相应的方法:

YUI().use('dom-base', function(Y) {
    //这里可以用到模块dom-base里对YUI添加的方法Y.DOM
    alert(Y.DOM) //[object object]
})

而在同一个页面里,YUI实例里如果没有指明use(‘dom-base’),就没有Y.DOM这个方法

YUI().use('', function(Y) {
    alert(Y.DOM) //undefined
})

沙箱

这里YUI().use(”,function(Y){…})就是一个安全沙箱,可以确保这里面的Y是纯天然无污染的,Y实例里有什么功能完全取决于use里传进的模块名称,function(Y){}里面的程序跟外界是隔离的,在里面创建的变量(除了全局变量)以及对YUI的添加修改都不会影响到同个页面上其他人写的程序。

但是这个纯天然无污染是有点代价的,就是每次都要新建一个YUI实例,消耗内存,但如果不怕Y被污染,可以不每次都创建实例:

var Y = YUI();
Y.use('dom-base', function(Y) {
    //可以同时使用dom-base和oop模块里添加的方法
});
Y.use('oop', function(Y) {
    //可以同时使用dom-base和oop模块里添加的方法
});

add()和use()配合一些参数(例如require)和YUI Loader就成了YUI3模块化编程的基础。

最简化的YUI沙箱

去除了YUI Loader以及require等参数,参考自这里
(更多…)

陆战军旗

2010-4-7 评论(7) 分类:作品 Tags:

army

AI版(AI很笨):http://cnbang.net/army

前言

小时候玩的棋类游戏中,军旗算是玩得比较多的,接下来就是跳棋了。不知有多少年没碰过军旗,上次偶然在姐家下了一盘,挺怀念的,网上查了一下,四国军棋是挺多,但我们的玩法是不断翻开盖住的棋子的,据说这叫翻翻棋,边锋游戏有,下载后发现不咋地,应该是十几年前做的了,我想自己做一个网页版的。奇怪怎么这么久还没有大型的网页版的棋牌游戏,总比QQ游戏方便很多吧。

前期

前期得先把棋盘画好,定好棋子在上面的走动规则。话说这棋盘画得我自己挺满意的,呵,高仿真啊,我是把棋盘拍照下来照着画的,不过缺点是高度太大了,对于小电脑一个屏幕可能还看不到整个棋盘。棋子就不咋地,很一般,不过想换肤很容易。

程序的实现方面,这次我还是用了jquery,本来想用YUI练习下的,但发现YUI本来就不适合用来做这样的游戏,只适合用于分模块构建中大型网站,另外它没有在dom里保存data的功能,所以还是用回jquery。
(更多…)