每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会于某个函数绑定。
函数的定义方式
<script type="text/javascript" charset="utf-8">/*** 每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。* 由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会于某个函数绑定。* 函数通常是使用函数声明语法定义的,如下面的列子所示:*//*** @example 1:函数声明式定义函数* * * @param {Object} num1* @param {Object} num2*/function sum(num1,num2){return num1+num2;}/*** @example 2: 函数表达式式定义函数* * @param {Object} num1* @param {Object} num2* @note: 代码声明了一个sum变量并将其初始化为一个函数*/var sum = function(num1,num2){return num1+num2; };/*** @example 3:Function构造函数定义函数(不推荐使用)* * @syntax : var functionName = Function("参数1","参数2","参数3",...,"函数体");* @note:Function构造函数中最后一个参数是函数构造的函数体,其他的参数是函数构造的参数。* 从技术角度讲,这是一个函数表达式。但是,我们不推荐使用这种方法定义函数,因为这种语法会导致解析两次代码* (第一次是解析常规ECMAScript代码,第二次是解析传入函数中的字符串),从而影响性能。不过,这种语法对于理解* "函数是对象,函数名是指针"的概念是很非常直观的。*/var sum = Function("num1","num2","return num1+num2;");
</script>
函数定义方式效率比较
<script type="text/javascript" charset="utf-8">//函数表达式式定义函数var start = Date.now();for(var i=0;i<10000;i++){var sum = function(num1,num2){return num1 + num2;};}var stop = Date.now();console.log("(10000次) 函数表达式式定义函数,总耗时: "+(stop-start));var start = Date.now();for(var i=0;i<100000;i++){var sum = function(num1,num2){return num1+num2; };}var stop = Date.now();console.log("(100000次)函数表达式式定义函数,总耗时: "+(stop-start));//函数声明式定义函数var start = Date.now();for(var i=0;i<10000;i++){function sum(num1,num2){return num1+num2;}}var stop = Date.now();console.log("(10000次) 函数声明式定义函数,总耗时: "+(stop-start));var start = Date.now();for(var i=0;i<100000;i++){function sum(num1,num2){return num1+num2;}}var stop = Date.now();console.log("(100000次)函数声明式定义函数,总耗时: "+(stop-start));//Function构造函数定义函数var start = Date.now();for(var i=0;i<100;i++){var sum = Function("num1","num2","return num1+num2;");}var stop = Date.now();console.log("(100次) Function构造函数定义函数,总耗时: "+(stop-start));var start = Date.now();for(var i=0;i<1000;i++){var sum = Function("num1","num2","return num1+num2;");}var stop = Date.now();console.log("(1000次)Function构造函数定义函数,总耗时: "+(stop-start));/********************* 【开始】运行结果 ********************(10000次) 函数表达式式定义函数,总耗时: 6(100000次)函数表达式式定义函数,总耗时: 47(10000次) 函数声明式定义函数,总耗时: 12(100000次)函数声明式定义函数,总耗时: 113(100次) Function构造函数定义函数,总耗时: 611(1000次)Function构造函数定义函数,总耗时: 5095********************* 【结束】运行结果 ********************/
</script>
不同定义方式的函数解析执行顺序
<script type="text/javascript" charset="utf-8">/*** 解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。* 解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);* 至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析器执行。* * 例子如下:* 例子分析:受执行顺序的影响,(函数是对象,函数名是指针)getName被后执行的表达式式定义的函数覆盖*/var getName = function(){return "李四";};console.log(getName());function getName(){return "张三";}console.log(getName());/********************* 【开始】运行结果 ********************李四李四********************* 【结束】运行结果 ********************/
</script>
函数共有的属性和方法
属性:
1、length:值为函数希望接受的参数的个数;
2、caller:保存着调用当前函数的函数的引用;
3、prototype:每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和方法。在ECMAScript 5 中,prototype 属性是不可枚举的。
方法:
1、apply(socpe,argyArray):在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
第一个参数(scope)表示运行函数的作用域,第二个参数是一个Array实例,也可以是arguments对象(函数调用时的参数)。
2、call(scope,arg1,arg2,arg3....):在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
第一个参数(scope)表示运行函数的作用域,后面的参数都是函数调用时的参数。
<script type="text/javascript" charset="utf-8">/*** arguments 它是一个类似数组对象,包含着传入函数中的所有参数。*/function sum(){var total = 0;for(var i=0;i<arguments.length;i++){total+=arguments[i];}return total;}console.log("sum(1): "+sum(1));console.log("sum(1,2): "+sum(1,2));console.log("sum(1,2,3): "+sum(1,2,3));console.log("sum(1,2,3,4): "+sum(1,2,3,4));/********************* 【开始】运行结果 ********************sum(1): 1sum(1,2): 3sum(1,2,3): 6sum(1,2,3,4): 10********************* 【结束】运行结果 ********************/
</script>
<script type="text/javascript" charset="utf-8">/*** 定义阶乘函数* @param {Object} num* * @note: 下面定义阶乘的函数例子中,函数名字与函数执行体存在紧密的耦合* 在一起,因此想用其他的函数名时则需要修改函数体代码。JavaScript提供* 了一种方式消除这种紧密耦合现象,就是使用 arguments.callee*/function factorial(num){if(num <= 1){return 1;}else{return num*factorial(num-1);}}console.log("factorial(5): "+factorial(5));/*** 定义阶乘函数(函数名与代码体无耦合关系)* arguments.callee: arguments的属性,保存着arguments的所属函数的引用,即 arguments.callee 等价于 arguments对象所属函数。* @param {Object} num*/function factorial1(num){if(num <= 1){return 1;}else{return num * arguments.callee(num-1);}}console.log("factorial1(5): "+factorial1(5));/********************* 【开始】运行结果 ********************factorial(5): 120factorial1(5): 120********************* 【结束】运行结果 ********************/
</script>
<script type="text/javascript" charset="utf-8">/*** function 中this指向的是函数的调用者,即谁调用了函数this就代表谁;*/window.name = "我乃是window";var zhangsan = {name:"我乃是张三"};var lisi = {name:"我是李四"};function getName(){return this.name;}console.log(getName.call(this));console.log(getName.call(window));console.log(getName.call(zhangsan));console.log(getName.call(lisi));/********************* 【开始】运行结果 ********************我乃是window我乃是window我乃是张三我是李四********************* 【结束】运行结果 ********************/
</script>