引言:函数就是对象。函数不同于其他对象的决定性特点是函数存在一个被称为[[call]]的内部属性。内部属性无法通过代码访问而是定义了代码执行时的行为。[[call]]属性是函数独有的,表明该对象可以被执行。理解函数的行为是理解javascript 的核心。
2.1 声明还是表达式
函数有两种字面形式。第一种:函数声明
function add(num1,num2){
return num1+num2;
}
第二种:函数表达式(这种函数被称为匿名函数)
var add=function(num1,num2){
return num1+num2;
};
两种形式的区别:函数声明会被提升至上下文。这意味着你可以先使用函数后声明它们。
原因:函数声明会被提升,这是因为引擎提前知道了函数的名字。函数表达式只能通过变量引用,因此无法提升。
2.2 函数就是值
可以像使用对象一样使用函数,也可以将他们赋给变量,在对象中添加他们,将他们当成参数传给别的函数,或从别的函数中返回。
function sayHi(){
console.log("hi");
}
sayHi(); //outputs "hi"
var sayHi2=sayHi;
sayHi2(); //outputs "hi"
这段代码首先有一个函数声明。然后有一个变量sayHi2被创建,并且被赋予sayHi的值。sayHi和sayHi2现在指向同一个函数。两者都可以执行,并且具有相同结果。
只要你记住函数就是对象,很多行为就变得容易理解了。
例如,你可以将函数当成参数传递给其他的参数。javascript的数组的sort()方法,接受一个比较函数作为可选参数。每当数组中有两个值需要进行比较时都会调用比较函数。如果第一个值小于第二个,比较值返回负数。如果第一个值大于第二个,比较值返回正数。如果相等,返回0。
在默认情况下,sort将数组中每个对象转换为字符串,然后进行比较。这意味着你无法在不指定比较函数的情况下为数字的数组精确排序。例子如下:
var numbers=[1,5,8,4,7,10,2,6];
number.sort(function(first,second){
return first-second;
});
console.log(numbers); //"[1,2,4,5,6,78,10]"
number.sort();
console.log(numbers); //"[1,10,2,4,5,6,7,8]"
在本例中,被传给sort()的比较函数其实是一个函数表达式。第二次输出结果是因为默认的sort函数将所有值都转换为字符串进行比较。
2.3 参数
可以给javascript 函数传递任意数量的参数而不造成错误。那是因为函数参数实际上被保存在一个被称为arguments的类似数组的对象中。arguments可以自由增长来包含任意个数的值,这些值可以通过数字索引来引用。arguments的length属性可以告诉你目前有多少个值。arguments对象自动存在于函数中。
下面是一个简单的使用arguments和函数的期望参数个数的例子。实际传入的参数的数量不影响函数的期望参数个数。
function reflect(value){
return value;
}
console.log(reflect("hi")); //"hi"
console.log(reflect("hi",25)); //"hi"
console.log(reflect.length); //1
reflect=functio(){
return arguments[0];
}
console.log(reflect("hi")); //"hi"
console.log(reflect("hi",25)); //"hi"
console.log(reflect.length); //0
本例中先定义了一个具有单一命名参数的reflect()函数,但是两个参数传递给它时并,没有任何错误。由于只有一个命名参数,所以length属性为1。之后重新定义reflect()为无命名参数的函数,它返回传入的第一个参数arguments[0]。但它的length为0。
假设你想创建一个函数接受任意数量的参数并返回他们的和。因为你不知道有多少个参数,所以无法使用命名函数,在这种情况下,使用arguments最好。举例如下:
function sum(){
var result =0,
i=0,
len=arguments.length;
while(i<len){
result+=arguments[i];
i++;
}
return result;
}
console.log(sum(1,2)); //3
console.log(sum(50)); //50
console.log(sum()); //0
sum()函数接收任意数量的参数,并在while循环中遍历他们的值来求和。由于result的初始值为0,该函数就算没有参数,也可以正常工作。
2.4 重载
javascript 函数可以接受任意数量的参数且参数类型完全没有限制,javascript 函数不存在重载。
function say(message){
console.log(message);
}
function say(){
console.log("default");
}
say("hello"); //"default"
在javascript里,当你试图定义多个重名的函数时,只有最后的定义有效,之前的函数声明被完全删除。
function say(message){
if (arguments.length===0){
message="default";
}
console.log(message);
}
say("hello"); //hello
本例中,say()函数的行为视传入参数的个数而定。
2.5 对象方法
在下面代码中,变量person被赋予了一个对象的字面形式,包含属性和方法sayname.
var person={
name:"hui",
sayName:function(){
console.log(person.name);
}
};
person.sayName(); //"hui"
2.5.1 this 对象
sayName()方法直接引用了person.name在方法和对象之间建立了紧耦合。上例中应该改person.name为this.name。
javascript所有的函数作用域内都有一个this对象代表调用函数的对象。在全局作用域,this代表全局对象(浏览器里的window)。当一个函数作为对象的方法被调用时,默认this的值等于那个对象。
function sayNameForAll(){
console.log(this.name);
}
var person1={
name:"hui",
sayName: sayNameForAll
};
var person2={
name:"xia",
sayName: sayNameForAll
};
var name="mm";
person1.sayName();
person2.sayName();
sayNameForAll();
运行结果如下:
本例中先定义函数,sayNameForAll.然后以字面形式创建两个对象以sayNameForAll函数作为sayName方法。当person1调用sayName时,输出“hui".当person2调用sayName时,输出“xia".最后定义了全局变量name.,所以当直接调用sayNameForAll是输出是"mm".
2.5.2 改变this
记住函数是对象,而对象有方法,所以函数也有。
(1)call()方法
它以指定的this值和参数来执行函数。call()的第一个参数指定了函数执行时的this值,其后所有的参数都是需要被传入函数的参数。
function sayNameForAll(label){
console.log(label+":"+this.name);
}
var person1={
name:"hui"
};
var person2={
name:"xia"
};
var name="mm";
sayNameForAll.call(this,"global");
sayNameForAll.call(person1,"person1");
sayNameForAll.call(person2,"person2");
运行结果如下:
由于使用了call()方法,你不需要将函数加入每个对象。因为你显式指定了this值而不是让javascript引擎自动指定。
(2)apply()方法
apply()的工作方式和call()方法完全一样,但它只接受两个参数,this值和一个数组或者类似数组的对象,内含需要被传入函数的参数(也就是说你可以把arguments对象作为apply()的第二个参数)。
function sayNameForAll(label){
console.log(label+":"+this.name);
}
var person1={
name:"hui"
};
var person2={
name:"xia"
};
var name="mm";
sayNameForAll.call(this,["global"]);
sayNameForAll.call(person1,["person1"]);
sayNameForAll.call(person2,["person2"]);
运行结果和上例一样。
(3)bind()方法
ECMAScript5中新增的这个方法。bind()方法的第一个参数为this对象的值,第二个参数可选,可以不写,也可以写传入函数的参数值。
下面例子演示了使用bind的例子。创建了sayNameForPerson1()函数,并将person1绑定为其this对象的值。然后创建了sayNameForPerson2()函数,并将person2绑定为其this对象的值,person2绑定为其第一个参数。
function sayNameForAll(label){
console.log(label+":"+this.name);
}
var person1={
name:"hui"
};
var person2={
name:"xia"
};
var sayNameForperson1=sayNameForAll.bind(person1);
sayNameForperson1("person1");
var sayNameForperson2=sayNameForAll.bind(person2,"person2");
sayNameForperson2();
person2.sayName=sayNameForperson1;
person2.sayName("person2");
sayNameForPerson1()函数没有绑定参数,所以你仍需传入lable参数用于输出。sayNameForPerson2()函数不仅绑定this为person2,同时也绑定了第一个参数为"person2".这意味着你可以调用sayNameForPerson2()函数而不传入任何额外参数。
运行结果如下:
例子最后将sayNameForPerson1()设置为person2的sayName方法。由于其this值已经绑定,所以虽然sayNameForPerson1()现在是person2 的方法。它仍然输出person1.name的值。
完 等待第三章更新哦