JavaScript的函数详解

         函数,在C语言之类的过程式语言中,是顶级的实体,而在Java/C++之类的面向对象的语言中,则被对象包装起来,一般成为对象的方法.在JavaScript中,函数本身与其他任何的内置对象在地位上是没有任何区别的,也就是说函数本身也是对象.总的来说,函数在JavaScript中可以:
              被赋值给一个变量;
              被赋值为对象的属性;
              作为参数被传入别的函数;
              作为函数的结果被返回;
              用字面量来创建.
  一.创建函数
          创建JavaScript函数的一种不常见的方式是通过new操作符来作用于Function"构造器".

var add=new Function("x","y","return(x+y)");
print(add(2,4));
将会打印结果:
6

          参数列表可以有任意的参数,然后紧跟着是函数体,,如果函数体比较复杂,那拼接String需要花费很大的力气,所以JavaScript提供了一种语法糖,即通过字面量来创建函数. 

function add(x,y){
    return x+y;
}或:
var add=function(x,y){
return  x+y;
}

           function关键字会调用Function来new一个对象,并将参数表和函数体准确地传递给Function的构造器.通常来说,在全局作用域内声明一个对象,只不过是对一个属性赋值而已,比如上例中的add函数,事实上只是为全局对象加了一个属性,属性名为add,而属性的值是一个对象,即function(x,y){return x+y}
           函数跟其他的对象一样,都是作为一个独立的对象存在于JavaScript的运行系统,例如:

function p(){
    print("invoke p by ()");
}
p.id="func";
p.type="function";
print(p);
print(p.id+":"+p.type);
print(P());
运行结果如下:
function (){
    print("invoke p by ()");
}
func:function
invoke p by ()
//p函数引用了一个匿名函数(对象),但是同时又可以拥有属性,这些属性不会影响函数本身的功能,完全跟其他对象一样,

 二.函数作用域
       JavaScript中的变量作用域为函数体内有效,而无块作用域.我们在Java函数方法中使用两个for循环时可以使用相同的变量名(如i),而 JavaScript就不可以.JavaScript的函数是在局部作用域内运行的,在局部作用域内运行的函数体可以访问其外层(可能是全局作用域)的变量和函数. JavaScript的作用域为词法作用域,其作用域是在定义时就确定下来的,而非在执行时确定,例如:

var  str="global";
function  scopeTest(){
print(str);
var str="local";
print(str);
}
scopeTest();
运行结果为:
undefined
local

       为什么函数这个时候不去访问外部的str变量呢?因为在词法分析结束后,构造作用域链的时候会将函数内定义的var 变量放入该链,因此在整个函数内部是可见的(从函数的第一行到最后一行),由于str变量本身是未定义的,程序顺序进行,到第一行就会返回未定义.
三.调用对象
        在JavaScript中,在所有函数之外声明的变量为全局变量,而在函数内部声明的变量(通过var关键字)为局部变量.在执行一个函数时,函数的参数和其局部变量会作为调用对象的属性进行存储.同时,解释器会为函数创建一个执行器上下文.与上下文对应的是一个作用域链.作用域链是关于作用域的链,通常实现为一个链表,链表的每个项都是一个对象,即全集对象.对应的,在一个函数中,作用域上会有两个对象,第一个(首先被访问到的)为调用对象,第二个为全局对象.
        当函数需要用到某个变量时,解释器会遍历作用域链,例如:

var  str="global";
function  scopeTest(){
    print(str);
    var str="local";
    print(str);
}

        当解释器进入scopeTest函数的时候,一个调用对象就被创建了,其中包含了str变量作为其中的一个属性并被初始化为underfined,当执行到第一个print(str)时,解释器会在作用域链中查找str,找到之后,打印其值为underfined,然后执行赋值语句,此时调用对象的属性str会被赋值为local,因此第二个print(str)语句会打印local.
四. call和apply
        call和apply通常用来修该函数的上下文,函数中的this指针将被替换为call和apply的第一个参数,例如:

//定义一个人,名字为zhaoyanzhi
var zhaoyanzhi={
name : "zhaoyanzhi",
age : 23,
}
//定义另一个人,名字为zhayanwen
var  zhaoyanwen={
name : "zhaoyanwen",
age : 17
}
//定义一个全局的函数对象
function printName(){
return this.name;
}
//设置printName的上下文为zhaoyanzhi,此时的this为zhaoyanzhi
print(printName.call(zhaoyanzhi));
//设置printName的上下文为zhaoyanwen,此时的this为zhayanwen
print(printName.call(zhayanwen));
print(printName.apply(zhaoyanzhi));
print(printName.apply(zhayanwen));
只有一个参数的时候call和apply的使用方法是一样的,如果有多个参数:
setName.apply(zhaoyanzhi,["yanzhi"])
print(printName.apply(zhaoyanzhi));
setName.call(zhaoyanwen,"yanwen")
得到的结果为:
yanzhi
yanwen
apply的第二个参数为一个函数需要的参数组成的一个数组,而call则需要跟若干个参数,参数之间以逗号
(,)隔开

 五.使用函数

   (1)赋值给一个变量

function add(x,y){
return x+y;
}
var a=0;
a=add; //将函数赋值给一个变量,a的值是一个对象
var b=a(2,3); //调用这个新的函数a
print(b);  //打印的结果为5

   (2)赋值为对象的属性

var obj={
id:"obj1"
}
obj.func=add; //赋值为obj对象的属性
obj.func(2,3); //返回5

    (3)作为参数传递

function adPrint2(str handler){
print(handler(str));
}
//将字符串转换为大写形式,并返回
function  up(str){
return str.toUpperCase();
}
//将字符串转换为小写的形式,并返回
function low(str){
return str.toLowerCase();
}
adPrint2("hello, world", up);
adPrint2("hello,world",low);
运行结果为:
HELLO, WORLD
hello,world

   (4)作为函数的返回值

function currying(){
  return function(){
      print("curring");
}
}

     函数currying返回一个匿名函数,这个匿名函数会打印"curring",简单地调用currying()会得到下面的结果.

function(){
   print("curring");
}

     这个结果本身就是一个匿名函数,如果要掉用currying返回的这个匿名函数,需要这样:

          currying()();

     第一个括号操作,表示调用currying本身,此时返回值为函数,第二个括号操作符调用这个返回值,结果为:

           currying

相关推荐