layout: post title: JavaScript函数 category: Javascript tags: [JavaScript] —
函数定义
函数名只能包含字母、数字、下划线或$,且不能以数字开头。
创建函数,可用函数定义表达式或者函数声明语句。
var f = function fact(x){}
函数定义表达式包含名称,名称将作为函数的局部变量,在函数内部使用,代指函数。
函数声明语句不是真正的语句,不能出现在循环、条件、try/catch/finally以及with语句中;
声明语句置于在不会执行到的位置,仍可在整个作用域可访问,可在被定义代码之前使用。
定义表达式的变量声明被提前了,但是对变量赋值不会提前,函数在被定义之前无法使用,否则调用时会出现错误:
"TypeError: undefined is not a function"
return 后没表达式,则返回undefined值;
函数不包含return语句,那它只执行函数体内语句,并返回undefined给调用者;
没有返回值的函数,有时称为过程。
函数的执行
函数执行的几种方式,当作函数、方法、构造函数、间接调用
如果构造函数没有形参,可以省略实参列表和圆括号
构造函数返回值情况
function MyClass(name) {
this.name = name;
return name; // 构造函数的返回值?
}
var obj1 = new MyClass('foo');
var obj2 = MyClass('foo');
var obj3 = new MyClass({});
var obj4 = MyClass({});
obj1 = MyClass对象
obj2 = 'foo'
obj3 = {}
obj4 = {}
点击链接,查看结果
函数的形参和实参
执行函数时,没传入值的形参,默认值是undefined的,有人利用这个,隐式定义函数内部的局部变量
调用函数间隔传实参时,可用null或undefined占位符替代空白的参数
函数中实参传入的是引用,函数内部对其操作,对外部是可见的
实参对象arguments
实参对象是个类数组对象, arguments.callee递归调用 如果你把参数命名为arguments,那么这个参数就会覆盖它原有的特殊变量。
函数属性、方法
-
length属性,函数形参的个数
-
prototype属性,实现引用类型定义和继承
-
name 函数名字,匿名函数为空,比如通过
var fun = function (){}
声明的表达式。 - _proto_,内部属性,函数也是对象,也有继承的原型
- call和apply在特定的作用域中执行函数,这里实际设定函数执行的this。apply 的第二个参数是数组或类数组对象
-
toString toLocalString indexOf都是返回函数代码,不同的浏览器表现出不同的效果,比如删除注释,格式化代码。
-
bind方法 ES5中新增,将函数“绑定至”对象并传入一部分参数,传入的实参放在完整实参列表的左侧 这个例子考虑到bind返回的函数被当成构造函数使用情况
/** * 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,undefined) {
function log(msg) {
console.log(msg);
}
log1 = log; // 法一:定义不带var的变量,将引用暴露到外部
global.log2 = log; // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐)
return log // 法三:返回函数值
}(window));
自更新函数
function selfUpdate() {
window.selfUpdate = function() {
alert('second run!');
};
alert('first run!');
}
selfUpdate(); // first run!
selfUpdate(); // second run! 这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑
函数判断
严谨上讲,typeof 识别出函数可能有误,可执行对象,如alert、正则表达式不是函数
函数书写规范
-
函数参数定义的几种注释
/*,...*/ function(fn /*, initial*/) getPropertyNames(o,/*optional*/a) arraycopy(/*array */ from,/*index*/from_start)
-
优雅的风格
// bad function a() { test(); console.log('doing stuff..'); //..other stuff.. var name = getName(); if (name === 'test') { return false; } return name; } // good function a() { var name = getName();// 定义变量放在前面 test(); console.log('doing stuff..'); //..other stuff.. if (name === 'test') { return false; } return name; } // bad function a() { var name = getName(); if (!arguments.length) { return false; } return true; } // good function a() { if (!arguments.length) {// 参数校验放在前面 return false; } var name = getName(); return true; }
函数式编程
-
高阶函数 如果函数作为参数或返回值使用时,就称为高阶函数
-
不完全函数
-
具有记忆功能的函数
/** * 记忆函数 */ 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]; } }