Heim > Web-Frontend > js-Tutorial > Hauptteil

无废话JavaScript教程(全集)_javascript技巧

PHP中文网
Freigeben: 2016-05-16 19:00:09
Original
1132 Leute haben es durchsucht

许多读者认为我那本《JavaScript语言精髓与编程实践》读来辛苦,所以我一直想写个简单的读本。索性,这次就写个最简单的吧。

《程序员》2008.09期有一篇名为《无废话ErLang》的文章,这让我想到了许多的诸如“无废话C”、“无废话书评”这类的文章,也想到了JavaScript可没有一篇“无废话”,所以决定开个篇来写这个。与这个决定相关的,还因为另一个缘故:许多读者认为我那本《JavaScript语言精髓与编程实践》读来辛苦,所以我一直想写个简单的读本。索性,这次就写个最简单的吧。

声明一下:如果只想看复杂的东西,不要读这篇文章了。
一、JavaScript最初其实是过程式的
追溯到1.0时代的JavaScript,其实是过程式的。它的基本特性有只有两项,一项是能够直接放在网页的HTML
标签中去接管事件,例如:

复制代码 代码如下:




第二项则是支持一种简单的对象构造器(函数)。其实这个时代的构造器不如说是初始化函数更合适,它应当
这样写:

复制代码 代码如下:


function MyObject() {
this.xxx = '...';
this.yyy = '...';
}
obj = new MyObject();


所以,早期的JavaScript无可置疑地背上了“基于对象的过程式脚本语言”的名头,这一点也不冤枉。除了
上面两项特性,JavaScript有着一些一般性的脚本语言的性质,例如:
 -整个.js文件一次性装载到执行环境(例如WEB浏览器)中,在经历一次语法分析之后,开始逐行执行;
 -在上述语法分析周期,(具名的)函数和用"var"声明的变量被预先处理在一个标识符表中,以便脚本代码使用;
 -从全局的代码行或函数调用开始执行,整个过程中执行不到的代码不被查错(除第一步中的语法检错外)。
也具有通常的过程式语言的性质,例如:
 -有if/for/while/switch等语句;
 -用function来声明函数,使用"(..)"来声明它的形式参数表,以及表示函数调用和传参;
 -类似于C语言的基本语法,包括使用"{..}"来表示代码块,以及使用"!="等运算符号;
 -一个类似于Java语言的对象操作运算符"."号,和属性、方法这样的基本概念。
好了,现在你看到了一个基本的JavaScript语言,它的代码只有象C一样的函数与语句行,支持非常简单的面
向对象编程。OK,这其实也差不多是JavaScript的全部……嗯……全部的语法基础观念。如果你用过一门哪怕
稍稍入门一点的程序语言,你都会觉得JavaScript其实挺简单的。
是啊,“写个函数,然后调用它”,就这么简单。例如:
function hi() {
alert('hello, world.');
}
hi();
二、稍微复杂一点的是数据类型
JavaScript有六种基本数据类型,分为两类。一类是值类型,即undefined,string, number和boolean;一类
是引用类型,即function和object。检测数据X是何种类型,可以简单地使用"typeof X"来返回一个字符串。
值类型与引用类型在其它高级语言中,是用“访问过程中是传值还是传引用”来区别的。简单说,在下面函数
中:
function foo(X) {
}
X传入的是值本身,还是一个指向该值的引用(你可以想象成指针),表明了X是何种类型。与其它语言不同的
是,JavaScript不在调用入口上加指示字来说明传值的方法,例如:
function foo(var X) {
// 一般高级语言中,var表明总是传入变量X的引用
}
而是,简单的由脚本引擎根据实际传入的X的数据类型来决定如何传值。例如:
function foo(X) {
...
}
foo('123'); // <- 字符串'123'值
foo(aObj); // <- aObj是一个对象引用
能这样处理的关键,在于JavaScript类型系统足够简洁。六种基本类型包括了三个哲学化的观念:能执行的
与不能执行的;对象或非对象;有(值)或无(值)。显然,理解这种哲学性的思想并不容易,因为更复杂一层
的、自包含的逻辑是:函数也是对象、值也是对象、无值也是值。
这就是JavaScript类型系统的全部了。如果你想简单的用用,那么你记住下面的就够了:
 -string、number、boolean三种简单值类型是用来传给网页显示的;
 -object用来存放其它的object、funtion或上述简单值类型,并用'.'运算通过一个属性名找到它们;
 -undefined是用来检测数据有效无效的;
 -function是用来执行的。
当然,如果你要成为思想家或者语言学的疯子,那么去思考上面的哲学化命题吧,我不拦着你。
三、能用鼻子想通的就是直接量了
或许很多人都搞不明白JavaScript中的直接量声明,然而它确实是非常简单的。既然我们大多数高级语言都
支持常量声明,甚至最原始的汇编语言也支持立即值——例如:
// in C
#define ABYTE 256
// in delphi
const
ABYTE = 256
; in asm
mov ah, 256

那么JavaScript当然……必然……可以面无愧色地支持直接量了——他们其实是一个概念。例如:
// in javascript
var
aByte = 256
只不过在理解的时候,一定要切记:所有上述的代码中,所谓直接量或立即值,是指那个'256',而不是那
个变量或常量的标识符ABYTE/aByte。更进一步的,你要知道JavaScript支持了8种直接量声明:
--------------------
数值:支持整数,浮点和0x等进制前缀,以及……等等;
布尔值:true/false;
无值:undefined;
函数:function() { ... },也称为匿名函数;
字符串:使用'..'或"..",支持多行和转义符;
正则表达式:使用/../..,支持g,i,m等正则配置;
数组:使用[..],支持嵌套数组;
对象:使用{...},支持嵌套对象声明;
--------------------
你可以把上述字符量作为一个个体,用在代码——我的意思是表达式或语句行——的任意位置。用鼻子就可以
进行的推论是:
//既然可以写:
aaa = 'hello, ' + 'world';
//那么就必然可以写:
bbb = [1,2,3] + [4,5,6];
//同样也就必然可以写:
ccc = /abc/ + /cdf/
//同样:
// ……
如上的,你可以把所有的直接量放在表达式或语句中间。偶尔的,因为语法解析的必要,你可能需要用一对
括号把这个直接量括起来——否则语法上会出现歧义,例如下面这个:
ddd = (function() {}) + (function() {})
好了,直接量原本就这么简单,你只需要期望自己还有一个没退化的鼻子就好了。
四、原型继承
原型继承可能是这个世界上最简单不过的东西了。
我们假定一个对象是一张表——伟大的Anders就支持我这个假设,他说JavaScript的对象是“属性包”——
这样的一个表中存放的就是“name=value”这样的“名字/值”对。当我们想用下面的代码:
aObj.name
去找值(value)时,就在表里查一下(用delphi的人应该记得TStringList吧)。对象,哦,所谓对象——在
我曾经的理解里——就是“带有继承关系的结构体(struct/record)”。那么,继承关系是?
是这样,如果上面的查找不成功,对于原型继承来说,只需要在aObj这个对象的“原型”中找一下,就成了。
这个原型也是一个对象,记录在构造器函数的prototype属性中。例如:
function MyObject() {
// ...
}
MyObject.prototype = xxx;
var aObj = new MyObject()
zzz = aObj.name;
当在aObj中找不到name这个属性时,按照上面的规则,就会去xxx这个对象中找,也就是试图找找"xxx.name"。
由于xxx本身也是一个对象,也会有一个构造器函数(例如xxxObject()),因此当xxx.name也找不到时,就会
去xxxObject.prototype里面去找……如此……如此深层次的挖掘,直到再也找不到……就返回undefined。
多简单啊,所谓原型继承,只不过是一个简单的检索规则。
反过来说,你需要让aObj能访问到某个成员,也只需要修改一下它(或它们——一指类似aObj的实例)的原型
就好了。这在JavaScript中是非常常用的。例如你想让所有的string都有某个属性:
String.protoype.MyName = 'string'
又例如你想让所有的对象都有某个属性(或方法,或其它什么东东),那么:
Object.prototype.getMyName = function() {
return this.MyName;
}
多么美好,现在String也能getMyName了,函数也能getMyName了,所有没有名字的也有名字了——当然,名
字是undefined。
没名字也是名字,我没想过你会不会变成哲学疯子,对不起。

五、函数式
这个可不是JavaScript的发明,它的发明人已经死了,而他的这个发明还在困扰着我们……如同爱迪生的灯
泡还在照耀着我们。
其实函数式语言很简单,它就是一种与命令式语言同样“完备”的语言实现方案。由于它的基础思想与命令
式——如果你不想用这个难于理解的名词,那就把它换成C,或者Delphi好了——语言完全不同,所以大多数
情况下,它也与这些传统的、通用的、商业化的语言格格不入。
而事实上,你天天都在用它。
下面这行代码,就充满了函数式语言的思想:
a + b
是吗?真的,如果你把那个“+”号看成一个函数,就完全一样了。事实上,所谓函数(function),就是一个
执行过程、一段运算、一个功能入口……也就是说,代码中的某个东西,要么它是数据,要么它是运算。而如
果它是运算,你就可以把它看成“函数”。上面这行代码——表达式中,a和b显然是数据,而+号则是对之进
行操作的运算,所以它自然可以看成一个“功能、过程或运算”。所以……操作两个数求和的“函数”可以写
成这样:
func_add(a, b)
或者这样:
(+ a b)
所有这些,只是文字上的标记法不同而已。就好象我说这个符号是“jia”,而你非得说它是“暗得”,另一个
人却非要读作“a-d-d”。有什么不同吗?没有。
所有程序员天天都在写语句,写表达式,与运算逻辑。大家都知道这些东西都可以写在一个……嗯……无比巨
大的函数里,或者分在无数个小的、看起来很漂亮的函数里。当所有这些函数一个又一个连续起来运算时,它
就成了“函数式语言”。所以称为“函数式语言的祖老爷爷”的LISP就是这样“把函数运算连起来”的语言:

复制代码 代码如下:


(defun subst (x y z)
(cond ((atom z)
(cond ((eq z y) x)
('t z)))
('t (cons (subst x y (car z))
(subst x y (cdr z))))))


有人认为它是丑陋的意大利面条,也有人认为它是最简洁的语言。Oh,随你,了解了就好了,管它象什么尼。
由于函数式语言只有连续执行的函数——所以它没有了“语句”而又必须实现逻辑上的完整性。简单地说,他
需要在连续执行的过程中实现“顺序、分支与循环”三个基本逻辑。函数式的解决之道,是用三元条件运算来
代替if/then/else,也就下面的代码:
if a then b else c
// 相当于
a ? b : c
另外,就是用递归(函数调函数)来实现循环。考虑到递归函数调用中会导致栈溢出(Stack overflow at ...),
所以函数式语言就提出了“尾递归”,也就是在书写代码是“确保”仅只在函数内的最后一个运算中递归调用。
这样一来,这个递归调用就不需要保留栈了——因为再没有后续运算了,因此就能被优化成一行不需要返回的
jmp汇编指令。
世界真美好,所谓函数式不过是一堆运算入口,以及jmp、jmp、jmp。但,但是,难道不是吗——这块CPU?
六、更高级的函数式(1)
其实世界并不美好。
如果一块CPU死躺在那里,它也就只有顺序呀、分支啦、循环之类的指令在里头。但是当它运行起来,就得有一
个时钟在滴嗒…滴嗒…如果是多核的,还会同时有好几个这样的东东在滴嗒着。
这就是问题,世界原本不是单一时序,也不是守序的。所以会有并发,会有中断,也会有异常。函数式语言应
该自己应该像意大利面条一样无限延展开去,那么是不是有多个滴嗒时就该有多根意大利面条呢?但,这种情
况下还叫意大利面条吗?
函数式里的解决方案叫延续(Continuation)和结点。延续是停止当前转到另一处、然后再返回来;而结点则确
保一个位置上的代码是独立完备的,与另一个结点无关。在函数式语言中,多个结点就象平行宇宙,大多数情
况下它们是互相透明的,如果它们要发生联系,得需要极其巨大的能量,以及……一个虫洞。
多个结点的问题我们不需要深究,多个空间下的访问带来的时空问题,是天体学家以及时空多维理论家们研究
的问题,我们只需要知道:如果你希望多个结点之间存在交叉的访问关系,那么世界/宇宙会因此毁灭。这样类
似的说明写在ErLang这类天生支持多个宇宙的语言的白皮书、参考手册以及宇航员日常指南之中。如果你要穷
究其根源,并且认为自己已经能明确了解300个以上的高等数字、物理学和天体学术语,地么下面有一份面向程
序员的入口指引,不妨从这里开始:

http://www.php.cn/
延续是解决状态问题的方法之一。简单说来,有时间就有状态:有过去,现在和将来。对于过去,我们有历史,也就会因为过去发生了什么而决定现在发生什么,例如因为昨天喝高了小酒,所以今天只能吃稀粥;而现在也就是明天的昨天,所以要为了明天能做什么而记录下今天的状态,例如今天已经吃了稀粥;至于明天,当然,将要发生的事情很多,我们得一件一件地做好准备。
Oh,重要的就是这个“做好准备”了。这在计算机系统里叫:事件。或者叫计划,或者叫突发。知道为什么我们的PC机可以运行吗?En...是因为有一个指令流水线在按照一个足够微小的时间片去执行指令。对于运算系统来说,这个定时发生的指令访问行为,就是中断。所以有一天一个朋友问我:如果执行逻辑只有顺序、分支与循环,那么流程系统中的触发器算什么?我想了很久,无解。而现在来回答这个问题,就得把“时间”这个维度加上,所谓突发、并发以及类似的东西,是时序概念下的逻辑。
好了,当我们“做好准备”,为了将来要触发某个东西做准备的时候——简单的说,就当是个时钟处理程序好了(在windows中它叫OnTimer,在浏览器中它叫setTimeout/setInterval),为了这个时候我们能够在函数式语言中做好一顿意大利面条,我们说明:“我们已经做好了准备”,以及“我们做好了怎样的准备”。而按照函数式的基本原则,一个函数是与状态无关的,它与“将来”这样一个时序无关。这,变成了一个矛盾。
事实上开始我们已经与这个矛盾正面冲突了一次,这就是“循环”。因为循环需要一个状态量来指示循环进度,
这个“进度”就是时序相关的。而函数式使用了“递归”来解决它,也就是通过递归函数的参数来传递这个进度。在“递归参数”——这个界面的两边,函数式都是时序无关的。由此带来的问题就是栈溢出,解决方法则是尾递归;又由此带来的问题就是编程复杂性,以及能否证明尾递归能替代所有递归;解决方案是……温伯格说得没错,所有解决问题的方法都会带来新的问题。Oh...又是温伯格。 现在,我们明确的需要“更多的状态”了,因为我们已经将系统运行一个或是多个的时序里——也就CPU。即使我们有结点,而且保证“没有虫洞”,那么我们也需要解决一个CPU中的过去、现在与将来的问题。
函数式的仙家们说了两个字:持续。简单啊,就是把为过去、现在的状态,和为将来的准备作为函数的参数传过去。看起来,“现在”立即就要爆炸了,因为既要包括过去的、现在的状态以及变化,还要包括将来的运算。于是新的解决方案是:若要现在不爆炸,"持续"的界面上不要发生运算就好了。
运算由“将来”根据“过去”的数据做决策,这就是持续。支持它的函数式特性,就是在惰性求值。简单的说,
function f(x, c) {
...
c(x);
}
f(data, f);
由于在传送界面f()上,c本身是不求值的,所以爆炸会发生在/不会发生都是在将来c(x)进行运算的时候。如果
宇宙的熵有极限,那么这个极限也是在末可知的将来。而且,在末可知的将来,也有可能回扭曲回现在。这就是
持续的两个原则:
--------------------
一个成功持续之后,是一个新的持续(或过程)
一个失败持续将回到上一个选择点
--------------------
正是因为有持续的状态,且这个状态及持续本身都是通过函数参数传递的,所以,“回到选择点”只是将来自身
的一个决择,与现在的状态是无关的。
七、更高级的函数式(2)
无论如何,种种函数式的复杂性,确实是在编程范型中保持一种自我的纯粹性而存在的。例如生成器(产生器)
这个问题,由于一批数据的产生之间是有关系的(例如增量),而产生过程是时序相关的(不总是在一次调用中
得到全部的数据),因此函数式语言定义了一个“生成器”这样的函数概念。我们可以在生成器中定义一个yield,
这个返回就是“将来”回到这个“现在”的一个虫洞。通过这个虫洞,我们可以拿到一个时序相关的数据。下面
就是这样的一个例子(mozilla上的斐波那契数列示例):


八、结语
好象,好象这篇《无废话》有很多废话……哈哈,玩笑啦,真的全无废话,还是活人能看的木?
真的那么想读无废话,你应该去读大学教材了。或者,这里还有一本《JavaScirpt语言精髓与编程实践》。
看见木,语言……精髓哦……,哈哈。

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage