- length
- 函数的length属性是只读属性, 代表函数形参数量, 也就是函数定义时指定的期望参数个数
- prototype: 每个函数都包含一个prototype属性, 这个属性指向一个对象的引用, 叫做原型对象, 每个函数都包含不同的原型对象, 当函数用作构造函数是, 新创建的对象会冲原型对象上继承属性
- call()和apply()
- 可以将call()和apply()看做某个对象的方法, 通过调用方法的形势调用函数, 他们的第一个实参是要调用函数的对象, 这个对象成为函数调用上下文, 函数体执行时内部this指向它的引用比如: f.call(0); f.apply(); 等价于:
o.m = f; o.m(); delete o.m;
- 在ecmascript 5的严格模式中, call()和apply()的第一个实参都会变成this的值, 即使传入的是null或者undefined, 在ecmascript 3和非严格模式中, 传入的null和undefined会被全局对象替代, 其他元素值将被会对应包裹对象所替代
- call()第一个实参作为函数调用上下文, 后续的实参都传入调用函数作为实参比如: 以对象o的方法调用函数f()并且传入两个参数,可以这样调用
f.call(o, 1, 2);
- apply()与call()相似, 不过传入的实参都放入一个数组中如下面两种方法等价
f.apply(o, [1, 2]); f.call(o, 1, 2);
- 如果一个函数的实参可以是任意数量, 那么给apply()传入的参数数组长度可以是任意的, 这在很多情况下很有用, 比如找出数组中最大的数值元素可以如下:
var biggest = Math.math.apply(Math, array_of_numbers);
- apply()接受实参也可以是类数组对象, 比如可以将当前函数的arguments以apply()传入另一个函数来调用, 比如下面的代码为方法m包裹了log功能, 但是调用方法不变
function trace(o, m) { var original = o[m]; o[m] = function () { console.log(new Date(), "Entering: ", m); var result = original.apply(this, arguments); console.log(new Date(), "Exiting: ", m); return result; } // end wrapper method } // end trace()
- 可以将call()和apply()看做某个对象的方法, 通过调用方法的形势调用函数, 他们的第一个实参是要调用函数的对象, 这个对象成为函数调用上下文, 函数体执行时内部this指向它的引用比如: f.call(0); f.apply(); 等价于:
- bind()
- bind()是ecmascript5中新增的方法, 但是可以再ecmascript3中模拟bind()
- 在f()上调用bind()兵传入对象o作为参数返回一个新的函数, 以函数调用的方式调用新的函数将会吧元素的f()当做o的方法来调用, 传入新函数的任何实参都将传入原始函数, 比如:
function f(y) { return this.x + y; } // end f() var o = {x: 1}; var g = f.bind(o); g(2); // 3
- ecmascript5中的bind()不仅仅是将函数绑定至一个对象, 还会附带参数绑定如:
var sum = function (x, y) { return x + y; } // end sum() // succ将只期望一个实参y, x已绑定为1 var succ = sum.bind(null, 1); succ(2); // 3 function f(y, z) { return this.x + y + z; }; var g = f.bind({x: 1}, 2); g(3); // 6
- 在ecmascript3中模拟bind():
if (!Function.prototype.bind) { Function.prototype.bind = function (o) { var self = this, boundArgs = arguments; return function () { var arg = [], i, len; for (i = 1, len = boundArgs.length; i < len; ++i) { arg.push(boundArgs[i]); } // end for for (i = 0, len = arguments.length; i < len; ++i) { arg.push(arguments[i]); } // end for self.apply(o, arg); } // end function } // end bind() } // end if
- 注意: 上面的办法不能完全模拟ecmascript5中的bind(), 真正的bind()返回一个函数对象, 这个函数对象的length属性是绑定函数的形参减去绑定实参的个数, ecmascript5中bind()方法可以用作构造函数, 如果bind()返回的函数用作构造函数, 将忽略传入bind的this, 元素函数就会以构造函数调用
- toString(): 大多数toString()返回函数完整代码, 内置函数通常返回类似"[native code]"的字符串
- Function()构造函数: 接受任意数量的字符串实参, 最后一个实参中的文本为函数体, 其余的为形参, 创建一个匿名函数
- Function()构造函数允许javascript在运行时动态创建并编译函数
- 每次调用Function()构造函数都会解析函数体兵创建新的函数对象, 如果在循环中或者多次调用函数中执行构造函数, 会影响效率, 相比之下循环中的嵌套函数和函数定义表达式不会每次执行时都编译.
- Function()构造函数创建的函数不是使用词法作用于, 函数体代码的编译总会是在顶层函数执行如下
1 var scope = "global"; 2 function constructFunction() 3 { 4 var scope = "local"; 5 return new Function("return scope"); 6 } // end construcFunction() 7 constructFunction()(); // global
- 可以将Function()构造函数认为是在全局作用于中执行的eval(), Function()构造函数在实际编程中很少用到
- 可调用对象: 可以再函数调用表达式中调用的对象, 所有函数都是可调用的, 但并非所有可调用对象都是函数
- IE浏览器(IE8以前的版本)实现了客户端方法(如window.alert()和document.getElementById()), 使用了可调用的宿主对象而不是内置函数对象, IE中的这些方法在其他浏览器中也存在, 但是他们本质上不是Function对象, IE9将他们实现为真正的函数, 因此这类可调用的对象将越来越罕见
- 另一个常见的可调用对象是RegExp对象, 直接调用它比exec()更快捷一些, 在javascript中这是彻底的非标准特性, 代码最好不好对可调用的RegExp对象有太多依赖, 这个特性在不久的将来将可能废除并删除
- 要检测一个对象是不是真正的函数对象, 可以检测class属性
1 function isFunction(x) 2 { 3 return Object.prototype.toString.call(x) === "[object Function]"; 4 } // end isFunction()
- 记忆: 通过闭包等方法保存可能重复计算的结果, 函数调用是先查询是否曾经计算过. 本质上是牺牲算法的空间复杂度换取时间复杂度, 在客户端javascript中代码执行时间复杂度往往成为瓶颈, 因此在大多数场景下, 这种牺牲空间换取时间的做法是可取的
1 function memorize(f) 2 { 3 var cache = {}; 4 return function() 5 { 6 var key = arguments.length + Array.prototype.join.call(arguments, ","); 7 if (key in cache) 8 { 9 return cache[key]; 10 } // end if 11 else 12 { 13 return cache[key] = f.apply(this, arguments); 14 } // end else 15 }; // end function 16 } // memorize()
例如求最大公约数, 斐波那契数列之类函数可以采用记忆法