一、JS函数的本质
JS函数本质就是一种对象!!!
编写者完全可以用操作对象的方式去操作函数
var obj = {
};
function fn() {
}
/**
* 分别向对象和函数添加属性值,以验证函数其实就是一类对象
*/
obj.info = "对象";
fn.info = "函数";
console.log("obj是" + obj.info); // 正常打印"obj是对象"
console.log("fn是" + fn.info); // 正常打印"fn是函数"
JS函数名与变量有着相似的特性,函数名用于找到函数(对象)在内存中的位置。
当仅输出函数名(变量名)时,将打印函数体(对象)的有关信息;
当输出函数名+参数列表时,将打印函数执行后返回的结果。
/**
* 直接声明一个函数名为fn1的函数(对象)
*/
function fn1() {
return "我是命名函数";
}
/**
* 将一个匿名函数(对象)赋值给变量fn1
*/
var fn1 = function() {
return "我是匿名函数";
};
/**
* 用构造法实例一个函数对象,将其赋值给变量fn3
*/
var fn3 = new Function("return '我是new出来的函数'");
console.log(fn1); // 输出函数fn1的信息
console.log(fn1()); // 输出函数fn1执行后返回的结果——"我是命名函数"
console.log(fn2); // 输出变量fn2所指向的匿名函数的信息
console.log(fn2()); // 输出fn2执行后返回的结果——"我是匿名函数"
console.log(fn3); // 输出变量fn3所指向的匿名函数的信息
console.log(fn3()); // 输出fn3执行后返回的结果——'我是new出来的函数'
由于JS弱类型的检测机制,编写者将其它类型的值赋给函数变量时也不会出现问题,它同普通的变量赋值一样。
function fn1() {
return "我是命名函数";
}
var fn1 = function() {
return "我是匿名函数";
};
var fn3 = new Function("return '我是new出来的函数'");
/**
* 将任意的整型数值赋值给fn1、fn2和fn3
*/
fn1 = 99;
fn2 = 100;
fn3 = 101;
console.log(fn1); // 正常打印99
console.log(fn2); // 正常打印100
console.log(fn3); // 正常打印101
值得注意的是,以var方式引导函数的变量在预解析时同样会被初始化为undefined,如果在声明语句前调用该函数将无效,这一点与以function命名形式声明的函数不同。
consolo.log(fn); // 显示undefined
console.log(fn()); // 报错,提示fn不是一个函数
var fn = function() {
return "我是匿名函数";
}
二、函数调用的特殊情况
1.匿名函数的直接调用
当function作为关键字出现在句首时,会被JS视为命名函数进行解析,所以匿名函数的直接调用需要添加合法的字符以进行区分
/**
* 通过添加运算符进行匿名函数的调用
*/
!function(){
}();
/**
* 通过添加括号进行匿名函数的调用
*/
(function(){
})();
2.方法的调用
对象方法的调用一般使用:对象名.方法名(参数列表)
也可以使用:对象名[“方法名”] (参数列表)(可以传字符串变量)
当遇到特殊字符开头的方法名时,通常会使用到后者。
var obj = {
info1: function(){
return "我是info1方法";
},
'info2': function(){
return "我是info2方法";
}
};
var name = "info2";
console.log(obj.info1());
console.log(obj.info2());
console.log(obj['info2']());
console.log(obj[name]());
3.函数的间接调用
每个函数对象都有call()和apply()两个方法,这两个方法可以改变函数this的指向,间接传参并调用函数。
var num = 1;
var obj = {
num: 2,
add: function(num) {
console.log(this.num + num);
}
};
var array = [3];
obj.add(3); // 将输出2 + 3 == 5
obj.add.call(window, 3); // 将输出1 + 3 == 4
obj.add.apply(window, array); // 将输出1 + 3 == 4
三、可变参数列表
当函数参数的数量不确定的时候,可以用arguments对象进行可变参数操作。
arguments是一个形态类似数组的对象,每个函数都包含这个对象,arguments按顺序存储了所有的参数,对应的属性名从0开始递增。
function fn(a, b, c) {
console.log(arguments);
}
fn(10, 20, 30);
打印结果:
Arguments {
0: 10
1: 20
2: 30
}
arguments.callee()方法即指代函数本身,可以直接通过其调用函数
function fn() {
return arguments.callee;
}
console.log(fn()); // 将打印出函数fn的信息
可以使用arguments.length属性对传参操作进行检查
function fn(a, b) {
// 若函数接收到的参数个数与函数定义的形参个数不相等,则抛出异常
if (arguments.length != fn.length) {
throw new Error("The number of parameters is illegal!");
}
return a + b;
}
console.log(fn(1, 5)); // 正常计算出6
console.log(fn(1)); // 抛出Error
console.log(fn(1, 5, 8)); // 抛出Error