函数是什么?在JavaScrit中函数是一段代码,只定义一次,但可以被执行或调用任意次。当然不是上图的含树。对于函数我想干嘛用的大家都知道,这里没什么好赘述的。
在深入的学习之前,我先了解了一下。做了一点知识预览。
- 函数是参数化的:
function(x,y){}
意思就是()里面的可以传参数,然后拿来用。函数声明的时候()里面的参数叫形参,调用的时候()里面的参数叫实参(除了实参,调用的时候还会拥有一个本次调用的上下文this)。 - 如果函数作为对象的属性,就称他为对象的方法。当调用这个函数的时候他的上下文就是这个对象,即this的值。用于初始化一个对象的函数叫构造函数。
- 函数即对象,所以可以像使用对象那样使用他,比如:给他添加属性、把他赋值给变量、作为实参传递给其他函数等等。
- 函数中可以嵌套函数,闭包就是嵌套函数的功劳啊。
函数的定义
函数的定义有两种方法:
- 函数语句
function add(x,y){
return x+y;
}
上面的就是函数语句定义的函数。其实就是创建了一个新的函数对象,并将其赋值给变量add。函数的名字实际是看不见的,add仅仅是变量的名字。函数还可以赋值给其他变量var a = add; a(1,2);
。
- 函数表达式
书中原文
用函数表达式来定义函数指适用于它作为一个大的表达式的一部分,比如在赋值和调用中定义函数
/*这就相当于将函数add赋值给了a,如果我们要使用函数add就不能再直接add(2,4)了,变成了a(2,4)。
为何呢?因为这时的函数add()不再是一个全局的对象了。
*/
var a = function add(x,y){
return x+y;
}
/*这里函数的名称被省略了。
为何要省略?1、相当划出一块私有作用域,避免数据污染。2、执行完就销毁,避免内存长驻。
什么情况下不省略?需要指代自己的时候 */
var b = function (x,y){
return x-y;//return会使函数停止,所以return后面的语句都不会执行;
console.log('look at me!');
}
var f = function fact(x){
if(X=1)return 1;else return x*fect(x-1);//这里就指代了自己
}
//定义后立即调用,这是一种很新奇的写法,但是用到的场景太少了。最外层的括号不能去到,去到就会被当作是定义函数啦。这种写法我还不能给出合理的解释,以后补充。
var c = (function(x,y){return x*y}(2,4));
上面代码块的注释很重要!!!还需要注意的是,函数声明语句会被提前,但是用函数表达式声明函数,变量的声明提前了,但是变量的赋值却没有提前,所以在声明前调用会报错。
函数调用
函数在定义的时候是不会执行的,只有调用函数时才会执行。调用函数有4种方法。
- 作为函数调用
function add (x,y) {
return x+y;
}
var a = add; //将函数add赋值给变量a
//以下都是作为函数调用
add(1,2)
a(1,2)
var b =(function(x,y){return x+y;}(1,2))
- 作为方法
一个方法无非就是保存在一个对象的属性里的函数。比如有一个对象a,a对象有一个属性b,有一个方法f。那么a.b=f;
就把f函数赋值给了a对象的属性b。就等于给a对象定义了方法b。
//作为方法调用
a.b();
a.['b']()//这种写法也是可以的
//如果需要传参
a.b(12,22);
前面说过,作为方法调用上下文就是它所属的对象。即this指向对象。
var a ={
b:1,
c:2,
add: function(){return this.b+this.c;}
}
a.add();//这里返回3
需要注意的是this是一个关键字,不是变量也不是属性名。不可给this赋值。
this没有作用域的限制,嵌套的函数没有办法继承this,这是就需要将this赋值给一个变量。通常都用self作为变量名。
var o ={
a: 1,
add: function(){
var self = this;
function(){
return self .a;//这里如果使用this,那么this不是全局对象就是undefined
}
}
}
- 构造函数调用
如果函数或者方法调用之前带有关键字new,它就构成了构造函数调用。
构造函数是什么?我更倾向于这样的一种解释——JavaScript中没有类,只有new运算来模拟类,构造函数无从谈起。在其他语言中,比如java,构造方法用于返回该类的对象。
附上连接http://www.2cto.com/kf/201402/281841.html
这里说的很详细。
如果构造函数不需要传参,那么下面两种写法等价:
var a = new Object();
var a = new Object;
- 使用call()和apply()间接调用
这种情况在以后call和apply的介绍时一并介绍。
函数的实参和形参
前面说过形参就是函数定义是()中的参数,实参就是调用函数时()中实际传递的参数。JavaScrit在定义和调用时都没有指定参数的类型。调用时甚至不检查传入的参数个数。
用例子说明一切
假设有这么一个函数:
function add(x,y){
return x+y;
}
1、当实参比形参少的时候add(1);
,这时候x=1,而y将被设置为undefined。为了避免这样的情况。应该给省略的参数赋一个合理的默认值。
function add(x,y){
if(y === undefined) y = 0;
//还有一种更简洁的写法 y=y || 0;
return x+y;
}
2、当实参比形参多的时候add(1,2,3);
,形参只有两个只能对应前两个实参,第三个实参将被忽略。这个时候,就要用到arguments了。
arguments并不是什么高深的东西,就两句话——arguments指向实参对象的引用,是一个类数组对象(是对象不是数组)。可以像使用数组一样使用他。
function add(x,y){
var a = 0;
for(var i = 0;i<arguments.length;i++){
a += arguments[i];
}
return a;
}
上面的代码不管传多少个参数都可以将所有实参的和返回了,但如果我想要判断我传入的实参的类型呢?比如我只想将数字相加,其他都滚蛋。
function add(x,y){
var a = 0;
for(var i = 0;i<arguments.length;i++){
if(argument[i].isNumber()){
a += arguments[i];
}
}
return a;
}
像这种函数,也叫做不定实参函数。这种函数的实参个数不能为零(你要是一个实参都不传那还用个卵的arguments)。arguments[]对象最适合的应用场景是在这样一类函数中,这类函数包含固定个数的命名和必须参数,以后随个数不定的可选参数。
-
callee(被召者)和caller(呼叫者)属性
这两个属性是实参对象arguments的属性。这两个属性在严格模式下读写都会产生类型错误(在webstorm里会飘红)。书上说:callee指代当前正在执行的函数。听着有点像call;caller指代调用当前正在执行的函数的函数。没听懂。附上一个连接便于理解:http://www.cnblogs.com/dingyuanxin/p/4176310.html
实参
这里还是详细的说一下实参。当函数形参超过三个时,要记住正确的顺序还是挺麻烦的,那在多个形参传递的时候,应该怎么避免这种情况呢?——传一个对象。例如:
function getPersonInfo(name,age,sex) {
console.log(name+age+sex);
}
//要是我这样传,name就成了18,age成了王二了。
getPersonInfo('18','王二','男');
//所以可以这样写
function getPersonInfo(args){
console.log(args.name+args.age+args.sex);
}
getPersonInfo({name:'王二',age'18',sex:'男'});
实参类型
实参可以是任意类型——对象、函数、字符串、null、undefined等等。有些时候,为了确保函数执行不报错,会对实参的类型进行判断,这样的判断很重要但经常被忽略。