1、JavaScript权威指南模板JavaScript权威指南 - 函数函数本身就是一段JavaScript代码,定义一次但可能被调用任意次。如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法。用于初始化一个新创建的对象的函数被称作构造函数。相对于其他面向对象语言,在JavaScript中的函数是特殊的,函数即是对象。JavaScript可以把函数赋值给变量,或者作为参数传递给其他函数,甚至可以给它们设置属性等。JavaScript的函数可以嵌套在其他函数中定义,这样定义的函数就可以访问它们外层函数中的任何变量。这也就是所谓的“闭包”,它可以给JavaScript带来强劲
2、的编程能力。1.函数定义函数使用function关键字定义,有函数语句和函数表达式两种定义方式。/一.函数语句类:/打印对象所有属性名称和值。function printprops(obj) for (var key in obj) console.log(key + : + objkey); /计算阶乘的递归函数,函数名称将成为函数内部的一个局部变量。function factorial(n) if (n = 1) return 1; return n * factorial(n);/二.函数表达式类:/计算n的平方的函数表达式。这里将一个函数赋给一个变量。var square = func
3、tion (x) return x * x; /兔子数列。函数表达式也可以包含名称,方便递归。var foo = function foo(n) if (n = 1) return 1; else foo(n - 1) + foo(n - 2);/数组元素升序排列。函数表达式也能作为参数传递给其他函数。var data = 5, 3, 7, 2, 1;data.sort(function (a, b) return a - b; );/函数表达式有时定义后立即调用。var tensquared = (function (x) return x * x; (10);函数命名函数名称要求简洁、描述
4、性强,因为这样可以极大改善代码的可读性,方便别人维护代码;函数名称通常是动词或以动词开头的词组。通常来说,函数名编写有两种约定:一种约定是函数名第一个单词首字母小写,后续单词首字母大写,就像likeThis();当函数名包含多个单词时,另一种约定是用下划线来分割单词,就像like_this()。项目中编写方法名时尽量选择一种保持代码风格一致。还有,对于一些私有函数(不作为公用API的一部分),这种函数通常以一条下划线作为前辍。2.函数调用函数声明后需要通过调用才能被执行。JavaScript中通常有4种方式来调用函数:作为普通函数;作为对象方法;作为构造函数;通过它们的call()和apply
5、()方法间接调用。下面就通过一些具体示例来演示上述4中函数的调用方式。1.对于普通函数,通过调用表达式就可直接调用,这种方式很直接也很常见。/定义一个普通函数。var strict = function () return !this; ; /检测当前运行环境是否为严格模式。/通过函数名直接调用。console.log(strict(); 注:根据ES3和非严格的ES5对普通函数调用的规定,调用上下文(this)是全局对象;在严格模式下,调用上下文则是undefined。2.通常,保存在对象属性里的JavaScript函数被称作“方法”。/定义一个对象直接量。var calc = a: nul
6、l, b: null, add: function () /将函数保存在对象属性中。 return this.a + this.b; ;/通过对象名调用方法。calc.a = 1, calc.b = 2;console.log(calc.add(); 注:对象方法中的调用上下文(this)不同于普通函数中的上下文。这里this指代当前对象。方法链:当方法返回值是一个对象,那么这个对象还可以再调用它的方法。每次调用的结果都是另外一个表达式的组成部分,这种方法调用方式最终会形成一个序列,也被称为“方法链”。所以,在自己设计API的时候,当方法并不需要返回值时,最好直接返回this。这样以后使用AP
7、I就可以进行“链式调用”风格的编程。需要注意的是,this是一个关键字,Javascript语法不允许给它赋值。再者,关键字this没有作用域的限制,嵌套的函数不会从外层调用它的函数中继承this。也就是说,如果嵌套函数作为方法调用,其this指向为调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象就是undefined。下面通过一段代码来具体说明。var o = m: function () /对象中的方法 var self = this; /将this的值保存在一个变量中 console.log(this = o); /输出true,表明this就是这个引用对象o f();
8、/调用嵌套函数f() function f() /定义一个嵌套函数(*普通函数,非对象方法) console.log(this = o); /输出false,this的值为全局对象或undefined console.log(self = o); /输出true,变量self指外部函数的this值 3.如果函数或者防方法调用之前带有关键字new,它就构成构造函数调用。构造函数调用会创建一个新的对象,构造函数通常不使用return,函数体执行完毕它会显示返回。还有,创建的对象继承自构造函数的prototype属性,构造函数中使用this关键字来引用这个新创建的对象。/与普通函数一样的定义方式。f
9、unction Person(name, age) this.name = name; this.age = age; this.say = function () console.log(My name is + this.name + , I am + this.age + years old.); /用关键字new调用构造函数,实例化对象。var obj = new Person(Lamb, 21);obj.say();/调用对象方法。4.我们知道Javascript中的函数也是对象,所以函数对象也是可以包含方法的,其中call()和apply()两个方法可以用来间接地调用函数,这两个方
10、法都可以显式指定调用函数里面的调用上下文this。/定义一个打印函数。function print() if (this.text) alert(this.text); else alert(undefined); /call方法间接调用方法,并指定其调用上下文。print.call( text: hello );关于call()和apply()两个方法的用法以及区别下面详细讨论。3.函数的实参和形参JavaScript中的函数定义不需要指定函数形参的类型,调用函数时也不检查传入形参的个数。这样,同时也会留下两个疑问给我们:当调用函数时的实参个数和声明的形参个数不匹配的时候如何处理;如何显式测
11、试函数实参的类型,以避免非法的实参传入函数。下面就简单介绍JavaScript是如何对上述两个问题做出处理的。可选参数当调用函数的时候传入的实参比函数定义时指定的形参个数要少,剩下的形参都将设置为undefined。一般来说,为了保持函数较好的适应性,都会给省略的参数设置一个合理的默认值。function getPropertyNames(obj,/*optional*/arr) arr=arr|; for (var property in obj) arr.push(property); return arr;需要注意的是,当使用这种可选实参来实现函数时,需要将可选实参放在实参列表的最后。一
12、般来书,函数定义中使用注释/*optional*/来强调形参是可选的。实参对象当调用函数时传入的参数个数超过了原本函数定义的形参个数,那么方法中可以通过实参对象来获取,标识符arguments是指向实参对象的引用。实参对象是一个类数组对象,可以通过数字下标来访问传入函数的实参值。实参对象有一个重要的用处,就是让函数可以操作任意数量的实参,请看下面的例子:/返回传入实参的最大值。function max(/* . */) var max = Number.NEGATIVE_INFINITY; /该值代表负无穷大。 for (var i = 0; i max) max = argumentsi;
13、return max;/调用。var largest = max(10, 45, 66, 35, 21); /=66还有重要的一点,如果函数中修改arguments元素,同样会影响对应的实参变量。除以上之外,实参对象还包含了两个属性callee和caller:callee是ECMAScript标准规范的,它指代当前正在执行的函数。caller是非标准属性但是大多数浏览器都支持,它指代当前正在执行函数的函数。/callee可以用来递归匿名函数。var sum = function (x) if (x 25s(5); /=252.函数可以保存在对象的属性或数组元素中;var array = fun
14、ction (x) return x * x; , 20;array0(array1); /=4003.函数可以作为参数传入另外一个函数;/这里定义一些简单函数。function add(x, y) return x + y; function subtract(x, y) return x - y; function multipty(x, y) return x * y; function divide(x, y) return x / y; /这里函数以上面某个函数做参数。function operate(operator, num1, num2) return operator(num
15、1, num2);/调用函数计算(4*5)-(2+3)的值。var result = operate(subtract, operate(multipty, 4, 5), operate(add, 2, 3);console.log(result); /=154.函数可以设置属性。/初始化函数对象的计数器属性。uniqueInteger.counter = 0;/先返回计数器的值,然后计数器自增1。function uniqueInteger() return uniqueInteger.counter+=1; 当函数需要一个“静态”变量来在调用时保持某个值不变,最方便的方式就是给函数定义属性
16、,而不是定义全局变量,因为定义全局变量会让命名空间变的杂乱无章。5.作为命名空间的函数函数中声明的变量只在函数内部是有定义,不在任何函数内声明的变量是全局变量,它在JavaScript代码中的任何地方都是有定义的。JavaScript中没有办法声明只在一个代码块内可见的变量的。基于这个原因,常常需要定义一个函数用作临时的命名空间,在这个命名空间内定义的变量都不会污染到全局变量。/该函数就可看作一个命名空间。function mymodule() /该函数下的变量都变成了“mymodule”空间下的局部变量,不会污染全局变量。/最后需要调用命名空间函数。mymodule();上段代码还是会暴露出
17、一个全局变量:mymodule函数。更为常见的写法是,直接定义一个匿名函数,并在单个表达式中调用它:/将上面mymodule()函数重写成匿名函数,结束定义并立即调用它。(function () /模块代码。();6.闭包闭包是JavaScript中的一个难点。在理解闭包之前先要明白变量作用域和函数作用域链两个概念。变量作用域:无非就是两种,全局变量和局部变量。全局变量拥有全局作用域,在任何地方都是有定义的。局部变量一般是指在函数内部定义的变量,它们只在函数内部有定义。函数作用域链:我们知道JavaScript函数是可以嵌套的,子函数对象会一级一级地向上寻找所有父函数对象的变量。所以,父函数对
18、象的所有变量,对子函数对象都是可见的,反之则不成立。需要知道的一点是,函数作用域链是在定义函数的时候创建的。关于“闭包”的概念书本上定义很具体,但是也很抽象,很难理解。简单的理解,“闭包”就是定义在一个函数内部的函数(这么说并不准确,应该说闭包是函数的作用域)。var scope = global scope; /全局变量function checkscope() var scope = local scope; /局部变量 function f() return scope; /在作用域中返回这个值 return f();checkscope(); /=local scope上面一段代码就就
19、实现了一个简单的闭包,函数f()就是闭包。根据输出结果,可以看出闭包可以保存外层函数局部变量,通过闭包可以把函数内的变量暴露在全局作用域下。闭包有什么作用呢?下面一段代码是上文利用函数属性定义的一个计数器函数,其实它存在一个问题:恶意代码可以修改counter属性值,从而让Integer函数计数出错。/初始化函数对象的计数器属性。uniqueInteger.counter = 0;/先返回计数器的值,然后计数器自增1。function uniqueInteger() return uniqueInteger.counter+=1; 闭包可捕捉到单个函数调用的局部变量,并将这些局部变量用作私有状
20、态,故我们可以利用闭包的特性来重写uniqueInteger函数。/利用闭包重写。var uniqueInteger = (function () /定义函数并立即调用 var counter = 0; /函数的私有状态 return function () return counter += 1; ;)();/调用。uniqueInteger(); /=1uniqueInteger(); /=2uniqueInteger(); /=3当外部函数返回后,其他任何代码都无法访问counter变量,只有内部的函数才能访问。根据输出结果可以看出,闭包会使得函数中的变量都被保存在内存中,内存消耗大,所
21、以要合理使用闭包。像counter一样的私有变量在多个嵌套函数中都可以访问到它,因为这多个嵌套函数都共享同一个作用域链,看下面一段代码:function counter() var n = 0; return count: function () return n += 1; , reset: function () n = 0; ;var c = counter(), d = counter(); /创建两个计时器c.count(); /=0d.count(); /=0 能看出它们互不干扰c.reset(); /reset和count方法共享状态c.count(); /=0 因为重置了计数器
22、cd.count(); /=1 而没有重置计数器d书写闭包的时候还需注意一件事,this是JavaScript的关键字,而不是变量。因为闭包内的函数只能访问闭包内的变量,所以this必须要赋给that才能引用。绑定arguments的问题与之类似。var name = The Window;var object = name: My Object, getName: function () var that = this; return function () return that.name; ; ;console.log(object.getName()(); /=My Object到这里
23、如果你还不明白我在说什么,这里推荐两篇前辈们写的关于“闭包”的文章。阮一峰,学习Javascript闭包(Closure)russj,JavaScript 闭包的理解7.函数属性、方法和构造函数前文已经介绍过,在JavaScript中函数也是对象,它也可以像普通对象一样拥有属性和方法。length属性在函数体里,arguments.length表示传入函数的实参的个数。而函数本身的length属性表示的则是“形参”,也就是在函数调用时期望传入函数的实参个数。function check(args) var actual = args.length; /参数的真实个数 var expected
24、= args.callee.length; /期望的实参个数 if (actual!=expected) /如果不同则抛出异常 throw Error(Expected + expected+args;got + actual); function f(x,y,z) check(arguments); /检查实参和形参个数是否一致。 return x + y + z; prototype属性每个函数都包含prototype属性,这个属性指向一个对象的引用,这个对象也就是原型对象。当将函数用作构造函数的时候,新创建的对象会从原型对象上继承属性。call()方法和apply()方法上文提到,这两个
25、方法可以用来间接调用函数。call()和apply()的第一个实参表示要调用函数的母对象,它是调用上下文,在函数内通过this来引用母对象。假如要想把函数func()以对象obj方法的形式来调用,可以这样:func.call(obj);func.apply(obj);call()和apply()的区别之处是,第一个实参(调用上下文)之后的所有实参传入的方式不同。func.call(obj, 1, 2); /实参可以为任意数量func.apply(obj, 1, 2); /实参都放在了一个数组中下面看一个有意思的函数,他能将一个对象的方法替换为一个新方法。这个新方法“包裹”了原始方法,实现了AO
26、P。/调用原始方法之前和之后记录日志消息function trace(o, m) var original = om; /在闭包中保存原始方法 om = function () /定义新方法 console.log(new Date(), Entering:, m); /输出日志消息 var result = original.apply(o, arguments); /调用原始方法 console.log(new Date(), Exiting:, m); /输出日志消息 return result; /返回结果 这种动态修改已有方法的做法,也被称作“猴子补丁(monkey-patching
27、)”。bind()方法bind()方法是ES5中新增的方法,这个方法的主要作用是将函数绑定至某个对象。该方法会返回一个新的函数,调用这个新的函数会将原始函数当作传入对象的方法来调用。function func(y) return this.x + y; /待绑定的函数var o = x: 1 ; /将要绑定的对象var f = func.bind(o);/通过调用f()来调用o.func()f(2); /=3ES3中可以通过下面的代码来实现bind()方法:if (!Function.prototype.bind) Function.prototype.bind = function (o /* , args */) /将this和arguments保存在变量中,以便在嵌套函数中使用。 var self = this, boundArgs = arguments; /bind()方法返回的是一个函数。 return function () /创建一个参数列表,将传入bind()的第二个及后续的实参都传入这个函数。 var ar
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1