函数的定义
函数名称只能包含字母、数字、下划线或$,且不能以数字开头。定义时可用函数定义表达式或者函数声明语句。
var f = function fact(x){}
函数定义表达式包含名称,名称将作为函数的局部变量,在函数内部使用,代指函数。
函数声明语句不是真正的语句,不能出现在循环、条件、try/catch/finally以及with语句中;声明语句置于在不会执行到的位置,仍可被整个作用域可访问,可在被定义代码之前使用。定义表达式的变量声明被提前了,但是对变量赋值不会提前,函数在被定义之前无法使用,否则调用时会出现错误:"TypeError: undefined is not a function"
return语句没有一个与之相关的表达式,则它返回undefined值;如果一个函数不包含return语句,那它只执行函数体内语句,并返回undefined给调用者;没有返回值的函数,有时称为过程。
函数执行
函数执行的几种方式,当作函数、方法、构造函数、间接调用。如果构造函数没有形参,可以省略实参列表和圆括号,调用函数传实参有间隔时,可用null或undefined占位符替代空白的参数。
function MyClass(name) { this.name = name; return name; // 构造函数的返回值? } var obj1 = new MyClass(‘foo‘);// MyClass对象 var obj2 = MyClass(‘foo‘);// ‘foo’ var obj3 = new MyClass({});// {} var obj4 = MyClass({});// {}
实参对象
实参对象是类数组对象,可用arguments.callee递归调用,如果你把参数命名为arguments,那么这个参数就会覆盖它原有的特殊变量。
函数的形参和实参
定义的函数括号内靠后面的形参没传入相应的实参,则默认值为undefined,有人利用这个,隐式定义函数内部的局部变量。函数传入参数的校验及抛出错误,函数中实参传入的是引用,函数内部对其操作,对外部是可见的。
// 函数传参引用 var oo = { x: 1, y: 2, get z() { return 3; } } function fns(obj) { obj.h = 4; } fns(oo);
函数属性、方法
函数也是对象,所以也有属性和方法,函数的length属性,函数形参的个数。
apply方法的第二个参数可以是数组或类数组对象。
bind方法是ES5中新增,将函数“绑定至”对象并传入一部分参数,传入的实参放在完整实参列表的左侧。
/** * ES5.0支持bind函数,ES3.0不支持bind,这里在ES3中模拟实现,能应用于大部分场景, * 如有问题,欢迎一起探讨 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind */ Function.prototype.bind || (Function.prototype.bind = function (that) { var target = this; // If IsCallable(func) is false, throw a TypeError exception. // 通过call、apply调用时,校验传入的上下文 if (typeof target !== ‘function‘) { throw new TypeError(‘Bind must be called on a function‘); } var boundArgs = slice.call(arguments, 1); function bound() { // 返回的bind函数被当构造函数 if (this instanceof bound) { var self = createObject(target.prototype); var result = target.apply( self, boundArgs.concat(slice.call(arguments))); // Object(result) === result 判断调用返回是不是对象 return Object(result) === result ? result : self; } // 返回的bind函数以一般函数形式调用 else { return target.apply( that, boundArgs.concat(slice.call(arguments))); } } // NOTICE: The function.length is not writable. bound.length = Math.max(target.length - boundArgs.length, 0); return bound; });
高阶函数
如果函数作为参数或返回值使用时,就称为高阶函数。
作为命名空间的函数
函数命名空间暴露接口有以下几种方法
var mylib = (function (global) { function log(msg) { console.log(msg); } log1 = log; // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐) global.log2 = log; // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐) return { // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量mylib(推荐) log: log }; }(window));
自更新函数
function selfUpdate() { window.selfUpdate = function () { alert(‘second run!‘); }; alert(‘first run!‘); } selfUpdate(); // first run! selfUpdate(); // second run!
这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑。
函数书写规范
函数参数定义的几种注释
/*,...*/ AP.reduce || (AP.reduce = function(fn /*, initial*/) { getPropertyNames(o,/*optional*/a) // /*optional*/表示参数可选 function arraycopy(/*array */ from,/*index*/from_start){}; }
具有记忆功能的函数
/** * 记忆函数 */ function memorize(f) { var cache = {}; return function () { var key = arguments.length + ‘‘ + Array.prototype.join.call(arguments, ‘,‘); if (!(key in cache)) cache[key] = f.apply(null, arguments); return cache[key]; } }