JavaScript学习总结(四)function函数部分
转自:http://segmentfault.com/a/1190000000660786
概念
函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。
js
支持两种函数:一类是语言内部的函数(如eval()
),另一类是自己创建的。
在 JavaScript
函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。
您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。
函数调用
有如下四种调用js
函数的方式:
-
作为函数
-
作为方法
-
作为构造函数
-
通过
call()
和apply()
返回函数的函数
-
当函数无明确返回值时,返回的值就是
undefined
。 -
当函数有返回值时,返回值是什么就返回什么。
我们可以通过使用 return
语句实现将函数返回调用它的地方。
在使用 return
语句时,函数会停止执行,并返回指定的值。
函数通常会返回一个唯一值,那么这个值也可能是另一个函数:
<script type="text/javascript">
//函数表达式
var box = function(){
var a=1;
return function(){
alert(a++)
}
alert(a);//永远不会执行
}
alert(box());//弹出"function(){alert(a++)}"
</script>
在这里,我们只需将返回值赋值给某个变量,然后就可以像使用一般函数那样调用它了:
<script type="text/javascript">
var box = function(){
var a=1;
return function(){
alert(++a)
}
}
var newFunc = box();
newFunc();//2
</script>
如果想让返回的函数立即执行,亦可以使用box()()
来执行这段代码。
ECMAScript
所有函数的参数都是按值传递的,言下之意就是参数不会按引用传递。
PS:如果存在按引用传递的话,那么函数里的那个变量将会是全局变量,在外部也可以访问。
(1)值类型:数值、布尔值、null、undefined。
(2)引用类型:对象、数组、函数。
引用类型值
:指的是那些保存在堆内存中的对象,意思是,变量中保存的实际上只是一个指针,这个指针执行内存中的另一个位置,由该位置保存对象;
创建匿名函数
function(){
return ‘hi’; //单独的匿名函数是无法运行的,就算能运行也无法调用,因为没有名字
}
这种匿名函数的用法在JQuery
中非常多。直接声明一个匿名函数,立即使用。用匿名函数的好处就是省得定义一个用一次就不用的函数,而且免了命名冲突的问题,js
中没有命名空间的概念,因此很容易函数名字冲突,一旦命名冲突以最后声明的为准。
在javascript
语言里任何匿名函数都是属于window
对象。在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this
指向也是window
对象
(function(){
console.log(this === window);//true
})();
通过自我执行来执行匿名函数:
//通过自我执行来执行匿名函数
<script type="text/javascript">
(function (){ // (匿名函数)();第一圆括号放匿名函数,第二个圆括号执行
alert('Lee');
})();
</script>
把匿名函数自我执行的返回值赋给变量:
//把匿名函数自我执行的返回值赋给变量
<script type="text/javascript">
var box = (function (){
alert('Lee');
})(); //弹出”Lee”;
alert(box); //弹出 undefined,如果写出alert(box()),那么只会弹出一个"Lee"
</script>
var box= (function () {
return 'hi';
})();
console.log(box);//hi
自我执行匿名函数的传参:
//自我执行匿名函数的传参
<script type="text/javascript">
(function (age){
alert(age);
})(100); //弹出100
</script>
自执行函数的三种写法
var result = function (){
alert(2);
}();
另一种语法也可得到同样结果:
var result = (function () {
console.log(2);
})();
将函数返回值分配给变量:
var result = (function () {
return 2;
}());
js
创建动态函数:
js
支持创建动态函数,动态函数必须用Function
对象来定义(Function是js
中的一个对象,是固定不变的,规定Function
对象的"F"必须大写,当是function
的时候,我们知道是定义函数的时候所使用的一个关键字:function funName(x,y)
,当是Function
的时候(F大写的时候),我们知道是js
中的对象)
创建动态函数的基本格式:var 变量名 = new Function("参数1","参数2","参数n","执行语句");
看下面的一段代码:
<script type="text/javascript">
var square = new Function ("x","y","var sum ; sum = x+y;return sum;");
alert("square(2,3)的结果是:"+square(2,3)); //square(2,3)的结果是:5
</script>
square
是动态创建的函数,在Function
对象后面的括号里的每一部分内容都必须是字符串形式的,也就是说都必须用引号(""或者是'')括起来
这段代码:
var square = new Function ("x","y","var sum ; sum = x+y;return sum;");
和下面这段代码:
//函数声明
function square (x,y){
var sum;
sum = x+y;
return sum;
}
是一摸一样的,只不过一个是动态函数
,一个是静态函数
。
我们为什么要把代码分成一小段一小段的代码呢?,把一个字符串分成了若干个独立的字符串的优点就在于我们可以通过修改其中的某些字符串来随时改变函数的作用。
回调函数
回调就是一个函数的调用过程。那么就从理解这个调用过程开始吧。函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b。那么这个过程就叫回调。
其实中文也很好理解:回调,回调,就是回头调用的意思。函数a的事先干完,回头再调用函数b。
这里必须清楚一点:函数b是你以参数形式传给函数a的,那么函数b就叫回调函数。
在jquery里的绝大多数效果函数都涉及到callback函数。jquery效果函数
例如:
<script type="text/javascript">
$("div").show(1000,function(){
//callback function
});
</script>
这里的callback function
换成实例可以是:
<script type="text/javascript">
$("div").show(1000,function(){
console.log("hello world")
});
</script>
Callback
实际上是,当一个函数执行完后,现执行的那个函数就是所谓的callback
函数。怎么样?很好理解吧……
方法和函数的区别
var arr = [1,2,3,4,5]
var a =12; // 变量:自由的
arr.a= 5; //属性:属于一个对象
function show() //函数:自由的
{
alert(‘a’);
}
arr.fn = function() //方法:属于一个对象
{
alert(‘b’);
}
其实方法就是函数,只不过方法是有所属的对象。
我们所熟知的,将函数绑定到 click
事件
语法:
$(selector).click(function)
| 参数| 描述|
| ------------- |:-------------:|
| function | 可选。规定当发生 click 事件时运行的函数。 |
这种形式在jquery
中经常见到。它是将function
当做该方法的参数,向该方法添加一个事件处理函数。
js全局函数
全局函数与内置对象的属性或方法不是一个概念。全局函数它不属于任何一个内置对象。
JavaScript 中包含以下 7 个全局函数,用于完成一些常用的功能:
escape( )、eval( )、isFinite( )、isNaN( )、parseFloat( )、
parseInt( )、unescape( )。
函数的几个作用
作为一个类构造器使用
function Class(){}
Class.prototype={};
var item=new Class();
作为闭包使用
(function(){
//独立作用域
})();
作为构造函数调用
所谓构造函数,就是通过这个函数生成一个新对象(object)。
<script type="text/javascript">
function Test(){//大写,以区分普通函数
this.x = 10;
}
var obj = new Test();
alert(obj.x); //弹出 10;
</script>
可以使用 new
运算符结合像 Object()
、Date()
和 Function()
这样的预定义的构造函数来创建对象并对其初始化。面向对象的编程其强有力的特征是定义自定义构造函数以创建脚本中使用的自定义对象的能力。创建了自定义的构造函数,这样就可以创建具有已定义属性的对象。下面是自定义函数的示例(注意 this
关键字的使用)。
function Circle (xPoint, yPoint, radius) {
this.x = xPoint; // 圆心的 x 坐标。
this.y = yPoint; // 圆心的 y 坐标。
this.r = radius; // 圆的半径。
}
调用 Circle 构造函数时,给出圆心点的值和圆的半径(所有这些元素是完全定义一个独特的圆对象所必需的)。结束时 Circle 对象包含三个属性。下面是如何例示 Circle
对象。
var aCircle = new Circle(5, 11, 99);
使用构造器函数的优点是,它可以根据参数来构造不同的对象。 缺点是构造时每个实例对象都会生成重复调用对象的方法,造成了内存的浪费。
<script type="text/javascript">
function Test(name){
this.occupation = "coder";
this.name = name;
this.whoAreYou = function(){
return "I'm " + this.name + "and I'm a " + this.occupation;
}
}
var obj = new Test('trigkit4');//利用同一个构造器创建不同的对象
var obj2 = new Test('student');
obj.whoAreYou();//"I'm trigkit4 and I'm a corder"
obj2.whoAreYou();//"I'm student and I'm a corder"
</script>
依照惯例,我们应该将构造器函数的首字母大写,以便显著地区别于一般的函数。
以下两种形式的定义函数方式是等价的。
<script type="text/javascript">
var test = function(){
alert("Hello World");
}
alert(typeof(test));//output function
</script>
这里明确定义了一个变量test
,他的初始值被赋予了一个function
实体
<br/>
<script type="text/javascript">
function test(){
alert("Hello World");
}
alert(typeof(test));//output function
</script>
<br/>
看看下面这种定义式函数形式:
<script type="text/javascript">
function test(){
alert("Hello World");
};
test();//居然输出Hello,很奇怪不是吗?
function test(){
alert("Hello");
};
test();//正常滴输出了Hello
</script>
很显然,第一个函数并没有起到作用,很奇怪不是吗?我们知道,javascript
解析引擎并不是一行一行地执行代码,而是一段一段地执行代码。在同一段程序的分析执行中,定义式的函数语句会被优先执行,所以第一个定义的代码逻辑已经被第二个覆盖了,所以两次调用相同函数,只会执行第二个。
作为值的函数
函数在js
中不仅是一种语法,也是一个值。也就是说可以将函数赋值给变量,存储在对象的属性或数组的元素中,作为参数传入另一个函数中。
函数的名字实际是看不见的,它仅仅是变量的名字,这个变量指代函数对象
<script type="text/javascript">
function square(x,y){
return x*y;
}
var s = square; //s和square指代同一个函数
square(2,3);//6
s(2,4);//8
</script>
除了可以将函数赋值给变量,同样可以将函数赋值给对象的属性,当函数作为对象的属性调用时,函数就称为方法
<script type="text/javascript">
var obj = {square:function(x,y){ //对象直接量
return x*y;
}};
var ect = obj.square(2,3);
</script>
prototype属性
每一个函数都包含prototype属性,这个属性指向一个对象的引用,这个对象称为原型对象。
详见:javascript学习总结(五)原型和原型链
call()和apply()
apply()
函数有两个参数:第一个参数是上下文,第二个参数是参数组成的数组。如果上下文是null,则使用全局对象代替。例如:
function.apply(this,[1,2,3])
call()
的第一个参数是上下文,后续是实例传入的参数序列,例如:
function.call(this,1,2,3);
高阶函数
这里的高阶函数可不是高数里的那个高阶函数,所谓高阶函数就是操作函数的函数,它接收一个或多个函数作为参数,并返回新函数
参数arguments
当函数被调用时,会得到一个免费奉送的参数数组,那就是arguments
数组。通过它,函数可以访问所有它被调用时传递给他的参数列表。这使得编写一个无需指定参数个数的函数成为可能。
<script type="text/javascript">
var sum = function(){
var i ,sum =0;
for(i = 0;i<arguments.length;i+=1){
sum+=arguments[i];
}
return sum;
};
document.writeln(sum(4,5,23,13,35,46,-10));//116
</script>
在ECMAScript
中的参数在内部是用一个数组来表示的,函数接收到的始终都是这个数组,而不关心数组中包含哪些参数
function add(num1,num2){
num = num1 + num2;
return num;
}
var result = 12,count = 20;
alert(add(result,count));//32;命名的参数只提供便利,解析器不会验证命名参数
实际上,arguments
并不是一个真正的数组,它只是一个类数组的对象,它拥有一个length
属性,但他缺少所有数组的方法。另外,arguments
对象的长度是由传入的参数个数决定的,而不是由定义函数时的命名参数的个数决定的
函数在定义或者声明的时候,所有的参数都是形参,因此,我们可以根据实际情况来命名参数,函数也只有在被调用时才会传入实参。而每个函数在被调用时都会自动取得两个特殊变量:this
函数的递归
函数的递归,即一个函数在通过名字调用自身的情况下构成的:
通过使用argument.callee
代替函数名:
//arguments.callee是一个指向正在执行的函数的指针
<script>
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
</script>
思维导图
最后附上一张前辈总结的思维导图: