function与感叹号!

最近有空可以让我静下心来看看各种代码,function与感叹号的频繁出现,让我回想起2个月前我回杭州最后参加团队会议的时候,@西子剑影抛出的一样的问题:如果在function之前加上感叹号 (!) 会怎么样?比如下面的代码:

!function(){alert('iifksp')}()        // true

在控制台运行后得到的值时true,为什么是true这很容易理解,因为这个匿名函数没有返回值,默认返回的就是undefined,求反的结果很自然的就是true。所以问题并不在于结果值,而是在于,为什么求反操作能够让一个匿名函数的自调变的合法?

平时我们可能对添加括号来调用匿名函数的方式更为习惯:

(function(){alert('iifksp')})()        // true

或者:

(function(){alert('iifksp')}())        // true

虽然上述两者括号的位置不同,不过效果完全一样。

那么,是什么好处使得为数不少的人对这种叹号的方式情有独钟?如果只是为了节约一个字符未免太没有必要了,这样算来即使一个100K的库恐怕也节省不了多少空间。既然不是空间,那么就是说也许还有时间上的考量,事实很难说清,文章的最后有提到性能。

回到核心问题,为什么能这么做?甚至更为核心的问题是,为什么必须这么做?

其实无论是括号,还是感叹号,让整个语句合法做的事情只有一件,就是让一个函数声明语句变成了一个表达式

function a(){alert('iifksp')}        // undefined

这是一个函数声明,如果在这么一个声明后直接加上括号调用,解析器自然不会理解而报错:

function a(){alert('iifksp')}()        // SyntaxError: unexpected_token

因为这样的代码混淆了函数声明和函数调用,以这种方式声明的函数a,就应该以 a(); 的方式调用。

但是括号则不同,它将一个函数声明转化成了一个表达式,解析器不再以函数声明的方式处理函数a,而是作为一个函数表达式处理,也因此只有在程序执行到函数a时它才能被访问。

所以,任何消除函数声明和函数表达式间歧义的方法,都可以被解析器正确识别。比如:

var i = function(){return 10}();        // undefined
1 && function(){return true}();        // true
1, function(){alert('iifksp')}();        // undefined

赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。并且,对函数一元运算可以算的上是消除歧义最快的方式,感叹号只是其中之一,如果不在乎返回值,这些一元运算都是有效的

!function(){alert('iifksp')}()        // true
+function(){alert('iifksp')}()        // NaN
-function(){alert('iifksp')}()        // NaN
~function(){alert('iifksp')}()        // -1

甚至下面这些关键字,都能很好的工作:

void function(){alert('iifksp')}()        // undefined
new function(){alert('iifksp')}()        // Object
delete function(){alert('iifksp')}()        // true

最后,括号做的事情也是一样的,消除歧义才是它真正的工作,而不是把函数作为一个整体,所以无论括号括在声明上还是把整个函数都括在里面,都是合法的:

(function(){alert('iifksp')})()        // undefined
(function(){alert('iifksp')}())        // undefined

说了这么多,实则在说的一些都是最为基础的概念——语句,表达式,表达式语句,这些概念如同指针与指针变量一样容易产生混淆。虽然这种混淆对编程无表征影响,但却是一块绊脚石随时可能因为它而头破血流。

最后讨论下性能。我在jsperf上简单建立了一个测试:http://jsperf.com/js-funcion-expression-speed,可以用不同浏览器访问,运行测试查看结果。我也同时将结果罗列如下表所示(由于我比较穷,测试配置有点丢人不过那也没办法:奔腾双核1.4G,2G内存,win7企业版):

Option Code Ops/sec
Chrome 13 Firefox 6 IE9 Safari 5
! !function(){;}() 3,773,196 10,975,198 572,694 2,810,197
+ +function(){;}() 21,553,847 12,135,960 572,694 1,812,238
- -function(){;}() 21,553,847 12,135,960 572,694 1,864,155
~ ~function(){;}() 3,551,136 3,651,652 572,694 1,876,002
(1) (function(){;})() 3,914,953 12,135,960 572,694 3,025,608
(2) (function(){;}()) 4,075,201 12,135,960 572,694 3,025,608
void void function(){;}() 4,030,756 12,135,960 572,694 3,025,608
new new function(){;}() 619,606 299,100 407,104 816,903
delete delete function(){;}() 4,816,225 12,135,960 572,694 2,693,524
= var i = function(){;}() 4,984,774 12,135,960 565,982 2,602,630
&& 1 && function(){;}() 5,307,200 4,393,486 572,694 2,565,645
|| 0 || function(){;}() 5,000,000 4,406,035 572,694 2,490,128
& 1 & function(){;}() 4,918,209 12,135,960 572,694 1,705,551
| 1 | function(){;}() 4,859,802 12,135,960 572,694 1,612,372
^ 1 ^ function(){;}() 4,654,916 12,135,960 572,694 1,579,778
, 1, function(){;}() 4,878,193 12,135,960 572,694 2,281,186

可见不同的方式产生的结果并不相同,而且,差别很大,因浏览器而异。

但我们还是可以从中找出很多共性:new方法永远最慢——这也是理所当然的。其它方面很多差距其实不大,但有一点可以肯定的是,感叹号并非最为理想的选择。反观传统的括号,在测试里表现始终很快,在大多数情况下比感叹号更快——所以平时我们常用的方式毫无问题,甚至可以说是最优的。加减号在chrome表现惊人,而且在其他浏览器下也普遍很快,相比感叹号效果更好。

当然这只是个简单测试,不能说明问题。但有些结论是有意义的:括号和加减号最优。

但是为什么这么多开发者钟情于感叹号?我觉得这只是一个习惯问题,它们之间的优劣完全可以忽略。一旦习惯了一种代码风格,那么这种约定会使得程序从混乱变得可读。如果习惯了感叹号,我不得不承认,它比括号有更好的可读性。我不用在阅读时留意括号的匹配,也不用在编写时粗心遗忘——

当我也这么干然后嚷嚷着这居然又节省了一个字符而沾沾自喜的时候,却忘了自己仓皇翻出一本卷边的C语言教科书的窘迫和荒唐……任何人都有忘记的时候,当再捡起来的时候,捡起的就已经不单单是忘掉的东西了。

2011-10-31更新:如果你使用aptana,那么在使用(!+-)时要注意一点,它们会让aptana的解析失效,导致Outline窗口没有任何显示。但是就代码本身而言,其运行没有任何问题。

转载:http://www.swordair.com/blog/2011/10/714/

Filed under javascript
八 10, 2012

关于浏览器支持的javascript版本

Comments Off

获取IE支持的javascript版本是一件麻烦的事,因为人家根本没有javascript概念,人家只有JScript的概念。但两者都是源于ECMAscript,所以从支持的ECMA版本可以推测出IE兼容的javascipr版本。

从表格中可以看出,IE6/7/8差不多是支持javascript1.5的水平

版本 时间 相当于 火狐 IE Opera Safari Chrome
1.0 March 1996

3.0


1.1 August 1996





1.2 June 1997





1.3 October 1998 ECMA-262 1st + 2nd edition
4.0


1.4






1.5 November 2000 ECMA-262 3rd edition 1.0 5.5 (JScript 5.5),

6 (JScript 5.6),

7 (JScript 5.7),

8 (JScript 5.8)

6.0 3.0-5 1.0-10.0.666
1.6 November 2005 1.5 + array extras + array and string generics + E4X 1.5



1.7 October 2006 1.6 + Pythonic generators + iterators + let 2.0



1.8 June 2008 1.7 + generator expressions + expression closures 3.0
11.50

1.8.1
1.8 + native JSON support + minor updates 3.5



1.8.2 June 22, 2009 1.8.1 + minor updates 3.6



1.8.5 July 27, 2010 1.8.2 + ECMAScript 5 compliance 4 9 11.60

当然你也可以写下面这个有点NC的代码。

<script language="Javascript">
var jsver = 1.0;
</script>

<script language="Javascript1.1">
jsver = 1.1;
</script>

<script language="Javascript1.2">
jsver = 1.2;
</script>

<script language="Javascript1.3">
jsver = 1.3;
</script>

<script language="Javascript1.4">
jsver = 1.4;
</script>

<script language="Javascript1.5">
jsver = 1.5;
</script>

<script language="Javascript1.6">
jsver = 1.6;
</script>

<script type="text/javascript">
document.write('这货支持的javascript版本是' + jsver);
</script>

上面的有点NC,你看看这个函数吧。

function getjsversion(){
    var n = navigator;
    var u = n.userAgent;
    var apn = n.appName;
    var v = n.appVersion;
    var ie = v.indexOf('MSIE ');
    if (ie > 0){
        apv = parseInt(i = v.substring(ie + 5));
        if (apv > 3) {
            apv = parseFloat(i);
        }
    } else {
        apv = parseFloat(v);
    }
    var isie = (apn == 'Microsoft Internet Explorer');
    var ismac = (u.indexOf('Mac') >= 0);
    var javascriptVersion = "1.0";
    if (String && String.prototype) {
        javascriptVersion = '1.1';
        if (javascriptVersion.match) {
            javascriptVersion = '1.2';
            var tm = new Date;
            if (tm.setUTCDate) {
                javascriptVersion = '1.3';
                if (isie && ismac && apv >= 5) javascriptVersion = '1.4';
                var pn = 0;
                if (pn.toPrecision) {
                    javascriptVersion = '1.5';
                    a = new Array;
                    if (a.forEach) {
                        javascriptVersion = '1.6';
                        i = 0;
                        o = new Object;
                        tcf = new Function('o', 'var e,i=0;try{i=new Iterator(o)}catch(e){}return i');
                        i = tcf(o);
                        if (i && i.next) {
                            javascriptVersion = '1.7';
                        }
                    }
                }
            }
        }
    }
    return javascriptVersion;
}
console.log(getjsversion());
Filed under javascript
六 15, 2012

在页面加载完后执行javascript代码

Comments Off

最近在写javascript100插件时,遇到网友提的一个问题,代码必须放在body结束标签之后才能正常执行,放在head标签里面就会报错了。
想了下原因,因为我写的这个javascript100插件中操作了页面的DOM元素,假如放在head之间,因为页面DOM还没开始渲染,所以会报错

为了让插件更加人性化,我还是想改进下这个缺陷,就必须让代码在页面加载完后执行JS代码

在网上搜索了下,让JS代码在页面加载完后执行有很多方法,我就一一列出来下
方法1:

<script defer="defer" language="javascript"></script>
//或者
<script defer language="javascript"></script>

但这种方法只有IE支持,其他浏览器不识别defer 这种方法不靠谱

方法2: 把js代码放在</body>结束标签之后, 因为浏览器代码渲染是从上至下的,这种方法是最傻瓜的,兼容所有浏览器

方法3: 利用document.onreadystatechange事件

if(document.readyState=="complete"){
   //js代码
 }

不过这种方法,也有缺点有的浏览器在readStates等于loaded时就完了,不会跳到complete状态,而且早期的火狐等浏览器不支持这个事件,单纯的用这种方法不靠谱

方法4:
利用window.onload事件

window.onload=function(){
	funA();
}

但这个方法在IE中只能在一个地方调用,假如2个地方调用,后面调用的就会把前面的添加的覆盖掉;

window.onload=function(){
	funA();
}

window.onload=function(){
	funB();
}

如果2个地方调用,funA()在IE中就不会执行了。
所以只能把它们写在一起,

window.onload=function(){
	funA();
	funB();
}

在IE中调用还是很不方便

不过,外国的一个大牛写了一个函数,利用这个函数,我们就可以多处调用了

var $$ = function(func){
	var oldOnload =window.onload;
	if(typeof window.onload != 'function'){
		window.onload = func;
	}else{
		window.onload = function(){
			oldOnload();
			func();
		}
	}
}

//调用
$$(function(){
	//页面加载完后执行js代码
})

我们推荐使用上面这个函数,兼容所有浏览器,最后我选择了这个方法

window.onload是使用的DOM 0 级方法,我们还可以使用DOM 2级方法给onload事件添加需要执行的代码
不过也得写一个兼容函数

var $$ = function(func){
	if ( document.addEventListener ) {
		window.addEventListener( "load", func, false );
	}else  if ( document.attachEvent ){
		window.attachEvent( "onload", func);
	}
}
$$(function(){
	//页面加载完后执行js代码
})

我还看了下jQuery的源码
jQuery里面还利用现代浏览器的DOMContentLoaded事件,这个事件是等DOM加载完后,就执行代码了,而不用等整个页面加载完后执行,但IE不支持这个事件,有兴趣的朋友可以自己研究研究

Filed under javascript
二 22, 2012

js中for in 和 for each in的区别

Comments Off

js 中for in 和for each in的区别
两个的作用都用来遍历对象,但为什么有了for in语句了还要for each in语句呢,后来看了下for each in开发的文档,for each in是作为E4X标准的一部分在javascript 1.6中发布的,而且E4X不是ECMAScript标准的一部分。

for each…in语句已被废弃,E4X中的大部分特性已被删除,但考虑到向后兼容,for each…in只会被禁用而不会被删除,可以使用ES6中新的for…of语句来代替。

 

var 超毛 = {
    身高:"185CM",
    体重:"70公斤",
    年龄:40
};
for (var i in 超毛){
    document.write("i = " + i + "..................超毛[i] = " + 超毛[i] + "<br>");
}
/*
结果
i = 身高..................超毛[i] = 185CM
i = 体重..................超毛[i] = 70公斤
i = 年龄..................超毛[i] = 40
*/
for each (var i in 超毛){
    document.write("i = " + i + "..................超毛[i] = " + 超毛[i] + "<br>");
}
/*
结果
i = 185CM..................超毛[i] = undefined
i = 70公斤..................超毛[i] = undefined
i = 40..................超毛[i] = undefined
*/

注意变量i的值是不一样的,for each in无法获得对象的属性名,只能获取到属性值

如果你想遍历对象,超毛建议还是使用for in
1,for in在遍历对象伤还是比for each in功能更加强大for in不仅能遍历处所有属性名和属性值,for each in只能遍历出属性值
2,for in是javascript 1.0就出来的语法,for each in是javascript 1.6才出来的语法,很多浏览器是 不支持的,比如IE6,7,8是不支持的,所以还是推荐使用for in

同时两者都能实现对数组的遍历
建议大家在遍历数组时,推荐不要使用for in语句和for each in语句,因为它们两无法保证遍历数组的顺序,还是老老实实使用for语句比较好

Filed under javascript
二 17, 2012

js中的时间转换—毫秒转换成日期时间

Comments Off

前几天,在项目中遇到js时间增加问题,要将js毫秒时间转换成日期时间

var oldTime = (new Date("2011/11/11 20:10:10")).getTime(); //得到毫秒数

怎么转换回去呢

从网上看了很多方法,大多数是用毫秒数除以365*24*60*60&1000,这么转回去,这种方法转换太过复杂,年月日,时分秒都要不同的方法获取,而且有的年份有366天,有的365天,这么算起来就太过复杂了。

后面自己试了一个方法,居然成功了

var oldTime = (new Date("2011/11/11 20:10:10")).getTime(); //得到毫秒数
var newTime = new Date(oldTime); //就得到普通的时间了

直接传入毫秒数作为参数,给Date对象就可以得到普通的时间了,然后通过getHours,getFullYear等方法获取年月日,时分秒了

Filed under javascript
一 14, 2012

javascript删除JSON元素

Comments Off

首先要搞清JSON的数据格式,我这里所说的JSON都是指javascript中的。

JSON数据是由对象和数组数据结构组成,我们只要学会javascript中对对象和数组的删除方法即可对JSON项进行删除操作

javasscript删除数组的3种方法

1,用shift()方法

shift:删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined

var chaomao=[1,2,3,4,5]

var chaomao.shift() //得到1

alert(chaomao) //[2,3,4,5]

2,用pop()方法

pop:删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefined

var chaomao=[1,2,3,4,5]

var chaomao.pop() //得到5

alert(chaomao) //[1,2,3,4]

前面的方法只能操作数组开头和结尾,无法操作中间项,如果要操作中间的项,使用splice方法

3,用splice方法

这个方法很强大,可以对数组任意项进行增加,删除,替换操作

修改操作:

var chaomao=[1,2,3,4,5]

chaomao.splice(2,1,8,9)

alert(chaomao) //1,2,8,9,4,5

第一个参数是准备操作的数组位置,第二个参数是操作位置后面的数组项数,第三个以后的就是,被替换后的内容

例子就是表示:从chaomao这个数组位置2开始(也就是值为3的那一项,数组下标从0开始的),位置2后的一项,替换成成8,9

如果把第二个参数改为2,也就是chaomao.splice(2,2,8,9),也就是说位置2后面的两项换成8,9,打印出来的结果就是1,2,8,9,5,3和4这2两项被换掉了

这里需要说明的是,被替换的项数不一定要和替换的项数相等,1项可以被换成3项,5项也可以被换成2项,基于这个原理,我们就用这个方法来对数组进行添加和删除操作

 

删除操作:

var chaomao=[1,2,3,4,5]

chaomao.splice(2,1)

alert(chaomao) //1,2,4,5

上面例子中,把chaomao中的位置2后的1项替换成空的,因为后面没有内容,结果可以看到,把3这项删除了

 

添加操作:

var chaomao=[1,2,3,4,5]

chaomao.splice(2,0,8,9)

alert(chaomao) //1,2,8,9,3,4,5

上面例子中,把chaomao中位置2后的0项换成8,9,也就等于添加了两项

其实,删除和添加操作都只是splice修改方法的两种衍生罢了
其他不靠谱的数组删除方法

之所以说不靠谱,是因为它没有真正删除数组项,而是是把它设置为空了,项本身的位置还在,数组项数没变

不靠谱的数组删除方法1:

var chaomao = [1,2,3,4,5];

chaomao[1]=null;

alert(chaomao.length); //5

打印出的项数还是5,并没有删除它,只是把chaomao[1]这一项值设为null,chaomao[1]=undefined,这中方法也是一样,并没有删除数组项

 

不靠谱的数组删除方法2:

var chaomao = [1,2,3,4,5];

delete chaomao[1];

alert(chaomao.length); //5

和方法1的结果一样,并没有真正删除它,而只是把这一项设为undefined

 javascript删除对象的方法

js中删除对象元素用delete操作符

我们来看看一个例子

var p = {
      "name": “chaomao”,
      "age": 45,
      "sex": "male"
};

delete p.name
for(var i in p){
console.log(i); //输出age,sex,name项已经被删除了
}

javascript删除JSON元素

前面我们已经会了JS删除对象和数组的方法
我们下面进对一个具体的JSON数据进行操作

var computer = { //这个JSON数据由数组和对象结构组成
      "cpu": ["intel","AMD"],
      "harddisk": ["Western Digital","Seagate"],
      "motherboard": ["ASUS","MSI"]
};

如果我们要删除CPU里面里intel这一个元素,因为这是数组里面的一项,所以我们用数组方法

computer.cpu.splice(0,1)
console.log(computer.cpu) //输出AMD,intel被删除了

我们如果想删除harddisk这一项,因为它是对象的一项,所以我们要用对象的删除方法进行操作

delete computer.harddisk
for(var i in computer){
console.log(i) //输出CPU,motherboard,harddisk被删除了
}

总结:JSON的主要数据结构是对象和数组,我们只要对相应的项的结构,进行相应的操作方法,就可以方便的删除JSON元素

Filed under javascript
一 12, 2012

关于在javascript中使用中文

Comments Off

一直想试下,能不能在javascript中使用中文,因为javascript是unicode字符集,我想程序里面应该可以使用中文的

(function(){
	var obj={
		"超毛":"123"
	}
	alert(obj["超毛"]);
})()

先来段这样的,吼吼成功鸟

再改成这样,调用时用.符号调用

(function(){
	var obj={
		"超毛":"123"
	}
	alert(obj.超毛);
})()

吼吼,依然成功鸟

我们再把对象定义中的引号去掉试试,看能否访问到

(function(){
	var obj={
		超毛:"123"
	}
	alert(obj.超毛);
})()

嘿嘿,还是可行的

function 超毛(){
	alert("超毛是帅锅");
}
超毛();

函数命名为中文也是可以的,既然函数可以,那么变量也应该是可以的,我们试试

var 超毛 = "超毛是帅锅";
alert(超毛);

哈,依然成功了

PS:我只在火狐中实验,有兴趣的朋友可以去其他浏览器试试,我想都是可行的
但我仍然建议不要这么做,因为在浏览器编码不确定的情况下,中文可能会乱码

转载于:https://my.oschina.net/ind/blog/298162

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值