[JavaScript]几个函数的小知识
函数声明和函数表达式的区别
函数声明
function functionName(arg0, arg1, arg2) {
//函数体
}
关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。
函数表达式
var functionName = function(arg0, arg1, arg2) {
//函数体
};
上面这种情况下创建的函数叫做匿名函数。
看下面这段代码,它会导致很多错误,
//这是错的
if(condition){
function sayHi() {
console.log("Hi");
}
}else {
function sayHi() {
console.log("hi!!");
}
}
因为函数声明提升问题,大部分浏览器会返回第二个声明(忽略condition),Firefox会在condition为true时返回第一个声明。
下面使用函数表达式就不会发生这种错误。
//这是对的
var sayHi;
if(condition){
sayHi = function() {
console.log("Hi");
};
}else {
sayHi = function() {
console.log("hi!!");
};
}
还可以把函数作为其他函数的值返回。
function A(propertyName){
return function(obj1, obj2){
var value1=obj1[propertyName];
var value2=obj2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
};
}
递归
递归函数时一个函数通过名字调用自身的情况下构成的!
function factorial(num){
if(num<=1){
return 1;
}else{
return num * factorial(num-1);
}
}
这是一个典型的递归求阶乘的函数。
但是如果我加入这串代码,就会出错
var anotherFactorial = factorial;
factorial = null;
anotherFactorial(5);//出错!!!
错误原因是return num * factorial(num-1);
返回的是factorial,而它已经不是函数了。
改成return num * arguments.callee(num-1);
就可以了
在严格模式下,不允许使用arguments.callee
,可以使用命名函数表达式来达成相同的结果
作用域链
当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后使用argument
和其他命名参数的值来初始化函数的活动对象。
后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,而像com[are()函数这样的局部环境的变量对象,则只在函数执行的过程中存在。
在创建compare()
函数时,会创建一个预先包含全部变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]
属性中。 当调用compare()
函数时,会为函数创建一个执行环境,然后通过赋值函数的[[Scope]]
属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。
作用域链本质上是一个指向变量对象的指针列表。
关于this对象
匿名函数的执行环境具有全局性,因此其this对象通常指向window
还有三个一般的规律
- 对于全局的方法调用,this指向的是全局对象window,即调用方法所在的对象
- 如果函数作为对象的方法调用,this指向的是这个上级对象,即调用方法的对象
- 构造函数中的this指向新创建的对象本身