函数属性、方法和构造函数



length 属性

在函数体里,arguments.length 表示传入函数的实参的个数。而函数本身的 length 属性表示函数形参的个数,该属性是只读属性。

function fun(name, age) {
  console.log(`实参个数:${arguments.length}`);  // 1 => 表示实参的个数
  console.log(`形参个数:${arguments.callee.length}`);  // 2 => 表示形参的个数
}
fun('cez');

function this参数 function中的参数_作用域


call() 方法和 apply() 方法

JS 中的函数也是对象,函数对象也可以包含方法。其中的两个方法 call()apply() 可以用来间接地调用函数。call()apply() 的第一个实参是要调用函数的母对象,它是调用上下文,在函数体内通过 this 来获得对它的引用。

var obj = {
  name: 'cez'
}

function fun() {
  console.log(this);
}

fun();
fun.call(obj);
fun.apply(obj);

function this参数 function中的参数_函数体_02

上面代码中,正常情况下,函数体内的 this 指向的是全局对象(Window),在使用 callapply 方法时可以显示的指定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]);

function this参数 function中的参数_构造函数_03



传入 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 this参数 function中的参数_作用域_04



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 this参数 function中的参数_作用域_05

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



可调用的对象

”可调用的对象”是一个对象,可以在函数调用表达式中调用这个对象。所有的函数都是可调用对象,但并非所有可调用对象都是函数。

function this参数 function中的参数_构造函数_06

在 IE8 及之前版本中,客户端方法如 window.alert()Document.getElementById() 使用了可调用的宿主对象,而不是内置函数对象,IE 中的这些方法在其它浏览器中也都存在,但它们本质上不是 Function() 对象。

另外一个常见的可调用对象是 RegExp 对象,这是一个非标准特性。在不同浏览器中,对 RegExp 执行 typeof 运算结果并不统一,有些返回 [function],有些返回 [object]

function this参数 function中的参数_javascript_07

function this参数 function中的参数_作用域_08

function this参数 function中的参数_function this参数_09



判断一个对象是否是函数对象

function isFunction(o) {
    return Object.prototype.toString.call(o) == '[object Function]';
}

function fun() { return 'cez'; }
isFunction(fun);  // true
isFunction([]);  // false