Js中的函数类型及声明和表达式

Js中比较有趣的恐怕就是函数类型了:

function value(){
return value;
}
alert(typeof value);    //“function”

typeof是一个获得操作数类型的操作符,打印结果是function类型,这和传统的c和c++之类的编程语言不一样,在Js中,function是一个类型。并且,实际上也是一个对象,因为,每一个函数实际上也是Object对象的继承。所以,函数也有自己的属性,最典型的就是this和argument属性,this实际上也是一个对象。对于函数而言,函数名只是一个指针,所以,当你要引用函数而不是运行函数时,就可以去掉后面的括号,比如将一个函数赋值给一个变量:

var sum=function(num1,num2){
return num1+num2;
};

注意,函数没有函数名,因为函数名在这里只是一个指针,而该函数已经赋给了sum,并且,变量也是一个指针,所以,sum可以代替函数,并且,就像初始化语句一样,在这个匿名函数末尾也有分号

函数不能重载,这和C++是截然不同的,但是ECMAscript允许用户定义同名函数,就像刚刚说的,函数名只是指针,就像常规指针一样,后定义的函数会覆盖前面的同名函数,就像指针赋值一样,并且由于Js的垃圾回收机制,当前面的函数已经没有指针调用的时候,他的执行环境也就结束了,会被自动回收掉,所以不用担心内存问题。

Js对于函数是十分宽容的,js解释器会在代码运行前把所有函数声明放到源代码树的顶端,所以:

alert(sum(10,10));
function sum(num1,num2){
return num1+num2;
}

这种写法仅适用于函数声明,如果是函数表达式将会报错。
这样的语句是可以运行的,因为在运行代码开始,或者说还没有运行的时候,Js解释器会先把所有的函数声明放到代码的最前端,所以,alert是认识sum的
也可以在一个函数内部写一个匿名函数作为返回值。

即然函数是一种对象,那么他也和其他对象一样有属性和方法每一个函数对象的实例都有length和prototype属性,并且有两个内置对象,this和argument,argument又有一个callee属性,argument对象中保存了这个函数的参数,他的callee属性是一个指针,指向拥有该argument对象的函数,在函数内部,为了降低函数内语句和函数名的耦合,通常用argument.callee来代替函数名。this 是一个对象,this引用的是当前函数据以执行的环境对象,如果该全局作用域函数没有调用对象,那么默认是window,如果有,则是调用对象。

如果是在另一个作用于内定义的函数默认是该环境对象
PS:Js中,while,for,if,不享有单独作用域,分割作用域的只有函数。

执行环境是Js中一个很重要的概念,执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的隐藏的仅后台可见的变量对象。或者说执行环境是一个宏观概念,生成的临时的变量对象是具体的实现。

全局执行环境是最外围的环境,在web浏览器中体现为Window对象。因此,全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完后,执行环境会被销毁,保存其中的变量和函数也会被销毁。

每个函数都有自己的执行环境,当执行流进入一个函数中时,函数的环境就会被推入一个环境栈,本质上是将变量对象指针压入栈,并且在变量对象中创建作用域链,作用域链也是一个指针列表,作用于链的前端永远是当前变量对象的引用,最后端是全局作用域,由内到外一层层包裹,每次this查找都会从前端开始,所以保证了this永远引用活动对象

window.color="red";
var o={color:"blue"};
function sayColor(){
    alert(this.color);
}

sayColor();  //red
o.sayColor=sayColor;
o.sayColor();  //blue

闭包
闭包是指有权访问另一个函数作用域的变量的函数,创建闭包的常见方式是在函数内部创建一个函数。
下面来看一个闭包的例子:

function createFunctions(){
    var result=new Array();
    for(var i=0;i<10;i++){
        result[i]=function (){
            return i;
        }
    }
    return result;

}

在这个函数中,我们首先新建了一个数组对象result,然后写了一个for循环,注意,JS中for循环不享有块级作用域,所以for内声明的i可以在for外,只要在函数内就行。然后我们在for内写了一个匿名函数返回i,又在for外返回i那么一般来看,似乎result每一个索引对应的元素保存的函数所返回的值应该是随索引不同而不同的,也就是说,result[0]返回0,result[1]返回1,以此类推,但是实际上每一个函数都返回10,这是因为,我们每在一个函数内部调用一个新函数,前面说过,会将环境栈压入并且创建变量对象,对于函数来说就是活动对象,活动对象一个重要属性就是作用域链,作用域链保存了从全局作用域到当前活动对象的所有引用,所以,对于闭包来说,只要最内层的函数没有消亡,那么,即便外部函数已经退出了,但是内部函数还是保存了他的引用,所以不会被回收掉,外部函数仍然存在,外部变量仍然存在,并且,活动对象保存的是变量本身而不是值,换句话说,变量总是保持最新的值,那么变量对象保存的变量的引用所得到的值也是最新的也就是10.那么我们怎么做才能让他正常工作呢?
下面是改进版:

function createFunction(){
    var result=new Array();
    for(var i=0;i<10;i++){
        result[i]=function(num){
            return function(){
                return num;
            }
        }(i);
    }
    return result;
}

这里首先讲一下,一般情况下函数是先声明后调用,如果想让函数在声明时就运行,可以用下面的语法:

(function funName(){}());
(function funName(){})();

上面的语法任意一种都可以,在最后一个括号中传入参数(如果有的话)。
我们在看上面的代码,改进之处在于,在赋值语句右边的匿名函数内部有创建了一个匿名函数,并且每次循环都立即运行,所以每次都返回一个最新的i值。注意到,内部匿名函数能够访问外部函数的变量,这是因为,首先,匿名函数因为没有调用对象,所以被看作是全局作用域(通过变量调用另说)。并且,内部函数被调用时创建的活动对象会包含整条作用域链,因此存储了外部函数的活动对象,也就保存了外部函数的变量的引用,所以可以访问。

每次运行for循环,都会执行赋值语句并且运行匿名函数,在匿名函数内部返回一个以当前i值的副本为参数的函数并保存在result[]数组的相应元素中,换句话说,每次立即运行都会生成被赋值为当前i值得num的副本保存在函数中,并付给result。

闭包的另一个作用是构建私有变量。

严格来说,Js中没有私有概念,但是,函数享有块级作用域,所以,在函数内部创建一个变量,外部是不可知的,然后通过闭包等技术,可以实现包含私有变量的对象的创建。

(function(){
    var name="1";
    Person=function(value){
        name=value;
    };
    Person.prototype.getName=function(){
        return name;
    };
    Person.prototype.setName=function(value){
        name=value;
    };


}());
var person1=new Person("Nicholas");
alert(person1.getName());   //Nicholas
person1.setName("Greg");
alert(person1.getName());    //Greg

当一个变量前面没有var关键字时,默认是全局作用域,(在严格模式下会报错)。所以Person是可以在外部调用,创建对象,并且包含了两个公有方法访问私有属性。

对于一些用于管理程序级的信息的对象,一般是可以单例,即,只创建一个该对象的实例,此时:

var singletion=(function(){
    var privateVariable=10;
    function privateFunction(){
        return false;
    }
    return {
        publicProperty:true,
        publicMethod:function(){
            privateVariable++;
            return privateFunction();
        }
    }
})();

或者也可以在函数内创建一个Object对象并返回。

var singletion=(function(){
    var privateVariable=10;
    function privateFunction(){
        return false;
    }
    var obj=new CustomType();
    obj.publicProperty=true;
    obj.publicMethod=function(){
        privateVariable++;
        return privateFunction();       
    }
    return obj;

})();

最后,我们可以想到,多查找作用域链的一个层次,就会多耗费一定的时间和内存资源,所以,闭包和私有变量使用应当谨慎。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值