JavaScript语言精粹——函数(第二部分)
九.参数:
<script>//构造一个讲很多个值相加的函数//注意函数内部定义的变量 sum 不会与函数外部定义的 sum 产生冲突。//该函数只会看到函数内部的那个变量var sum = function(){var i,sum = 0;for(i =0;i<arguments.length;i +=1){sum += arguments[i];}return sum;};document.writeln(sum(4,8,15,16,23,42)); //108
</script>
这不是一个特别有用的模式。在第6章中,我们将会看到如何给数组添加一个相似的方法来达到同样的效果。
十.返回
当一个函数被调用时,它从第一个语句开始执行,并在遇到关闭函数体的)时结束。那使得函数把控制权交还给调用该函数的程序部分。
return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。
一个函数总是会返回一个值。如果没有指定返回值,则返回undefined .
如果函数以在前面加上 new前缀的方式来调用,且返回值不是一个对象,则返回this (该新对象)。
十一.异常
JavaScript 提供了一套异常处理机制。异常是干扰程序正常流程的非正常(但是并非完全是出乎意料)的事故。当查出这样的事故时,你的程序就应该抛出一个异常:
var add = function(a,b){if(typeof a !== 'number' || typeof b !=='number'){throw{name: "TypeError",message: "add needs number"};}return a+b;
}
throw 语句中断函数的执行。它应该抛出一个 exception 对象。该对象包含可识别的异常类型的 name 属性和一个描述性的 message 属性。你也可以添加其他的属性。
该 exception 对象将被传递到一个 try 语句的 catch 从句:
<script>var try_it = function(){try{add("sevent");}catch(e){document.writeln(e.name + ": "+ e.message);}}try_it()//ReferenceError: add is not defined
</script>
如果在 try 代码块内抛出一个异常,控制权就会跳转到他的 catch 从句。
一个 try 语句只会有一个将捕捉所有异常的 catch代码块。如果你的处理手段取决于异常的类型,那么异常处理器必须检查异常对象的 name 属性以确定异常的类型。
注:其实 try catch 语句也可以嵌套使用,还可以和 finally 一起使用,本文就不加以讲解,要想了解的同学可以百度以下,或者私信我。
十二.给类型增加方法
JavaScript 允许给语言的基本类型增添方法。在上一次对象的博客中,我们可以清晰地看到,通过给Object.prototype 添加方法来使得该方法所有对象可用。这样的方式对所有函数可用:
<script>Function.prototype.medthod = function(name,func){this.prototype[name] = func;return this;}
</script>
通过给 Function.prototype 增加一个 method 方法,我们就不必键入 prototype 这个属性名了。这个缺点也就被掩盖了
<script>Number.method ('integer' , function (){return Math[this< 0 ? 'ceiling' : 'floor' ](this);});document.writeln ( (-10 / 3) .integer ()) // -3
</script>
JavaScript缺少一个移除字符串末端空白的方法。那是一个很容易修复的疏忽:
<script>string.method ( 'trim', function (){return this.replace (/^\S+|\s+$/g, "") ;});document.writeln ( '"'+" neat ".trim() + '"');
</script>
我们的 trim方法使用了一个正则表达式。我们将在后面看到更多关于正则表达式的内容。
通过给基本类型增加方法,我们可以大大提高语言的表现力。因为JavaScript 原型继承的动态本质,新的方法立刻被赋予到所有的值(对象实例)上,哪怕值(对象实例)是在方法被创建之前就创建好了。
基本类型的原型是公共的结构,所以在类库混用时务必小心。一个保险的做法就是只在确定没有该方法时才添加它。
<script>
//有条件地增加一个方法
Function.prototype.method = function (name,func){if( !this.prototype [name] ){this.prototype [name] =func;}
}
</script>
十三.递归
递归函数会直接或间接地调用自身的一种函数。递归是一种强大的编程技术,它将一个问题分解为一组相似的子问题,每一个都用一个寻常解去解决。一般来说,一个递归函数调用自身去解决它的子问题。
<script>var hanoi = function (disc, src, aux, dst){if(disc > 0){hanoi (disc - 1,src, dst, aux);document.writeln ( 'Move disc ' + disc +' from ' +src + ' to ' +dst) ;hanoi (disc - 1,aux,src, dst);}}hanoi(3, 'Src','Aux' , 'Dst' );
</script>
递归函数可以非常高效地操作树形结构,比如浏览器端的文档对象模型(DOM)。每次递归调用时处理给定树的一小段。
十四.作用域
在编程语言中,作用域控制着变量与参数的可见性及生命周期。对于程序员来说这是一个重要的帮助,因为他减少了名称冲突,并且提供了自动内存管理。
var foo = function(){var a=3,b=5;var bar = function(){var b=7,c=11;//此时,a 为 3, b 为 7 ,c 为 11a += b+c;//此时,a 为 21, b 为 7 ,c 为 11};//此时,a 为 3, b 为 5 ,c 还没有定义bar();//此时,a 为 21, b 为 5
};
大多数使用C语言语法的语言都拥有块级作用域。在一个代码块中(括在一对花括号中的语句集)定义的所有变量在代码块的外部是不可见的。定义在代码块中的变量在代码块执行结束后会被释放掉。这是件好事。
糟糕的是,尽管代码块的语法似乎表现出它支持块级作用域,但实际上JavaScript 并不支持。这个混淆之处可能成为错误之源。
JavaScript确实有函数作用域。那意味着定义在函数中的参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都可见。
很多现代语言都推荐尽可能迟地声明变量。而用在JavaScript上的话却会成为糟糕的建议,因为它缺少块级作用域。所以,最好的做法是在函数体的顶部声明函数中可能用到的所有变量。
十五.闭包:
作用域的好处是内部函数可以访问定义他们的外部函数的参数和变量(除了 this 和 arguments)。这是一件非常好的事情。
我们的 getElementsByAttribute 函数可以工作是因为它声明了一个 results 变量,且传递给 walk_the_DOM 的内部函数也可以访问 results 变量。
一个更有趣的情形是内部函数拥有比它的外部函数更长的生命周期。
之前,我们构造了一个myobject对象,它拥有一个value属性和一个increment方法。假定我们希望保护该值不会被非法更改。
var myObject = function(){var value = 0;return {increment:function(inc){value += typeof inc ==="number"? inc:1;},getValue:function(){return value;}}
}();
我们并没有把一个函数赋值给 myObject 。我们是把调用该函数后返回的结果赋值给它。注意最后一行的()。该函数返回一个包含两个方法的对象,并且这些方法继续享有访问 value 变量的特权。
<script>//创建一个名为 que 的构造函数。//他构造出带有 get_status 方法和 status 私有属性的一个对象。var quo = function(stuats){return {get_status:function(){return status;}};};//构造一个 que 实例var myQue = que("amazed");document.writeln(myQue.get_status());
</script>
十六.回调
函数可以让不连续事件的处理变得更容易。例如:假定有这么一个序列,由用户交互开始,向服务器发送请求,最终显示服务器的响应。最纯朴的写法可能会是这样的:
request = prepare_the_request() ;
send_request_synchronously(request,function(response){display (response) ;
});
这种方式的问题在于网络上的同步请求将会导致客户端进入假死状态。如果网络传输或服务器很慢,响应性的降低将是不可接受的。
更好的方式是发起异步的请求,提供一个当服务器的响应到达时将被调用的回调函数。异步的函数立即返回,这样客户端不会被阻塞。
request =prepare__the_request ();
send_request_asynchronously(request,function (response){display (response);
});
我们传递了一个函数作为参数给send_request_asynchronously 函数,它将在收到响应时被调用。
17.模块
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;});};
}());
注意一下最后一行。我们用() 运算法立即调用我们刚刚构造出来的函数。这个调用所创建并返回的函数才是 deentityify 方法.
document.writeln('<">'.deentityify()); // <">
模块模式利用了函数作用域和闭包来创建绑定对象与私有成员的关联,在这个例子中,只有deentityify方法有权访问字符实体表这个数据对象。 模块模式的一般形式是:
模块模式也可以用来产生安全的对象。假定我们想要构造一个用来产生序列号的对象:
var serial_maker = function () {
// 返回一个用来产生唯一字符串的对象。
// 唯一字符串由两部分组成: 前缀 + 序列号。
// 该对象包含一个设置前缀的方法,一个设置序列号的方法。
// 和一个产生唯一序列号的gensym方法 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; seq += 1; return result; } };
};
var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym(); // unique is "Q1000"
如果我们把seqer.gensym作为一个值传递给第三方函数,那个函数能用它产生唯一字符串,但却不能通过它来改变prefix或seq的值。
18.级联
getElement('myBoxDiv').move(250, 150).width(100).height(100).color('red').border('10px outset').padding('4px').appendText("Please stand by").on('mousedown', function (m) {this.startDrag(m, this.getNinth(m));}).on('mousemove', 'drag').on('mouseup', 'stopDrag').later(2000, function () {this.color('yellow').setHTML("What hath God Wraught?").slide(400, 40, 200, 200);}).tip("This box is resizeable");
19.套用
函数也是只,从而我们可以用有趣的方式去操作函数值,套用允许我们将函数与传递给它的参数相结合去产生出一个新的函数。
var addl = add.curry(1);
document.writeln(addl(6)); // 7
Function.method('curry', function () {var args = arguments, that = this;return function () {return that.apply(null, args.concat(arguments));};
}); // 有些不太对头
Function.method('curry', function () {var slice = Array.prototype.slice,args = slice.apply(arguments),that = this;return function () {return that.apply(null, args.concat(slice.apply(arguments)));};
});
说明:
// concat方法
var a = [1,2,3];
document.write(a.concat(4,5));
// slice方法
var arr = new Array(3);
arr[0] = "George";
arr[1] = "John";
arr[2] = "Thomas";
document.write(arr.slice(1)); // John,Thomas
20.记忆
函数可以用对象去记住先前操作的结果,从而能避免无谓的运算。这种优化被称为记忆。JavaScript的对象和数组要实现这种优化是非常方便的。
比如说,我们想要一个递归函数来计算Fibonacci数列,一个Fibonacci数字是之前连个Fibonacci数字之和。最前面的两个数字是0和1。
var fibonacci = function (n) {return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
for (var i = 0; i <= 10; i++) {document.writeln('// ' + i + ': ' + fibonacci(i));
}
// 0: 0
// 1: 1
// 2: 1
// 3: 2
// 4: 3
// 5: 5
// 6: 8
// 7: 13
// 8: 21
// 9: 34
// 10: 55
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;
};
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;
};
现在,我们可以使用memoizer来定义fibonacci函数,提供其初始的memo数组和fundamental函数:
var fibnoacci = 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);
});
JavaScript语言精粹——函数(第二部分)相关推荐
- 《javascript语言精粹》读书笔记——函数
这几天发现了一本好书,又薄又精辟,<JavaScript语言精粹> 看了对象.函数这两节,发现书如其名,确实是精粹. 函数的调用 函数调用的方式有四种: - 方法调用模式 - 函数调用模式 ...
- 《JavaScript语言精粹》学习笔记(函数(2))
<JavaScript语言精粹>学习笔记(函数(2)) 函数(Functions) 参数(Arguments) 当参数被调用时,会得到一个"免费"的参数数组argume ...
- 精通javascript、javascript语言精粹读书笔记
于是选择两本书做最后的冲刺: 精通javascript(jquery之父著作),javascript语言精粹.希望我也能成为一个javascript的好手. Dom Elements的属性 精通jav ...
- 《JavaScript语言精粹 修订版》 读书笔记
之前看到这篇文章, 前端网老姚浅谈:怎么学JavaScript?,说到怎么学习JavaScript,那就是 看书.分析源码. 10本书读2遍的好处,应该大于一本书读20遍. 看书主动学习,看视频是被动 ...
- 读阮一峰对《javascript语言精粹》的笔记,我有疑问。
<javascript语言精粹>是一本很棒的书籍,其中作者在附录列出了12种他所认为的javascript语言中的糟粕. 我最近开始跟读前端前辈的博客,其中读到了阮一峰的<12种不宜 ...
- Javascript语言精粹之Array常用方法分析
Javascript语言精粹之Array常用方法分析 1.Array常用方法分析 1.1 Array.prototype.sort() Javascript的默认比较函数假定被排序元素都是字符串,所以 ...
- javascript语言精粹 微盘_JavaScript语言精粹(修订版)pdf
摘要 "学习.理解.实践大师的思想,我们才有可能站在巨人的肩上,才有机会赶超大师,这本书就是开始." "这是一本介绍JavaScript语言本质的重要书籍,值得任何正在或 ...
- JavaScript 语言精粹读书笔记
最近在看 赵泽欣 / 鄢学鹍 翻译的 蝴蝶书, 把一些读后感言记录在这里. 主要是把作者的建议跟 ES5/ES5.1/ES6 新添加的功能进行了对比 涉及到的一些定义 IIFE: Immediatel ...
- JavaScript语言精粹-读书笔记(1)
JS 语言精粹 蝴蝶书 点击链接下载电子书 这本书需要基本的JS基础和一定的经验.第一次看感觉很普通,工作一段时间后发现这本书很多细节很重要.这本书基于 ECMA3版本写的,现在已经普遍使用ECMA6 ...
最新文章
- nojy 105 九的余数
- linux光驱驱动目录,linux下挂载光驱
- 怎样使用python替代shell?
- linux 命令行 ctrl z,Linux操作系统下运行命令时CTRL+Z的作用
- android jni 字符串拼接,JNI 字符串拼接方法
- 表达式树练习实践:入门基础
- 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析
- Ubuntu 12.04 安装配置 Apache2
- 关于二叉树,你该了解这些!
- JavaScript开发者应懂的33个概念js-33-concepts
- ps2021没法用神经元滤镜,ps2021神经滤镜不能下载
- 计算机bios更改usb端口,联想电脑bios怎么设置usb接口
- Mybatis之分页插件PageHelper工作原理
- Activity生命周期走向分析
- 批处理文件rd \s\q **是什么意思?
- SpringBoot集成Minio搭建自己的分布式文件服务器(Minio集成篇)
- 什么是Teardrop攻击?我们要如何防御Teardrop攻击?
- 如何将局域网IP映射为公网IP
- VLClclc Plugin Object的方法
- 数据结构-树与深度优先遍历
热门文章
- textrank 算法
- 信息系统项目管理师-五大过程组与十大管理49个过程之间的对应关系总览
- 2022年无线蓝牙耳机推荐?口碑最好的国产无线蓝牙耳机推荐
- 正则验证密码格式(密码必须包含字母大小写、数字、特殊字符,且不能少于8位)
- java exchanger 应用场景_java多线程系列:Semaphore和Exchanger
- 基于泰尔森回归的股票预测研究
- 有两种歌声可以打动我
- 手把手教你如何下载各种文具用品的商品图
- Android判断直播结束,Android 直播打断事件处理
- 关于计算机游戏的英语读法,电脑游戏,computer game,音标,读音,翻译,英文例句,英语词典...