您的位置:首页 > Web前端 > JavaScript

js函数的各种写法与调用

2014-09-20 16:31 204 查看
PS:

通过搜索把各种函数的调用,匿名函数,闭包,call(),apply(),以及函数前的各种符号的含义(;+-!~等等)都归纳在这里。

以下是我见过的各种js函数的各种写法以及调用,虽然有些写法及其调用我不清楚其专业术语叫啥,但并不影响我写一个总结笔记。

我们刚开始接触js语音,经常看到的这种名叫“使用function关键字来定义函数”的写法,如:

function f(e){

alert(e);

}

f("hello world");

把函数赋值给一个变量,这种大概叫“表达式方式定义函数”吧,如:

var f=function(e){

alert(e);

}

f("hello world");

如上,如果省略了函数名,这种也叫“匿名函数”,当然也可以不匿名,给一个函数名,这在用于递归函数时就用到了,如:

var fact=function factTemp(n){

if(n<=1) return 1;

else return n*factTemp(n-1);

}

fact(3); // return 6

还有一种叫“使用构造函数”来定义函数,如:

var f=new Function("x","y","alert(x*y);");

f(2,3); //==>6

以上是三种定义函数的写法及其调用,但是调用还有其他方式:

用call()函数间接调用,如:
function person(name){    this.name=name;    this.sayHi=function(age,blogs){        alert("My name is: "+this.name+";/n age:"+age+";/n blogs:"+blogs);        }}function me(name){    this.name=name;    }var person1=new person("function person");var me1=new me("function me");person1.sayHi.call(me1,24,"http://www.cnblogs.com/xiaomou2014");  //本来me1是没有sayHi函数的,经过用call就调用了personal里的sayHi函数了。


同样类似于call的同类函数apply也同样能实现这样子的功能,只不过他的第二个参数是数组而已,如:
function person(name){    this.name=name;    this.sayHi=function(age,blogs){        alert("My name is: "+this.name+";/n age:"+age+";/n blogs:"+blogs);        }}function me(name){    this.name=name;    }var person1=new person("function person");var me1=new me("function me");person1.sayHi.apply(me1,[24,"http://www.cnblogs.com/xiaomou2014"]);


函数可以赋值给变量,同时也可以把他直接赋值给对象的属性,如:

var o={ square:function(x){ return x*x; } };

var y=o.square(2); //y=4;

有时候我们定于了一个函数后需要他马上运行,这貌似也挺多见于js插件,如:

(function(){

alert("hello world");

})();

也可以给他一个函数名:

(function f(){

alert("hello world");

})();

第二种:

(function(){

alert("hello world");

}());

同样的我们也可以给他一个函数名:

(function f(){

alert("hello world");

}());

当然也可以给它一个参数:

(function(e){

alert(e);

}("hello world"));

看别人的插件,你会发现人家开头处加了一个";",这样就算页面js有错误,加载运行他的插件也能保证运行,如:

;(function(e){

alert(e);

}("hello world"));

这是为了防止多个文件拼接的时候出错。

假如上一个文件的结尾没有分号,比如

// a.js
(function a () {
// ...
}())
// b.js
(function b () {
// ...
}())


两个文件拼接起来的时候,b.js 会被认为是在调用 a 的返回值。

如果一个函数的参数很多,那么我们调用函数的时候并不能很好的记住他的顺序,把参数封装成对象,然后把对象里的一个个属性对应用于参数,这样子很好解决了这个问题,如:
1 var f=function(args){ 2     sayHi(args.country || "Chinese", 3             args.name, 4             args.qq, 5             args.phone, 6             args.email)                 7 } 8 function sayHi(country,name,qq,phone,email){ 9     alert("Hi, I am a "+country+", my name is "+name+";qq:"+qq+";phone:"+phone+";email:"+email);    10 }11 f({name:"xiao",phone:"13888888888",email:"123456@qq.com",qq:123456});


这样子只要把参数名记住了就可以了,不用管他的顺序,同时给需要赋默认值得参数也很方便,如 args.country || "Chinese",如果调用函数的时候没有给country 这一参数实参,那么他的默认值就是chinese了。

//


function与感叹号

转载自:http://swordair.com/function-and-exclamation-mark/

最近有空可以让我静下心来看看各种代码,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企业版):
OptionCodeOps/sec
Chrome 13Firefox 6IE9Safari 5
!!function(){;}()3,773,19610,975,198572,6942,810,197
++function(){;}()21,553,84712,135,960572,6941,812,238
--function(){;}()21,553,84712,135,960572,6941,864,155
~~function(){;}()3,551,1363,651,652572,6941,876,002
(1)(function(){;})()3,914,95312,135,960572,6943,025,608
(2)(function(){;}())4,075,20112,135,960572,6943,025,608
voidvoid function(){;}()4,030,75612,135,960572,6943,025,608
newnew function(){;}()619,606299,100407,104816,903
deletedelete function(){;}()4,816,22512,135,960572,6942,693,524
=var i = function(){;}()4,984,77412,135,960565,9822,602,630
&&1 && function(){;}()5,307,2004,393,486572,6942,565,645
||0 || function(){;}()5,000,0004,406,035572,6942,490,128
&1 & function(){;}()4,918,20912,135,960572,6941,705,551
|1 | function(){;}()4,859,80212,135,960572,6941,612,372
^1 ^ function(){;}()4,654,91612,135,960572,6941,579,778
,1, function(){;}()4,878,19312,135,960572,6942,281,186
可见不同的方式产生的结果并不相同,而且,差别很大,因浏览器而异。

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

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

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

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

2011-10-31更新:如果你使用aptana,那么在使用(!+-)时要注意一点,它们会让aptana的解析失效,导致Outline窗口没有任何显示。但是就代码本身而言,其运行没有任何问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: