返回 登录
0

JavaScript Function 函数深入总结

整理了JavaScript中函数Function的各种,感觉函数就是一大对象啊,各种知识点都能牵扯进来,不单单是 Function 这个本身原生的引用类型的各种用法,还包含执行环境,作用域,闭包,上下文,私有变量等知识点的深入理解。

函数中的return

return 语句可以不带有任何返回值,在这种情况下( return; 或函数中不含 return 语句时),函数在停止执行后将返回 undefiend 值。这种用法一般在需要提前停止函数执行而又不需要返回值的情况下。
return false 可以阻止元素的默认事件。
return 返回的是其所在函数的返回值
function n(){
(function(){
return 5;
})();
}
n();// undefined
//立即执行匿名函数中的return语句其实是返回给它所在的匿名函数的。

function n(){
var num= (function(){
return 5;
})();
console.log(num);
}
Function类型

函数实际上是对象,每个函数实际上都是 Function 类型的实例。而且与其他引用类型一样具有属性和方法。函数名实际上是一个指向内存堆中某个函数对象的指针。

定义函数的方式

函数声明
function sum(num1,num2){
return num1+num2;
}
函数表达式
var sum=function(num1,num2){
return num1+num2;
};
定义了一个变量 sum 并将其初始化为一个函数,注意到 function 关键字后面并没有函数名,这是因为在使用函数表达式定义函数,没必要使用函数名,通过变量 sum 即可引用函数。还要注意函数末尾有个分号,就像声明其他变量一样。

new 构造函数,虽然这种用法也是函数表达式,但该用法不推荐。因为这种语法会导致解析两次代码(第一次是解析常规的ECMAScript代码,第二次是解析传入构造函数中的字符串),影响性能。
使用 Function 构造函数,构造函数可以接受任意数量的参数,但最后一个参数始终都被看成是函数体,前面的参数则枚举出了新函数的参数。
var sum=new Function(‘num1’,’num2’,’return num1+num2;’);
sum;//
function anonymous(num1,num2
/**/) {
return num1+num2;
}
当使用不带圆括号的函数名是访问函数指针,而非调用函数。

理解参数

ECMAScript中所有参数传递的都是值(即使是引用也是传递的地址值,不是引用传递参数(可参考JavaScript传递参数是按值传递还是按引用传递))。ECMAScript函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。之所以这样,是因为ECMAScript中的参数在内部是用一个数组表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内,可以通过 arguments 对象来访问这个数组。从而获取传递给函数的每个参数。

function func(){
console.log(Object.prototype.toString.call(arguments));
}

func();// [object Arguments]
关于 arguments 的行为,它的值永远与对应命名参数的值保持同步。因为 arguments 对象中的值会自动反映到对应的命名参数。所以修改 arguments[1] ,也就修改了 num2 。不过这并不是说读取这两个值会访问相同的内存空间,它们的内存空间是独立的,但他们值会同步(WHY??),要是JavaScript能直接访问内存就好了验证一下。
但如果只传入了一个参数,那么 arguments[1] 设置的值不会反映到命名参数中,这是因为 arguments 对象的长度是由传入参数个数决定的,不是由定义函数时的命名参数个数决定的,没有传递值的命名参数将自动被赋予 undefiend 值,这就跟定义了变量但没初始化一样。
function doAdd(num1,num2){
console.log(arguments.length);
console.log(num2)
arguments[1]=10;
console.log(num2);
}
doAdd(5,0);//2 0 10

doAdd(5);//1 undefiend undefined
没有重载

ECMAScript函数不能像传统意义上那样实现重载,而在其他语言中(Java),可以为一个函数编写两个定义,只要这两个定义的签名(接收参数的类型和数量)不同即可。

不能实现重载的原因:

ECMAScript函数没有签名,因为其参数是由包含零个或多个值的数组来表示的。没有函数签名,真正的重载是不可能做到的。在ECMAScript中定义两个名字相同的的函数,则该名字只属于后定义的函数。如何实现类似于Java中的重载呢,其实可以通过判断传入函数的参数类型和个数来做出不同响应。
function reload(){
if(arguments.length==0){
console.log(‘没传参’);
}else if(arguments.legth==1){
console.log(‘传了一个参数’);
}
}
深入理解:将函数名想象为指针,也有助于理解为什么ECMAScript中没有函数重载的概念。
function add(){
return 100;
}
function add(num){
return num+200;
}

//实际上和下面代码没什么区别
function add(){
return 100;
}
add=function(num){
return num+200;
}
函数声明和函数表达式

实际上解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。

JavaScript运行机制浅探 中了解到对于解释型语言来说,编译步骤为:

词法分析(将字符流转换为记号流,是一对一的硬性翻译得到的是一堆难理解的记号流)
语法分析(这里进行所谓的变量提升操作,其实我觉得是把这些提升的变量保存在语法树中。要构造语法树,若发现无法构造就会报语法错误,并结束整个代码块的解析)
之后可能有语义检查,代码优化等。得到语法树后就开始解释执行了。解释性语言没有编译成二进制代码而是从语法树开始执行。
解析器会先读取函数声明,并使其在执行任何代码之前可用。至于函数表达式,则必须等到执行阶段才会被真正赋值。什么意思呢?虽然两者都进行了变量提升,待真正执行时构造活动对象从语法树种取声明添加到执行环境中,但一个是函数提升,一个是变量提升。

//函数声明
console.log(func);//function func(){}
function func(){

}

//函数表达式
console.log(func1);// undefined
var func1=function(){};
console.log(func1);// function(){}
作为值的函数

因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。

function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}

function concated(str){
return “Hi “+str;
}

callSomeFunction(concated,’xx’);// ‘Hi xx’
从一个函数中返回另一个函数的应用:假设有一个对象数组,想要根据某个对象属性对数组进行排序,但传给 sort() 方法的比较函数要接收两个参数,即要比较的值。我们需要一种方式来指明按照哪个属性来排序。我们可以定义一个函数它接收一个属性名,然后根据这个属性名来创建一个比较函数。默认情况下, sort 函数会调用每个对象的 toString() 方法以确定它们的次序。

function createCompare(property){
return function(obj1,obj2){
var value1=obj1[property],
value2=obj2[property];
if(value1

评论