每个函数都是Function
类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
一、定义函数
- 使用函数声明语法
function sum (num1, num2) {
return num1 + num2;
}
- 使用函数表达式
// 在使用函数表达式定义函数时,变量sum 即可以引用函数,无需函数名
var sum = function(num1, num2){
return num1 + num2;
}; // 注意函数末尾有一个分号,就像声明其他变量时一样
- 使用
Function
构造函数
var sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐
- 定义函数注意事项
(1)函数声明与函数表达式的区别
- 解释器会率先读取函数声明,并使其在执行之前可以访问;
- 而使用表达式则必须等到解析器执行到它所在的代码行,才会真正被解释执行(变量声明提前,而值留在原地);
- 除以上不同之外,函数声明与函数表达式两者是等价的。
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
↑以上代码会正常执行,因为在代码执行前,解析器通过函数声明提升,读取并将函数声明添加到执行环境中,放到代码树的顶部。
alert(sum(10,10)); // 报错
var sum = function(num1, num2){
return num1 + num2;
};
↑以上代码报错,原因在于函数位于一个初始化语句中,不是一个函数声明,不会被提前,而只会把var sum
提前,也就是在执行到函数所在的语句之前,变量sum
中不会保存有对函数的引用,所以会报错
(2)函数是对象,函数名是指针。由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同,也就是说,一个函数对象可以有多个名字:
function sum(num1, num2){
return num1 + num2;
}
alert(sum(10,10)); //20
var anotherSum = sum; // //变量anotherSum也指向了同一个函数对象
alert(anotherSum(10,10)); //20
sum = null; // //sum变量不再保存函数对象的指针了
alert(anotherSum(10,10)); //20,anotherSum这个变量仍能调用
二、没有重载的概念
function addSomeNumber(num){
return num + 100;
}
function addSomeNumber(num) {
return num + 200;
}
var result = addSomeNumber(100); //300
↑以上代码中,声明了两个同名函数,而结果则是后面的函数覆盖了前面的函数。以上代码实际上与下面的代码没有什么区别:
var addSomeNumber = function (num){
return num + 100;
};
addSomeNumber = function (num) {
return num + 200;
};
var result = addSomeNumber(100); //300
↑通过以上代码可知,在创建第二个函数时,实际上覆盖了引用第一个函数的变量addSomeNumber
,所以不能重载。
三、作为值的函数
- 因为函数名本身就是变量,所以函数也可以作为值来使用。
- 不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
(1)函数作为参数传递给另一个函数
要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。
function callSomeFunction(someFunction, someArgument){
return someFunction(someArgument);
}
function add10(num){
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
alert(result1); //20
function getGreeting(name){
return "Hello, " + name;
}
var result2 = callSomeFunction(getGreeting, "Nicholas");
alert(result2); //"Hello, Nicholas"
// 以上传递给callSomeFunction()的是 add10 和 getGreeting,而不是执行它们之后的结果。
(2)从一个函数中返回另一个函数
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: "Zachary", age: 28}, {name: "Nicholas", age: 29}];
data.sort(createComparisonFunction("name"));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction("age"));
alert(data[0].name); //Zachary
四、函数内部的属性
arguments
属性
arguments
是个类数组对象,包含着传入函数的所有参数,这个对象有一个叫callee
的属性,属性值是一个指针,指向拥有这个arguments
对象的函数本身。
function foo (){
var a =arguments.callee; // arguments.callee指foo函数本身
return a.toString();
}
foo();
/*
返回结果:
"function foo (){
var a =arguments.callee;
return a.toString();
}"
也就是说,一个函数内部,arguments.callee指的就是这个函数本身。
这个函数在递归调用时有点用,有许多缺陷,在ES5严格模式被移除
*/
this
属性
this
指的就是函数执行的环境对象,在哪个对象中执行,this就指哪个对象。当在网页的全局作用域中调用函数时, this
对象引用的就是 window
。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
caller
属性
caller
属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null
。
function outer(){
inner();
}
function inner(){
console.log(inner.caller);
}
outer();
/*
输出结果:
ƒunction outer(){
inner();
}
*/
五、函数的属性和方法
- 函数的属性
(1)length
属性:表示函数希望接收的命名参数的个数。
function sayName(name){
alert(name);
}
function sum(num1, num2){
return num1 + num2;
}
function sayHi(){
alert("hi");
}
console.log(sayName.length); //1
console.log(sum.length); //2
console.log(sayHi.length); //0
(2)prototype
属性:简单来说,是一个对象,是通过调用构造函数而创建的一个对象,包含可以由特定类型的所有实例共享的属性和方法。
- 函数的方法
(1)apply
与call
方法
- 两个方法的作用都是在特定的作用域中调用函数,实际上就是设置函数内部的
this
值;apply()
:接收两个参数,一个是函数运行的作用域,另一个是参数数组,可以是数组,也可以是arguments
这个类数组对象;- 在使用
call()
方法时,传递给函数的参数必须逐个列举出来;call()
方法与apply()
方法的作用相同,它们的区别仅在于接收参数的方式不同。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 传入 arguments 对象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
// 也可以使用 call()方法
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
至于是使用 apply()
还是 call()
,完全取决于你采取哪种给函数传递参数的方式最方便。
传递参数并非 apply()
和 call()
真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue,此时函数体内的 this 对象指向了 o
(2)bind()
方法:这个方法会创建一个函数的实例,其 this
值会被绑 定到传给 bind()
函数的值。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
↑以上代码中,sayColor()
调用 bind()
并传入对象o
,创建了objectSayColor()
函数。objectSayColor()
函数的 this
值等于 o
,因此即使是在全局作用域中调用这个函数,也会看到"blue"
。
网友评论