JavaScript权威指南模板

上传人:xin****ome 文档编号:60219016 上传时间:2022-03-07 格式:DOC 页数:10 大小:103.50KB
返回 下载 相关 举报
JavaScript权威指南模板_第1页
第1页 / 共10页
JavaScript权威指南模板_第2页
第2页 / 共10页
JavaScript权威指南模板_第3页
第3页 / 共10页
点击查看更多>>
资源描述
JavaScript 权威指南- 函数函数本身就是一段JavaScript代码,定义一次但可能被调用任意次。如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法。用于初始化一个新创建的对象的函数被称作构造函数。相对于其他面向对象语言,在JavaScript 中的函数是特殊的,函数即是对象。JavaScript 可以把函数赋值给变量,或者作为参数传递给其他函数,甚至可以给它们设置属性等。JavaScript 的函数可以嵌套在其他函数中定义,这样定义的函数就可以访问它们外层函数中的任何变量。这也就是所谓的“闭包”,它可以给JavaScript 带来强劲的编程能力。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 = function (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);函数命名函数名称要求简洁、描述性强,因为这样可以极大改善代码的可读性,方便别人维护代码;函数名称通常是动词或以动词开头的词组。通常来说,函数名编写有两种约定:一种约定是函数名第一个单词首字母小写,后续单词首字母大写,就像likeThis();当函数名包含多个单词时,另一种约定是用下划线来分割单词,就像like_this() 。项目中编写方法名时尽量选择一种保持代码风格一致。还有,对于一些私有函数(不作为公用API 的一部分),这种函数通常以一条下划线作为前辍。2.函数调用函数声明后需要通过调用才能被执行。JavaScript 中通常有4 种方式来调用函数:作为普通函数;作为对象方法;作为构造函数;通过它们的call() 和 apply()方法间接调用。下面就通过一些具体示例来演示上述4 中函数的调用方式。1.对于普通函数,通过调用表达式就可直接调用,这种方式很直接也很常见。/ 定义一个普通函数。var strict = function () return !this; ; /检测当前运行环境是否为严格模式。/ 通过函数名直接调用。console.log(strict();注:根据ES3 和非严格的ES5 对普通函数调用的规定,调用上下文(this)是全局对象;在严格模式下,调用上下文则是undefined 。2.通常,保存在对象属性里的JavaScript 函数被称作“方法” 。/ 定义一个对象直接量。var calc = a: null,b: null,add: function () /将函数保存在对象属性中。return this.a + this.b;/ 通过对象名调用方法。calc.a = 1, calc.b = 2;console.log(calc.add();注:对象方法中的调用上下文(this )不同于普通函数中的上下文。这里this 指代当前对象。方法链:当方法返回值是一个对象,那么这个对象还可以再调用它的方法。每次调用的结果都是另外一个表达式的组成部分,这种方法调用方式最终会形成一个序列,也被称为“方法链”。所以,在自己设计API 的时候,当方法并不需要返回值时,最好直接返回this 。这样以后使用API 就可以进行“链式调用”风格的编程。需要注意的是,this 是一个关键字,Javascript 语法不允许给它赋值。再者,关键字this 没有作用域的限制,嵌套的函数不会从外层调用它的函数中继承this。也就是说,如果嵌套函数作为方法调用,其this 指向为调用它的对象。如果嵌套函数作为函数调用,其this 值不是全局对象就是undefined 。下面通过一段代码来具体说明。var o = m: function () /对象中的方法var self = this; / 将 this 的值保存在一个变量中console.log(this = o); / 输出 true ,表明this 就是这个引用对象of(); / 调用嵌套函数f()function f() / 定义一个嵌套函数(* 普通函数,非对象方法)console.log(this = o); / 输出 false,this 的值为全局对象或undefinedconsole.log(self = o); / 输出 true ,变量self 指外部函数的this 值3.如果函数或者防方法调用之前带有关键字new ,它就构成构造函数调用。构造函数调用会创建一个新的对象,构造函数通常不使用return, 函数体执行完毕它会显示返回。还有,创建的对象继承自构造函数的prototype 属性,构造函数中使用this 关键字来引用这个新创建的对象。/ 与普通函数一样的定义方式。function 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() 两个方法可以用来间接地调用函数,这两个方法都可以显式指定调用函数里面的调用上下文this。/ 定义一个打印函数。function print() if (this.text) alert(this.text); else alert(undefined);/call 方法间接调用方法,并指定其调用上下文。print.call( text: hello );关于 call() 和 apply() 两个方法的用法以及区别下面详细讨论。3.函数的实参和形参JavaScript 中的函数定义不需要指定函数形参的类型,调用函数时也不检查传入形参的个数。这样,同时也会留下两个疑问给我们:当调用函数时的实参个数和声明的形参个数不匹配的时候如何处理;如何显式测试函数实参的类型,以避免非法的实参传入函数。下面就简单介绍JavaScript 是如何对上述两个问题做出处理的。可选参数当调用函数的时候传入的实参比函数定义时指定的形参个数要少,剩下的形参都将设置为undefined 。一般来说,为了保持函数较好的适应性,都会给省略的参数设置一个合理的默认值。function getPropertyNames(obj,/*optional*/arr) arr=arr|;for (var property in obj) arr.push(property); return arr;需要注意的是,当使用这种可选实参来实现函数时,需要将可选实参放在实参列表的最后。一般来书,函数定义中使用注释/*optional*/来强调形参是可选的。实参对象当调用函数时传入的参数个数超过了原本函数定义的形参个数,那么方法中可以通过实参对象来获取,标识符arguments 是指向实参对象的引用。实参对象是一个类数组对象,可以通过数字下标来访问传入函数的实参值。实参对象有一个重要的用处,就是让函数可以操作任意数量的实参,请看下面的例子:/ 返回传入实参的最大值。function max(/* . */) var max = Number.NEGATIVE_INFINITY; /该值代表负无穷大。for (var i = 0; i max) max = argumentsi;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 = function (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(num1, 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;当函数需要一个“静态”变量来在调用时保持某个值不变,最方便的方式就是给函数定义属性,而不是定义全局变量,因为定义全局变量会让命名空间变的杂乱无章。5.作为命名空间的函数函数中声明的变量只在函数内部是有定义,不在任何函数内声明的变量是全局变量,它在JavaScript 代码中的任何地方都是有定义的。JavaScript 中没有办法声明只在一个代码块内可见的变量的。基于这个原因,常常需要定义一个函数用作临时的命名空间,在这个命名空间内定义的变量都不会污染到全局变量。/ 该函数就可看作一个命名空间。function mymodule() / 该函数下的变量都变成了“ mymodule ”空间下的局部变量,不会污染全局变量。/ 最后需要调用命名空间函数。mymodule();上段代码还是会暴露出一个全局变量:mymodule 函数。更为常见的写法是,直接定义一个匿名函数,并在单个表达式中调用它:/ 将上面 mymodule() 函数重写成匿名函数,结束定义并立即调用它。(function () / 模块代码。();6.闭包闭包是 JavaScript 中的一个难点。在理解闭包之前先要明白变量作用域和函数作用域链两个概念。变量作用域:无非就是两种,全局变量和局部变量。全局变量拥有全局作用域,在任何地方都是有定义的。局部变量一般是指在函数内部定义的变量,它们只在函数内部有定义。函数作用域链:我们知道JavaScript 函数是可以嵌套的,子函数对象会一级一级地向上寻找所有父函数对象的变量。所以,父函数对象的所有变量,对子函数对象都是可见的,反之则不成立。需要知道的一点是,函数作用域链是在定义函数的时候创建的。关于“闭包”的概念书本上定义很具体,但是也很抽象,很难理解。简单的理解,“闭包”就是定义在一个函数内部的函数(这么说并不准确,应该说闭包是函数的作用域)。var scope = global scope; /全局变量function checkscope() var scope = local scope; /局部变量function f() return scope; /在作用域中返回这个值return f();checkscope(); /=local scope上面一段代码就就实现了一个简单的闭包,函数f()就是闭包。根据输出结果,可以看出闭包可以保存外层函数局部变量,通过闭包可以把函数内的变量暴露在全局作用域下。闭包有什么作用呢?下面一段代码是上文利用函数属性定义的一个计数器函数,其实它存在一个问题:恶意代码可以修改counter 属性值,从而让函数计数出错。/ 初始化函数对象的计数器属性。uniqueInteger.counter = 0;/ 先返回计数器的值,然后计数器自增1。function uniqueInteger() return uniqueInteger.counter+=1;闭包可捕捉到单个函数调用的局部变量,并将这些局部变量用作私有状态,故我们可以利用闭包的特性来重写uniqueInteger 函数。/ 利用闭包重写。var uniqueInteger = (function () /定义函数并立即调用var counter = 0; / 函数的私有状态return function () return counter += 1;)();/ 调用。uniqueInteger(); /=1uniqueInteger(); /=2uniqueInteger(); /=3当外部函数返回后,其他任何代码都无法访问counter变量,只有内部的函数才能访问。根据输出结果可以看出,闭包会使得函数中的变量都被保存在内存中,内存消耗大,所以要合理使用闭包。像 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因为重置了计数器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到这里如果你还不明白我在说什么,这里推荐两篇前辈们写的关于“闭包”的文章。阮一峰,学习Javascript 闭包( Closure)russj, JavaScript 闭包的理解7.函数属性、方法和构造函数前文已经介绍过,在JavaScript 中函数也是对象,它也可以像普通对象一样拥有属性和方法。length 属性在函数体里,arguments.length 表示传入函数的实参的个数。而函数本身的length 属性表示的则是“形参”,也就是在函数调用时期望传入函数的实参个数。function check(args) var actual = args.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() 方法上文提到,这两个方法可以用来间接调用函数。call() 和 apply() 的第一个实参表示要调用函数的母对象,它是调用上下文,在函数内通过this 来引用母对象。假如要想把函数func() 以对象obj方法的形式来调用,可以这样:func.call(obj);func.apply(obj);call()和 apply() 的区别之处是,第一个实参(调用上下文)之后的所有实参传入的方式不同。func.call(obj, 1, 2); / 实参可以为任意数量func.apply(obj, 1, 2); /实参都放在了一个数组中下面看一个有意思的函数,他能将一个对象的方法替换为一个新方法。这个新方法“包裹”了原始方法,实现了AOP。/ 调用原始方法之前和之后记录日志消息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 )”。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() 方法:/ 将 this 和 arguments 保存在变量中,以便在嵌套函数中使用。var self = this, boundArgs = arguments;/bind() 方法返回的是一个函数。return function () / 创建一个参数列表,将传入bind() 的第二个及后续的实参都传入这个函数。var args = , i;for (var i = 1; i boundArgs.length; i+) args.push(boundArgsi);for (var i = 0; i global scopeFunction() 构造函数可以看作是在全局作用域中执行的eval(),在实际开发中很少见到。8.函数式编程JavaScript 中可以像操控对象一样操控函数,也就是说可以在JavaScript 中应用函数式编程技术。使用函数处理数组假设有一个数组,数组元素都是数字,我们想要计算这些元素的平均值和标准差。可以利用map() 和 reduce() 等数组方法来实现,符合函数式编程风格。/ 首先定义两个简单的函数。var sum = function (x, y) return x + y; var square = function (x) return x * x / 将上面的函数和数组方法配合使用计算出平均数和标准差。var data = 1, 1, 3, 5, 5;var mean = data.reduce(sum) / data.length;var deviations = data.map(function (x) return x - mean; );高阶函数所谓高阶函数就是函数操作函数,它接收一个或多个函数作为参数,并返回一个新的函数。/ 返回传入函数 func 返回值的逻辑非。function not(func) return function () var result = func.apply(this, arguments);return !result;/ 判断传入参数 a 是否为偶数。var even = function (x) return x % 2 = 0;var odd = not(even); /odd为新的函数,所做的事和even() 相反。1, 1, 3, 5, 5.every(odd); /=true每个元素都是奇数。这里是一个更常见的例子,它接收两个函数f()和 g(),并返回一个新的函数用以计算f(g() 。/ 返回一个新的函数,计算function compose(f, g) return function () f(g(.)。/ 需要给f()传入一个参数,所以使用f() 的 call() 方法。/ 需要给 g()传入很多参数,所以使用g()的 apply()方法。return f.call(this, g.apply(this, arguments);var square = function (x) return x * x; var sum = function (x, y) return x + y; var squareofsum = compose(square, sum);squareofsum(2, 3); /=25记忆能将上次计算的结果缓存起来,在函数式编程当中,这种缓存技巧叫做“记忆”。下面的代码展示了一个高阶函数,memorize() 接收一个函数作为实参,并返回带有记忆能力的函数。/ 返回 f()的带有记忆功能的版本。function memorize(f) / 将值保存在闭包中。var cache = ;return function () / 将实参转换为字符串形式,并将其用做缓存的键。if (key in cache) return cachekey; else return cachekey = f.apply(this, arguments);memorize() 所返回的函数将它的实参数组转换成字符串,并将字符串用做缓存对象的属性名。如果缓存中存在这个值,则直接返回它,否则调用既定的函数对实参进行计算,将计算结果缓存起来并保存。下面代码展示了如何使用memorize() :/ 返回两个整数的最大公约数。function gcd(a, b) var temp;if (a = btemp = b;b = a;a = temp;while (b != 0) / 这里是求最大公约数的欧几里德算法temp = b;b = a % b;a = temp;return a;var gcdmemo = memorize(gcd);gcdmemo(85, 187);/ 当写一个递归函数时,往往需要实现记忆功能。var factorial = memorize(function (n) return (n 1209.参考与扩展本篇内容源自我对JavaScript 权威指南第8 章 函数章节的阅读总结和代码实践。总结的比较粗糙,你也可通过原著或MDN 更深入了解函数。
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 演讲稿件


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!