函数属性、方法和构造函数
length 属性
在函数体里,arguments.length
表示传入函数的实参的个数。而函数本身的 length
属性表示函数形参的个数,该属性是只读属性。
function fun(name, age) {
console.log(`实参个数:${arguments.length}`); // 1 => 表示实参的个数
console.log(`形参个数:${arguments.callee.length}`); // 2 => 表示形参的个数
}
fun('cez');
call() 方法和 apply() 方法
JS 中的函数也是对象,函数对象也可以包含方法。其中的两个方法 call()
和 apply()
可以用来间接地调用函数。call()
和 apply()
的第一个实参是要调用函数的母对象,它是调用上下文,在函数体内通过 this
来获得对它的引用。
var obj = {
name: 'cez'
}
function fun() {
console.log(this);
}
fun();
fun.call(obj);
fun.apply(obj);
上面代码中,正常情况下,函数体内的
this
指向的是全局对象(Window),在使用call
和apply
方法时可以显示的指定this
的值为obj
对象,并且在函数fun()
体内可以通过this
来获得对obj
对象的引用。
call() 和 apply() 方法区别
两个方法类型,区别在于传入实参的形式有所不同。call()
方法传入的实参以逗号分隔开,而 apply()
方法传入的实参都放在一个数组中。
var obj = {};
function fun(name, age) {
console.log(`名字:${name}, 年龄:${age}`);
}
fun.call(obj, 'cez', 22);
fun.apply(obj, ['zlz', 21]);
传入 apply() 的参数也可以是类数组对象。实际上,可以把当前函数的 arguments 数组直接传入 apply() 来调用另一个函数
function fun(x, y) {
return x + y;
}
function outFun() {
return fun.apply(null, arguments);
}
outFun(1, 2); // 3
上面代码中,把函数
outFun()
的arguments
数组传入apply()
,所以x = 1;y = 2
。
bind() 方法
这个方法的主要作用就是将函数绑定至某个对象。当在函数 f()
上调用 bind()
方法并传入一个对象 o
作为参数,这个方法将返回一个新的函数。调用新的函数将会把原始的函数 f()
当作 o
的方法来调用。传入新函数的任何实参都将传入原始函数。
function f(y) { // 待绑定的函数
return this.x + y;
}
var o = { x : 1 }; // 将要绑定的对象
var g = f.bind(o); // 通过调用 g(y) 来调用 o.f(y)
g(2); // 3
上面代码中,
bing()
方法会把它的第一个实参绑定给函数f()
的this
,所以函数f()
里的this
指向{ x : 1 }
对象。因为传入新函数g()
的参数都会将传入原始函数,所以y = 2
。
除了第一个实参外,传入bind() 的实参也都将传入原始函数
function f(y, z) {
return this.x + y + z;
}
var o = { x : 1 };
var g = f.bind(o, 2);
g(3); // 6
上面代码中,第一个实参绑定给函数
f()
的this
,从第二个实参起,会依次传入原始函数中,所以y = 2
。最后调用g(3)
的时候,这里的 3 便是参数z
的值。
js 原生实现 bind() 方法
// 返回一个函数,通过调用它来调用对象 o 中的方法 f()
function bind(f, o) {
return function() {
return f.apply(o, arguments);
}
}
function fun(y, z) {
return this.x + y + z;
}
var o = { x: 1 };
var g = bind(fun, o);
g(56, 10); // 67
toString() 方法
和所有 JS 对象一样,函数也有 toString() 方法,实际上,大多数的 toString() 方法的实现都是返回函数的完整源码。
function fun() {
return 'cez';
}
fun.toString();
Function() 构造函数
除了函数定义语句和函数直接量表达式之外,函数还可以通过 Function()
构造函数来定义
var fun = new Function("x", "y", "return x * y;");
// 相当于
var fun = function(x, y) {
return x * y
}
fun(2, 4) // 8
console.log(fun);
Function()
构造函数可以传入任意数量的字符串实参,最后一个实参所表示的文本就是函数体;它可以包含任意的 JS 语句,每条语句之间用分号隔开。传入构造函数的其他所有的实参字符串是指定函数的形参名字的字符串。如x,y
就是充当了函数fun()
的形参。
如果定义的函数不包含任何参数,只须给构造函数简单地传入一个字符串——函数体即可。
var str = "return '神经蛙';"
var fun = new Function(str);
// 或者
var fun = new Function("return '神经蛙';");
// 相当于
var fun = function() {
return '神经蛙';
}
上面代码中,
Function()
构造函数创建一个匿名函数function() { return '神经蛙'; }
,然后把匿名函数赋值给变量fun
。
由 Function() 创建的函数并不使用词法作用域,相反,函数体代码的编译总是会在顶层函数执行(也就是全局作用域)
var scope = "global";
function fun() {
var scope = "local";
return new Function("return scope"); // 无法捕获局部作用域
}
fun()(); // global
上面代码中,
Function()
构造函数的函数体执行的作用域总是全局作用域,所以返回的是global
。
可调用的对象
”可调用的对象”是一个对象,可以在函数调用表达式中调用这个对象。所有的函数都是可调用对象,但并非所有可调用对象都是函数。
在 IE8 及之前版本中,客户端方法如 window.alert()
和 Document.getElementById()
使用了可调用的宿主对象,而不是内置函数对象,IE 中的这些方法在其它浏览器中也都存在,但它们本质上不是 Function()
对象。
另外一个常见的可调用对象是 RegExp 对象,这是一个非标准特性。在不同浏览器中,对 RegExp 执行 typeof 运算结果并不统一,有些返回 [function]
,有些返回 [object]
。
判断一个对象是否是函数对象
function isFunction(o) {
return Object.prototype.toString.call(o) == '[object Function]';
}
function fun() { return 'cez'; }
isFunction(fun); // true
isFunction([]); // false