《JavaScript语言精粹》知识点总结(二)
来源:互联网 发布:淘宝助理怎么导出宝贝 编辑:程序博客网 时间:2024/06/01 08:59
第4章 函数
函数用于指定对象的行为。一般来说,所谓编程就是将一组需求分解成一组函数与数据结构的技能。
4.1 函数对象
在JS中函数就是对象。对象是“名/值”对的集合并拥有一个连到原型对象的隐藏连接。对象字面量产生的对象连接到 Object.prototype。函数对象连接到 Function.prototype。每个函数在创建时附有两个附加的隐藏属性:函数的上下文和实现函数行为的代码。
函数是对象,但与众不同之处在于它们可以被调用。
4.2 函数字面量
函数可以通过函数字面量来创建:
var add = function (a, b) { return a + b;};函数字面量包括四个部分:
一、保留字 function。
二、函数名,若省略则为匿名函数。
三、参数,可无。
四、函数主体。
函数字面量可以出现在任何允许表达式出现的地方。函数也可以被定义在其他函数中。一个内部函数自然可以访问自己的参数和变量,同时它也能方便地访问它被嵌套在其中的那个函数的参数与变量。
通过函数字面量创建的函数对象包含一个连到外部上下文的连接。这被称为闭包。它是JS强大表现力的根基。
4.3 调用
JS中一共有四种调用模式:方法调用、函数调用、构造器调用、apply调用。这些模式在如何初始化关键参数 this 上存在差异。
1. 方法调用
当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this 被绑定到该对象。如果一个调用表达式包含一个属性存取表达式(即一个 . 点表达式或 [subscript] 下标表达式),那么它被当作一个方法来调用。
//创建myObject。它有一个value属性和一个increment(增加)方法。//increment方法接受一个可选的参数。如果参数不是数字,那么默认使用数字1.var myObject = { value: 0, increment: function (inc) { this.value += typeof inc === 'number' ? inc : 1; } };myObject.increment();document.writeln(myObject.value); //1myObject.increment(2);document.writeln(myObject.value); //3方法可以使用 this 去访问对象,所以它能从对象中取值或修改对象。通过 this 可取得它们所属对象的上下文的方法称为公共方法。
2. 函数调用
如果在一个函数前面带上 new 来调用,那么将创建一个隐藏连接到该函数的 prototype 成员的新对象,同时 this 将会绑定到那个
当一个函数并非一个对象的属性时,那么它被当作一个函数来调用。
var sum = add ( 3, 4);
当函数以此模式调用时,this 被绑定到全局对象,这是语言设计的错误,应该绑定到外部函数的 this 变量。这个设计错误的后果是方法不能利用内部函数来帮助工作。但有一个解决办法,如果该方法定义一个变量并给它赋值为 this,那么内部函数就可以通过那个变量访问到 this。
//给myObject添加一个double方法。myObject.double = function () { var that = this; //解决方法 var helper = function () { that.value = add(that.value, that.value); }; helper(); //以函数形式调用helper}//以方法形式调用doublemyObject.double();document.writeln(myObject.value); //6
3. 构造器调用模式
var Quo = function (string) { this.status = string;};Quo.prototype.get_status = function() { return this.status;};var myQuo = new Quo('confused');document.writeln(myQuo.get_status()); //confused这种方法并不推荐。
4. apply 调用
apply 方法让我们构建一个参数数组并用其去调用函数。它也允许我们选择 this 的值。apply 方法接受两个参数,第一个是将绑定给 this 的值。第二个就是一个参数数组。
//构造一个包含两个数字的数组,并将它们相加。var array = [3, 4];var sum = add.apply(null, array); //sum的值为7//构造一个包含status成员的对象。var statusObject = { status: 'A-OK'};var status = Quo.prototype.get_status.apply(statusObject); //status值为'A-OK'
4.4 参数
无须指定参数个数的情况
//注意该函数内部定义的变量sum不会与函数外部定义的sum产生冲突//该函数只会看到内部的那个变量var sum = function () { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum;};document.writeln(sum(4, 8, 15, 16, 23, 42));但这并不是一个特别有用的模式。
arguements并不是一个真正的数组,它只是拥有 length 属性,但缺少所有数组的方法。
4.5 返回
如果函数以在前面加上 new 前缀的方式来调用,且返回值不是一个对象,则返回 this (该新对象)。
4.6 异常
var add = function (a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw { name: 'TypeError', message: 'add needs numbers' }; } return a + b;};//构造一个try_it函数,用不正确的方式调用之前的add函数var try_it = function () { try { add('seven'); } catch(e) { document.writeln(e.name + ': ' + e.message); }};try_it();
4.7 给类型增加方法
通过给 Function.prototype 增加方法来使得该方法对所有函数可用(要先确定没有该方法时才添加它)。
Function.prototype.method = function(name, func) { if (!this.prototype[name]) { this.prototype[name] = func; } return this;};只提取数字中的整数
Number.method('integer', function () { return Math[this < 0 ? 'ceiling' : 'floor'](this);});document.writeln(-3.3.integer()); //-3移除字符末端空白的方法
String.method('trim', function () { return this.replace(/^\s+|\s+$/g, '');});document.writeln('"' + " neat ".trim() + '"'); //"neat"
4.8 递归
//定义walk_the_DOM函数,它从某个给定的节点开始,按HTML源码中的顺序//访问该树的每个节点。//它会调用一个函数,并依次传递每个节点给它。walk_the_DOM调用自身去处理//每一个节点。var walk_the_DOM = function walk(node, func) { func(node); node = node.firstChild; while(node) { walk(node, func); node = node.nextSibling; }};
//定义getElementsByAttribute函数,它取得一个属性名称字符串//和一个可选 的匹配值//它调用walk_the_DOM,传递一个用来查找节点属性名的函数。//匹配的节点会累积到一个结果数组中。var getElementsByAttribute = function (attr, value) { var results = []; walk_the_DOM(document.body, function (node) { var actual = node.nodeType === 1 && node.getAttribute(att); if (typeof actual === 'string' && (actual === value || typeof value !== 'string') ) { results.push(node); } }); return results;};阶乘
var factorial = function factorial(i, a) { a = a || 1; if (i < 2) { return a; } return factorial(i - 1, a * i);}document.writeln(factorial(4)); //24
4.9 闭包
var myObject = function () { var value = 0; return { increment: function (inc) { value += typeof inc === 'number' ? inc : 1; }, getValue: function () { return value; } }}();我们是把调用函数的结果赋值给 myObject,注意最后一行的()。该函数返回一个包含两个方法的对象,并且这些方法继续享有访问value变量的特权。
下面的 quo 函数被设计成无须在前面加上 new 来使用,所以名字也没有首字母大写。
//创建一个quo的构造函数。//它构造出带有get_status方法和status私有属性的一个对象。var quo = function (status) { return { get_status: function () { return status; } };};//构造一个quo实例var myQuo = quo('amazed');document.writeln(myQuo.get_status());下面的例子更有用,注意要等 document 加载完成后再调用此函数。
// 定义一个函数,它设置一个DOM节点为黄色,然后把它渐变为白色var fade = function (node) { var level = 1; var step = function () { var hex = level.toString(16); node.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; setTimeout(step, 100); } }; setTimeout(step, 100);};fade(document.body);
理解内部函数能访问外面函数的实际变量而无须复制:
//糟糕的例子var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i++) { nodes[i].onclick = function (i) { alert(i); }(i); }};//结束糟糕的例子//更好的例子var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i++) { nodes[i].onclick = function (i) { return function () { alert(i); }; }(i); }};add_the_handler函数目的是给每个时间处理器一个唯一值(i)。糟糕的例子未能达到目的是因为事件处理器函数绑定了变量 i ,而不是函数在构造时变量 i 的值。
更好的例子中,我们定义了一个函数并立即传递i进去执行,而不是把一个函数赋值给 onclick。那个函数将返回一个事件处理器函数。这个事件处理器函数绑定的是传递进去的 i 的值,而不是定义在add_the_handlers 函数里的 i 的值。那个被返回的函数被赋值给 onclick。
4.11 回调
<span style="color:#000000;">//同步的请求,不好,会导致客户端进入假死状态。request = prepare_the_request();response = send_request_synchronously(request);display(response);//异步的情况,好,提供一个当服务器的响应到达时将被调用的回调函数。//异步的函数立即返回,这样客户端不会阻塞。request = prepare_the_request();send_request_asynchronously(request, function (response) { display(response);});</span>
4.12 模块
String.method('deentityify', function () { //字符实体表,它映射字符实体的名字到对应的字符 var entity = { quot: '"', lt: '<', gt: '>', }; //返回deentityify方法。 return function () { //这才是deentityify方法,它调用字符串的replace方法。 //查找'&'开头和';'结束的子字符串,如果这些字符可以在字符实体表中找到。 //那么就将该字符实体替换为映射表中的值,它用到了一个正则表达式 return this.replace(/&([^&;]+);/g, function (a, b) { var r = entity[b]; return typeof r === 'string' ? r : a; } ); };}());
再构造一个产生序列号的方法。
var serial_maker = function () { var prefix = ''; var seq = 0; return { set_prefix: function (p) { prefix = String(p); }, set_seq: function (s) { seq = s; }, gensym: function () { var result = prefix + seq; return result; } };};var seqer = serial_maker();seqer.set_prefix('Q');seqer.set_seq(1000);var unique = seqer.gensym();document.writeln(unique);seqer包括的方法都没用到 this 或 that。因此没有办法损害 seqer。只能调用方法才能改变prefix 和 seq 的值。
4.14 套用
Function.method('curry', function () { var slice = Array.prototype.slice; var args = slice.apply(arguments); var that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); };});var add4 = add.curry(4);document.writeln(add4(6));
4.15 记忆
//调用次数太多var fibonacci = function (n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);};//调用次数较少var fibonacci = function () { var memo = [0, 1]; var fib = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = fib(n - 1) + fib(n - 2); memo[n] = result; } return result; }; return fib;}();for (var i = 0; i < 10; i++) { document.writeln('//' + i + ': ' + fibonacci(i) + '<br>');}
一般情况
var memoizer = function (memo, fundamental) { var shell = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = fundamental(shell, n); memo[n] = result; } return result; }; return shell;};var fibonacci = memoizer([0, 1], function (shell, n) { return shell(n - 1) + shell(n - 2);});var factorial = memoizer([1, 1], function (shell, n) { return n * shell(n - 1);})for (var i = 0; i < 10; i++) { document.writeln('//' + i + ': ' + fibonacci(i) + '<br>');}for (var i = 0; i < 10; i++) { document.writeln('//' + i + ': ' + factorial(i) + '<br>');}
- 《JavaScript语言精粹》知识点总结(二)
- 《JavaScript语言精粹》知识点总结(一)
- 函数(二)----Javascript语言精粹
- JavaScript语言精粹 读书笔记(二)
- javascript语言精粹笔记(二)对象
- javascript 语言精粹 学习笔记(二)
- JavaScript语言精粹学习笔记-函数(二)
- JavaScript语言精粹(二)— 闭包
- 【JavaScript语言精粹】读书笔记(二)——对象
- Javascript知识点总结(二)
- JavaScript语言精粹之函数篇(二)
- JavaScript语言精粹(修订版)
- javascript语言精粹(笔记)
- 《JavaScript语言精粹(修订版)》试读
- 读《JavaScript语言精粹(修订版)》心得
- JavaScript语言精粹 读书笔记(一)
- JavaScript语言精粹 读书笔记(三)
- javascript语言精粹(蝴蝶书)-笔记
- Git代理设置,加速clone
- C++程序设计课程主页-2015级
- 理解并发编程的几种"性" -- 可见性,有序性,原子性 http://blog.sina.com.cn/s/blog_4adc4b090102whzx.html
- Ajax的工作原理
- 文件存储读写的工具类
- 《JavaScript语言精粹》知识点总结(二)
- POJ 1847 - Tram
- 最近正准备找工作呢,熟悉下递归算法,做了几个递归的例子包括汉诺塔问题
- 两个排序算法--冒泡排序,选择排序
- 寻找发帖王---sql语句
- 轩辕互动面试题两道比较复杂的
- java面试题_SQL_取某年的各个月份的发邮件数
- 搭建myeclipse+maven+tomcat的开发环境
- 面试时能和面试官聊的一些struts1的特性