function fn(num){
if(1 == num){
return 1;
}else{
fn = function(){
return 0;
};
return num * fn(num - 1);
}
}
console.info(fn(5));//0
关于作用域和声明提升,再看一个例子:
复制代码 代码如下:
var name = 'linjisong';
function fn(){
console.info(name);//undefined
var name = 'oulinhai';
console.info(name);//oulinhai
}
fn();
console.info(name);//linjisong
这里最不直观的可能是第3行输出undefined,因为在全局中已经定义过name了,不过按照上面解析的步骤去解析一次,就可以得出正确的结果了。另外强调一下,在ECMAScript中只有全局执行环境和函数执行环境,相应的也只有全局作用域和函数作用域,没有块作用域——虽然有块语句。
复制代码 代码如下:
function fn(){
var fnScope = 'a';
{
var blockScope = 'b';
blockScope += fnScope;
}
console.info(blockScope);//没有块作用域,所以可以在整个函数作用域内访问blockScope
console.info(fnScope);
}
fn();//ba,a
console.info(blockScope);//ReferenceError,函数作用域外,不能访问内部定义的变量
console.info(fnScope);//ReferenceError
对于作用域链,还可以使用with、try-catch语句的catch块来延长:
•使用with(obj){}语句时,将obj对象添加到当前作用域链的最前端。
•使用try{}catch(error){}语句时,将error对象添加到当前作用域链的最前端。
插了一段较为抽象的概念,希望不至于影响整个阅读的流畅,事实上,我在这里还悄悄的绕过了一个称为“闭包”的概念,关于函数与闭包,在下篇文章中再详细叙述。
7、函数内部对象与this
对于面向对象语言的使用者来说,this实在是再熟悉不过了,不就是指向构造函数新创建的对象吗!不过,在ECMAScript中,且别掉以轻心,事情没有那么简单,虽然在使用new操作符调用函数的情况下,this也的确是指向新创建的对象,但这只是指定this对象值的一种方式而已,还有更多的方式可以指定this对象的值,换句话说,this是动态的,是可以由我们自己自由指定的。
(1)全局环境中的this
在全局环境中,this指向全局对象本身,在浏览器中也就是window,这里也可以把全局环境中的this理解为全局执行环境相应的变量对象,在全局环境中定义的变量和函数都是这个变量对象的属性:
复制代码 代码如下:
var vo = 'a';
vo2 = 'b';
function fn(){
return 'fn';
}
console.info(this === window);//true
console.info(this.vo);//a
console.info(this.vo2);//b
console.info(this.fn());//fn
如果在自定义函数中要引用全局对象,虽然可以直接使用window,但更好的方式则是将全局对象作为参数传入函数,这是在JS库中非常通用的一种方式:
复制代码 代码如下:
(function(global){
console.info(global === window);//在内部可以使用global代替window了
})(this);
这种方式兼容性更好(ECMAScript的实现中全局对象未必都是window),在压缩时,也可以将global简化为g,而不用使用window了。
(2)函数内部属性this
在函数环境中,this是一个内部属性对象,可以理解成函数对应的活动对象的一个属性,而这个内部属性的值是动态的。那this值是怎么动态确定的呢?
•使用new调用时,函数也称为构造函数,这个时候函数内部的this被指定为新创建的对象。
复制代码 代码如下:
function fn(){
var name = 'oulinhai';//函数对应的活动对象的属性
this.name = 'linjisong';//当使用new调用函数时,将this指定为新创建对象,也就是给新创建对象添加属性
}
var person = new fn();
console.info(person.name);//linjisong
var arr = [fn];
console.info(arr[0]());//undefined
需要注意区分一下函数执行环境中定义的属性(也即活动对象的属性)和this对象的属性,在使用数组元素方式调用函数时,函数内部this指向数组本身,因此上例最后输出undefined。
•作为一般函数调用时,this指向全局对象。
•作为对象的方法调用时,this指向调用这个方法的对象。
看下面的例子:
复制代码 代码如下: