标签:
(1) 函数声明
函数实际上是对象。
每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。
由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
a. 使用函数声明语法定义;
function sum(num1,num2){
return num1+num2;
}
b. 使用函数表达式定义函数;
var sum=function(num1,num2){
return num1+num2;
};
//注意:function关键字后面没有函数名;函数末尾有一个分号;
c. 使用Function构造函数定义函数;
var sum=new Function("num1","num2","return num1+num2");
//Function构造函数可以接收任意数量的参数,但最后一个参数始终被看成是函数体,而前面的参数则枚举了新函数的参数。
//不推荐使用这种方法定义函数,因为这种语法会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数的字符串),从而影响性能。
(2) 没有重载(深入理解)
在创建第二个函数时,实际上覆盖了引用第一个函数的变量。
function addSomeNumber(num){
return num+100;
}
function addSomeNumber(num){
return num+200;
}
var result=addSomeNumber(100);//300
(3) 函数声明与函数表达式:
解析器在向执行环境加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
console.log(sum(10,10));
function sum(num1,num2){
return num1+num2;
}
//以上代码完全可以正常运行。因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。
console.log(sum(10,10));
var sum=function(num1,num2){
return num1+num2;
}
//以上代码会在运行期间产生错误
除了什么时候可以通过变量访问函数这一点区别之外,函数声明与函数表达式的语法其实是等价的。
也可以同时使用函数声明和函数表达式,例如var sum=function sum(){}。不过 ,这种语法在safari中会导致错误。
(4) 作为值的函数:
function callSomeFunction(someFunction,someArgement){
return someFunction(someArgement);
}
//这个函数接受两个参数。第一个参数应该是一个函数,第二个参数应该是要传递给函数的一个值。
function callSomeFunction(someFunction,someArgement){
return someFunction(someArgement);
}
function add10(num){
return num+10;
}
var result1=callSomeFunction(add10,10);
console.log(result1); //20
function getGreeting(name){
return "Hello,"+name;
}
var result2=callSomeFunction(getGreeting,"pengjielee");
console.log(result2); //"Hello,pengjielee"
要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。
//应用场景,我们有一个对象数组,我们想要根据某个对象属性对数组进行排序。
而传递给数组sort()方法的比较函数要接收两个参数,即要比较的值。可是,我们需要一种方式来指明按照哪个属性来排序。
function createComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
}
var data=[{name:"jim",age:20},{name:"tom",age:23}];
data.sort(createComparisonFunction("name"));
console.log(data[0].name);
data.sort(createComparisonFunction("age"));
console.log(data[0].age);
(5) 函数内部属性:
在函数内部,有两个特殊的对象:arguments和this。
arguments它是一个类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
//定义阶乘函数一般都要用到递归算法;在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名factorial紧紧耦合在了一起。为了消除这种紧密耦合的现象,可以这样使用arguements.callee。
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
函数内部的另一个特殊对象是this。this引用的是函数据以执行的环境对象——或者也可以说是this值。
window.color="red";
var o={color:"blue"};
function sayColor(){
console.log(this.color);
}
sayColor(); //"red"
o.sayColor=sayColor;
o.sayColor(); //"blue"
ECMAScript 5也规范化了另一个函数对象的属性:caller。除了Opera的早期版本不支持,其他浏览器都支持这个ECMAScript 3并没有定义的属性。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。
function outer(){
inner();
}
function inner(){
console.log(inner.caller);
}
outer();
//以上代码会导致显示outer()函数的源代码。因为outer()调用了inter(),所以inner.caller就指向outer()。为了实现更松散的耦合,也可以通过arguments.callee.caller来访问相同的信息。
function outer(){
inner();
}
function inner(){
console.log(arguments.callee.caller);
}
outer();
IE、Firefox、Chrome和Safari的所有版本以及Opera 9.6都支持caller属性。
当函数在严格模式下运行时,访问arguments.callee 会导致错误。ECMAScript 5还定义了arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始是undefined。定义这个属性是为了分清arguments.caller和函数的caller属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。
严格模式还有一个限制:不能为函数的caller属性赋值,否则会导致错误。
(6) 函数属性和方法:
每个函数都包含两个非继承而来的方法:apply()和call()。
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。
function sum(num1,num2){
return num1+num2;
}
function callSum(num1,num2){
return sum.call(this,num1,num2);//传入的是一个个参数
}
function callSum1(num1,num2){
return sum.apply(this,arguments); //传入arguments对象
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2]); //传入数组
}
console.log(callSum(10,10));//20
console.log(callSum1(10,10));//20
console.log(callSum2(10,10));//20
call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来。
至于是使用apply()还是call(),完全取决于你采取哪种给函数传递参数的方式最方便。如果你打算直接传入arguments对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方便;否则,选择call()可能更合适。
事实上,传递参数并非apply()和call()的真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。
window.color="red";
var o={color:"blue"};
//全局函数定义
function sayColor(){
console.log(this.color);
}
sayColor(); //red
//显式地在全局作用域中调用函数
sayColor.call(this); //red
//显式地在全局作用域中调用函数
sayColor.call(window); //red
//函数的执行环境改变,函数体内的this对象指向了o
sayColor.call(o); //blue
使用call()或apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。
ECMAScript5还定义了一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。
window.color="red";
var o={color:"blue"};
//全局函数定义
function sayColor(){
console.log(this.color);
}
//bind()方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值
var objectSayColor=sayColor.bind(o);
objectSayColor(); //blue
支持bind()方法的浏览器有IE9+、Firefox4+、Safari5.1+、Opera12+和Chrome。
标签:
原文地址:http://www.cnblogs.com/pengjielee/p/4440949.html