1. Function
函数接受的参数类型、个数没有限定。参数在内部是用数组arguments来存储的。因此函数没有重载。可以提供arguments【i】来表示传入的参数
4.1创建
使用函数声明和函数表达式创建,也可以使用Function()构造函数
Function sum(num1,num2){
函数声明
}
Var sum = function(num1,num2){
函数表达式
}
解析器会率先读取函数声明,并使其在执行任何代码之前可用;因为在代码执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境,js引擎在第一遍会声明函数并将它们放到源代码树的顶部。函数表达式须等到解析器执行到它所在代码行,才会真正被解析。
4.2函数内部属性
Arguments指代参数
Callee:arguments.callee指代拥有这个arguments对象的函数。在递归调用时,非常有用。
Caller:保存着调用当前函数的函数的引用
Function outer(){
Inner();
}
Function inner(){
Alert(inner.caller);//警告框中显示outer()函数的源代码,等价于arguments.callee.caller
}
This:this引用的是函数据以执行的环境对象。
4.3 call()和apply()扩大函数赖以运行的作用域
每个函数都包含这两个非继承而来的方法。这两个方法用途都是在特定的作用域里调用函数,实际上是设置函数体内的this值。
Apply()接受2个参数,一个是其中函数运行的作用域,另一个是参数数组
Call()接受多个参数,一个是其中函数运行的作用域,其他是传递给函数的参数必须逐个列举出来
Window.color = “red”;
Var o = {
Color:”blue”
};
Function sayColor(){
Alert(this.color);
}
sayColor(); //red
sayColor.call(window); //red
sayColor.call(this); //red
sayColor(o); //blue
4.4java与js创建函数
Java是:修饰符 返回类型 函数名(参数类型 参数名){
函数体
}
Js是:函数声明和函数表达式
函数声明:function 函数名(参数名){
函数体
}
函数表达式:var 函数名 = function(参数名){
函数体
}
4.5匿名函数
在function后面没有跟函数名的函数称为匿名函数。例如:
function(){
函数体
}
1.6 闭包
4.6.1闭包作用域链
闭包是有权访问另一个函数作用域中变量的函数。因此任何函数都是闭包。当某函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性即[[scope]]。然后使用this、arguments和其他命名参数的值来初始化函数的活动对象。
由此得出,js中所有函数都是闭包。
在全局环境中定义compare函数:
function compare(value1,value2){
if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else{
return 0;
}
}
var result = compare(5,10);
在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,并保存在[[scope]]。当调用compare()函数时,会创建一个包含this、arguments、value1、value2的活动对象。此刻compare()执行环境的作用域链(scope)是[[scope]]+活动对象。Scope可以看出数组,活动对象在最前面,[[scope]]在后面。
函数在创建时获得函数的[[scope]],通过该属性访问到所有父上文的变量。同一个父上下文中创建的闭包是共用一个[[scope]],其中一个闭包对[[scope]]修改会影响到其他闭包
每个执行环境都有一个表示变量的对象,全局环境的变量对象一直存在,局部函数的执行环境也有变量对象,但只在函数执行的过程中存在(闭包除外)。
4.6.2闭包中的变量
当你弄清作用域链,你对闭包中变量的取值将会很清楚。
在这里,我将再举一个例子,说明下作用域链。
var x = 10;
function foo(){
var y = 20;
function bar(){
var z = 30;
alert(x+y+z);
}
bar();
}
foo();
在创建foo函数时,此时会创建一个包含x为10的全局作用域。
同一个父上下文中创建的闭包是共用一个[[scope]],其中一个闭包对[[scope]]修改会影响到其他闭包。
function fee(){
var data = [];
for(var k=0;k<3;k++){
data[k] = function(){
return k
}
}
return data;
}
console.log(fee());//返回值如下
/*[function (){
return k
}, function (){
return k
}, function (){
return k
}]*/
由此可见,返回的是k值,而这个k存在父上下文中,所以data里存相同的数值。
当将data[k] = function(){return k}改为
data[k] = function(num){
return function(){return num}
}(k);此时data存不同的值。
4.6.2关于this对象
在全局函数中,this等于window,而当函数被某个对象调用时,this指代那个对象。但是匿名函数不一样,他的执行环境具有全局性,其this指代window。
var name = "lily";
var object = {
name : "my name",
getName : function(){
alert(this.name); //my name
return function(){
return this.name; //lily
}
}
};
alert(object.getName()()); //my name,lily
在每个函数被调用时其活动对象都会自动获取this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,不可能去访问父上下文。
看下面的例子
var name = "lily";
var person = {
name : "my name",
getName : function(){
var that = this;
return function(){
return this.name; //my name }
}
};
alert(person.getName()()); //my name
此刻就可以让闭包访问person对象了。
4.6.2模仿块级作用域
Js没有块级作用域,这个跟其他面向对象语言不一样。
例如: for(var i = 0 ; i< 3; i++){
alert(i);
}
alert(i); //3
用块级作用域(通常称为私有作用域)的匿名函数的语法如下:
(function(){
//这里是块级作用域。这里的变量一旦等到该函数执行完就被销毁。
})();
function(){}();会报错,这是因为js会将function关键字当做函数声明的开始,而函数声明后面不能加()。而函数表达式后面可以跟(),例如:
var lulu = function(){alert(895);}();
只要是临时需要变量,可以使用私有作用域。
例如: function out(count){
(function(){for(var i = 0 ; i< count; i++){
alert(i);
})();
alert(i);//此时会报错,i是一个没有声明的变量。
}
out(3);
变量i只能在循环中使用,使用后被销毁。而在私有作用域中能过访问父上下文的变量,因为匿名函数是一个闭包。
这种技术经常用在函数外部,从而限制全局作用域中添加过多的变量和函数。减少闭包占用内存的问题。