JavaScript语言精粹

上传人:文*** 文档编号:62329445 上传时间:2022-03-14 格式:DOC 页数:68 大小:259.50KB
返回 下载 相关 举报
JavaScript语言精粹_第1页
第1页 / 共68页
JavaScript语言精粹_第2页
第2页 / 共68页
JavaScript语言精粹_第3页
第3页 / 共68页
点击查看更多>>
资源描述
文档供参考,可复制、编制,期待您的好评与关注! 第1章 精华JavaScript的特性中有一部分特性带来的麻烦远远超出它们的价值。其中,一些特性是因为规范很不完善,从而可能导致可移植性的问题;一些特性会导致生成难以理解和修改的代码;一些特性促使我的代码风格过于复杂且易于出错;还有一些特性就是设计错误。有时候语言的设计者也会犯错。大多数编程语言都有精华部分和鸡肋部分。我发现如果只使用精华部分而避免使用鸡肋的部分,我可以成为一个更好的程序员。毕竟,用糟糕的部件怎么可能构建出好东西呢?标准委员会想要移除一门语言中的缺陷部分,这几乎是不可能的,因为这样做会损害所有依赖于那些鸡肋部分的糟糕程序。除了在已存在的一大堆缺陷上堆积更多的特性,他们通常无能为力。并且新旧特性并不总是能和谐共处,可能从而产生出更多的鸡肋部分。但是,你有权力定义你自己的子集。你完全可以基于精华部分去编写更好的程序。JavaScript中鸡肋部分的比重超出了预料。在短到令人吃惊的时间里,它从不存在发展到全球采用。它从来没有在实验室里被试用和打磨。当它还非常粗糙时,它就被直接集成到网景的Navigator 2浏览器中。随着JavaTM的小应用程序(Java applets)的失败,JavaScript变成了默认的“网页语言”。作为一门编程语言,JavaScript的流行几乎完全不受它的质量的影响。好在JavaScript有一些非常精华的部分。JavaScript最本质的部分被深深地隐藏着,以至于多年来对它的主流观点是:JavaScript就是一个丑陋的、没用的玩具。本书的目的就是要揭示JavaScript中的精华,让大家知道它是一门杰出的动态编程语言。或许只学习精华部分的最大好处就是你可以不用考虑鸡肋的部分。忘掉不好的模式是非常困难的。这是一个非常痛苦的工作,我们中的大多数人都会很不愿意面对。有时候,制定语言的子集是为了让学生更好的学习。但在这里,我制定的JavaScript子集是为了主专业人员更好的工作。1.1 为什么要使用JavaScriptJavaScript是一门重要的语言,因为它是web浏览器的语言。它与浏览器的结合使它成为世界上最流行的编程语言之一。同时,它也是世界上最被轻视的编程语言之一。浏览器的API和文档对象模型(DOM)相当糟糕,导致JavaScript遭到不公平的指责。JavaScript是最被轻视的语言,因为它不是所谓的主流语言。如果你擅长某些主流语言,但却在一个只支持JavaScript的环境中编程,那么被迫使用JavaScript确是相当令人厌烦的。大多数人觉得没必要去先学好JavaScript,但结果他们会惊讶地发现,JavaScript跟他们宁愿使用的主流语言有很大不同,而且这些不同至为关键。JavaScript令人惊异的事情是,在对这门语言没有太多了解,甚至对编程都没有太多了解的情况下,你也能用它来完成工作。它是一门拥有极强表达能力的语言。当你知道要做什么时,它甚至能表现得更好。编程是很困难的事情。绝不应该在对此一无所知时便开始你的工作。1.2 分析JavaScriptJavaScript建立在一些非常好的想法和少数非常坏的想法之上。那些非常好的想法包括函数、弱类型、动态对象和一个富有表现力的字面量表示法。那些坏的想法包括基于全局变量的编程模型。JavaScript的函数是(主要)基于词法作用域(lexical scoping)的顶级对象。JavaScript是第一个成为主流的lambda语言。实际上,相对Java而言,JavaScript与Lisp和Scheme有更多的共同点。它是披着C外衣的Lisp。这使得JavaScript成为一个非常强大的语言。现今大部分编程语言中都流行要求强类型。其原理在于强类型允许编译器在编译时检测错误。我们能越早检测和修复错误,付出的代价就越小。JavaScript是一门弱类型的语言,所以JavaScript编译器不能检测出类型错误。另一方面,弱类型其实是自由的。我们无须建立复杂的次,我永远不用做强制类型转换,也不用疲于应付类型系统以得到想要的行为。JavaScript有非常强大的对象字面量表示法。通过列出对象的组成部分,它们就能简单地被创建出来。这种表示法产生了流行的数据交换格式JSON。原型继承是JavaScript中一个有争议的特性。JavaScript有一个无类别(class-free)的对象系统,在这个系统中,对象直接从其他对象继承属性。这真的很强大,但是对那些被训练使用类去创建对象的程序员们来说,原型继承是一个陌生的概念。如果你尝试对JavaScript直接应用基于类的设计模式,你将会遭受挫折。但是,如果你学习使用JavaScript的原型本质,那么你的努力将会有所回报。JavaScript在关键思想的选择上饱受非议。虽然在大多数情况下,这些选择是合适的。但是有一个选择相当糟糕:JavaScript依赖于全局变量来进行连接。所有编译单元的所有顶级变量被撮合到一个被称为全局对象的公共命名空间中。这是一件糟糕的事情,因为全局变量是魔鬼,并且在JavaScript中它们是基础性的。在少数情况下,我们不能忽略鸡肋的部分。另外还有一些不可避免的糟粕,当涉及这些部分时,我们会将它们指出来。如果你想学习那些鸡肋的部分及如何拙劣地使用它们,请参阅任何其他的JavaScript书籍。JavaScript是一门有许多差异的语言。它包含很多错误和尖锐的边角(sharp edges),所以你可能会疑惑:“为什么我要使用JavaScript?”有两个答案。第一个是你没有选择。Web已变成一个重要的应用开发平台,而JavaScript是唯一一门所有浏览器都可以识别的语言。很不幸,Java在浏览器环境中失败了。JavaScript的蓬勃发展,恰恰说明了JavaScript确有其过人之处。另一个答案是,尽管JavaScript有缺陷,但是它真的很优秀。它既轻量又富有表现力。并且一旦你熟练掌握了它,就会发现函数式编程是一件很有趣的事。但是为了更好地使用这门语言,你必须知道它的局限。我将会无情地揭示它们。不要因此而气馁。这门语言的精华部分足以弥补它鸡肋的不足。1.3 一个简单的试验场如果你有一个Web浏览器和任意一个文本编辑器,那么你就有了运行JavaScript程序所需要的一切。首先,请创建一个HTML文件,可以命名为program.html:接下来,在同一个文件夹内,创建一个脚本文件,可以命名为program.js:document.writeln(Hello, world!);下一步,用你的浏览器找开你的HTML文件去查看结果。本书贯彻始终都会用到一个method方法去定义新方法。下面是它的定义:Function.prototype.method=function(name,func)this.prototypename=func;return this;第2章语法本章介绍JavaScript的精华部分的语法,并简要地概述其语言结构。2.1空白空白可能表现为格式化字符或注释的形式。空白通常没有意义,但是偶尔须要用它来分隔字符序列,否则它们就会被合并成一个单一的符号。例如,对如下代码来说:var that = this;var和that之间的空格是不能被移除的,但是其他的空格都可以被移除。JavaScript提供两种注释形式,一种是用/* */包围的块注释,另一种是以/为开头的行注释。注释应该被充分地用来提高程序的可读性。必须注意的是,注释一定要精确地描述代码。没有用的注释比没有注释更糟糕。用/* */包围的块注释形式来自于一门叫PL/I(默然说话:Programming Language One的简写。当中的“I”其实是罗马数字的“一”,它是一种IBM公司在19世纪50年代发明的第三代高级编程语言)的语言。在JavaScript中,*/可能出现在正则表达式字面上,所以块注释对于被注释的代码块来说是不安全的。例如:/* var rm_a = /a*/.match(s);*/导致了一个语法错误。所以,我建议避免使用/* */注释,而用/注释代替它。2.2 标识符标识符由一个字母开头,其后可选择性地加上一个或多个字母数字或下划线。标识符不能使用下面这些保留字:abstractboolean break bytecase catch char class const continuedebugger default delete do doubleelse enum export extendsfalse final finally float for functiongotoif implements import in instanceof int interfacelongnative new nullpackage private protected publicreturnshort static super switch synchronizedthis throw throws transient true try typeofvar volatile voidwhile with在这个列表中的大部分保留字尚未用在这门语言中。这个列表不包括一些本应该被保留而没有保留的字,诸如undefined、NaN和Infinity。JavaScript不允许使用保留字来命名变量或参数。更糟糕的是,JavaScript不允许在对象字面量中,或者在一个属性存取表达式的点号之后,使用保留字作为对象的属性名。标识符被用于语句、变量、参数、属性名、运算符和标记。2.3 数字JavaScript只有一个单一的数字类型。它在内部被表示为64位的浮点数,和Java的double一样。在JavaScript中,1和1.0是相同的值。如果一个数字字面量有指数部分,那么这个字面量的值是由e之前的部分乘以10的e之后部分的次方计算出来的。所以100和1e2是相同的数字。负数可以用前缀运算符-来构成。值NaN是一个数值,它表示一个不能产生正常结果的运算结果。NaN不等于任何值,包括它自己。你可以用函数isNaN(number)检测NaN。值Infinity表示所有大于1.79769313486231570e+308的值。数字拥有方法(参见第8章)。JavaScript有一个对象Math,它包含一套作用于数字的方法。例如,可以用Math.floor(number)方法将一个数字转换成一个整数。2.4 字符串字符串字面量可以被包围在单引号或双引号中,它可能包含0个或多个字符。是转义字符。JavaScript在被创建的时候,Unicode是一个16位的字符集,所以JavaScript中的所有字符都是16位的。JavaScript没有字符类型。要表示一个字符,只须创建仅包含一个字符的字符串即可。转义字符允许把那些正常情况下不被允许的字符插入到字符串中,比如反斜线、引号和控制字符。u约定允许指定用数字表示的字符码位。“A”=”u0041”字符串有一个ength属性。例如,”seven”.length是5。字符串是不可变的。一旦字符串被创建,就永远无法改变它。但通过+运算符去连接其他的字符串从而得到一个新字符串是很容易的。两个包含着完全相同的字符且字符顺序也相同的字符串被认为是相同的字符串。所以:c+a+t = cat是true。字符串有一些方法(参见第8章)。2.5 语句一个编译单元包含一组可执行的语句。在web浏览器中,每个标签都提供一个被编译且立即执行的编译单元。因为缺少链接器,JavaScript把它们一起抛入一个公共的全局命名空间中。附录A有更多关于全局变量的内容。当var语句被用在函数的内部时,它定义了这个函数的私有变量。switch、while、for和do语句允许有一个可选的前置标签(label),它配合break语句来使用。语句往往按照从上到下的顺序被执行。JavaScript可以通过条件语句(if和switch)、循环语句(while、for和do)、强制跳转语句(break、return和throw)和函数调用来改变这个执行序列。代码块是包在一对花括号中的一组语句。不像许多其他的语言,JavaScript中的代码块不会创建一个新的作用域,因此变量应该被定义在函数的顶端,而不是在代码块中。if语句根据表达式的值改变程序的控制流程。如果表达式的值为真,那么执行then代码块,否则,执行可选的else分支。下面列出的值被当作假:fasenullundefined数字0数字NaN其他所有的值都被当作真,包括true,字符串”false”,以及所有的对象。switch语句执行一个多路分支。它把其表达式的值和所有指定的case条件进行匹配。其表达式可能产生一个数字或字符串。当找到一个精确的匹配时,执行匹配的case从句中的语句。如果没有找到任何匹配,则执行可选的default语句。一个case从句包含一个或多个case表达式。case表达式不一定必须是常量。为了防止继续执行下一个case,case语句后应该跟随一上强制跳转语句。你可以用break语句去退出一个switch语句。while语句执行一个简单的循环。如果表达式值为假,那么循环将终止。而当表达式值为真时,代码块将被执行。for语句是一个结构更复杂的循环语句。它有两种形式。常见的形式由三个可选从句控制:初始化从句(initialization)、条件从句(condition)和增量从句(increment)。首先,;初 始化从句被执行,它的作用通常是初始化循环变量。接着计算条件从句的值。典型的情况是它根据一个完成条件检测循环变量。如果条件从句被省略掉,则假定返回 的条件是真。如果条件从句的值为假,那么循环将终止。否则,执行代码块,然后执行增量从句,接着循环会重复执行条件从句。另一种形式(被称为for in语句)会枚举一个对象的所有属性名(或键名)。在每次循环中,对象的另一个属性名字符串被赋值给for和in之间的变量。通常你须通过检测object.hasOwnProperty(variable)来确定这个属性名就是该对象的成员,还是从其原型链里找到的。for(myvar in obj)if(obj.hasOwnProperty(myvar)do语句就像while语句,唯一的区别是它在代码块执行之后而不是之前检测表达式的值。这就意味着代码块将总是要执行至少一次。try语句执行一个代码块,并捕获该代码块抛出的任何异常。catch从句定义了一个新的变量,它将接收该异常对象。throw语句抛出一个异常。如果throw语句在一个try代码块中,那么控制权会跳到catch从句中。如果throw语句在函数中,则该函数调用被放弃,且控制权会跳到调用该函数的try语句的catch从句中。throw语句中的表达式通常是一个对象字面量,它包含一个name属性和一个message属性。异常捕获器可以使用这些信息去决定该做什么。return语句会使一人函数提前返回。它也可以指定要被返回的值。如果没有指定返回表达式,那么其返回值是undefined。JavaScript不允许在return关键字和表达式之间换行。break语句会使程序退出一个循环语句或switch语句。它可以指定一个可选的标签,那将会使程序退出带该标签的语句。JavaScript不允许在break关键字和标签之间换行。一个expression语句可以给一个或多个变量或成员赋值,或者调用一个方法,或者从对象中删除一个属性。运算符=被用于赋值。不要把它和恒等运算符=混淆。运算符+=可以用于加法运算或连接字符串。2.6 表达式三元运算符?有三个运算数。如果第一个运算数值为真,它产生第二个运算数的值。但是,如果第一个运算数为假,它会产生第三个运算数的值。表2-1:运算符优先级.()属性存取及函数调用delete new typeof +-!一元运算符*/%乘法、除法、取模+-加法/连接、减法= 不等式运算符= !=等式运算符&逻辑与|逻辑或?:三元typeo运算符产生的值有number、string、boolean、undefined、function、object。如果运算数是一个数组或null,那么结果是object,这是不对的。第6章和附录A将会有更多关于typeof的内容。/运算符可能会产生一个非整数结果,即使两个运算数都是整数。函数调用引发函数的执行。函数调用运算符是跟随在函数名后面的一对圆括号。圆括号中可能包含将会传递给这个函数的参数。第4章将会有更多关于函数的内容。一个属性存取表达式用于指定一个对象或数组的属性或元素。下一章我将详细描述它。2.7 字面量对象字面量是一种方便指定新对象的表示法。属性名可以是标识符或字符串。这些名字被当作字面量名而不是变量名来对待,所以对象的属性名在编译时才能知道。属性的值就是表达式。下一章将会有更多关于对象字面量的信息。数组字面量是一个方便指定新数组的表示法。第6章将会有更多关于数组字面量的内容。第7章将会有更多关于正则表达式的内容。函数字面量定义了函数值。它可以有一个可选的名字,用于递归地调用自己。它可以指定一个参数列表,这些参数将作为变量由调用时传递的实际参数(arguments)初始化。函数的主体包括变量定义和语句。第4章将会有更多关于函数的内容。第3章 对象JavaScript的简单类型包括数字、字符串、布尔值(true和false)、null值和undefined值。其他所有的值都是对象。数字、字符串和布尔值“貌似”对象,因为它们拥有方法,但它们是不可变的。JavaScript中的对象是可变的键-值集合(keyed collections)。在JavaScript中,数组是对象,函数是对象,正则表达式是对象,当然,对象自然也是对象。对象是属性的容器,其中每个属性都拥有名字和值。属性的名字可以是包括空字符串在内的任意字符串。属性值可以是除undefined值之外的任何值。JavaScript中的对象是无类型(默然说话:或者说JavaScript只有一种类型,就是对象)(class-free)的。它对新属性的名字和值没有约束。对象适合用于收集和管理数据。对象可以包含其他对象,所以它们可以容易地表示成树形或图形结构。JavaScript包括一个原型链特性,允许对象继承另一对象的属性。正确地使用它能减少对象初始化的时间和内存消耗。3.1 对象定义对象定义提供了一种非常方便地创建新对象值的表示法。一个对象定义就是包围在一对花括号中的零或多个“名:值”对。对象定义可以出现在任何允许表达式出现的地方。var empty_object=;var stooge=“first-name”:”Jerome”,“last-name”:”Howard”;属性名可以是换手空字符串在内的任何字符串。在对象定义中,如果属性名是一个合法的JavaScript标识符且不是保留字,并不强制要求用引号括住属性名。所以用引号括住”first-name”是必须的,但是否括住first_name则是可选的。逗号用来分隔多个“名:值”对。属性的值可以从包括另一个对象定义在内的任意表达式中获得。对象是可嵌套的:var flight=airline:”Oceanic”,number:815,departure:IATA:”SYD”,time:”2004-09-22 14:55”,city:”Sydney”,arrival:IATA:”LAX”,time:”2004-09-23 10:42”,city:”Los Angeles”;3.2 检索要检索对象中包含的值,可以采用在后缀中括住一个字符串表达式的方式。如果字符串表达式是一个常数,而且它是一个合法的JavaScript标识符而非保留字,那么也可以用点(.)表示法代替。优先考虑使用点(.)表示法,因为它更紧凑且可读性更好。stooge“first-name”/”Joe”flight.departure.IATA/”SYD”如果你尝试检索一个并不存在的成员元素的值,将返回一个undefined值。stooge“middle-name”/undefinedstooge“FIRST-NAME” /undefinedflight.status/undefined|运算符可以用来填充默认值:var middle=stooge“middle-name”|”(none)”;var status=flight.status|”unknown”;尝试检索一个undefined值将会导致TypeError异常。这可以通过&运算符来避免错误。flight.equipment/undefinedflight.equipment.model/throw “TypeError”flight.equipment & flight.equipment.model/undefined3.3 更新对象中的值可以通过赋值语句来更新。如果属性名已经存在于对象中,那么这个属性的值被替换。stoogefirst-name=Jerome;如果对象之前并没有这个属性,那么该属性就被扩充到该对象中。stoogemiddle-name=Lester;flight.equipment=model:Boeing 777;3.4 引用对象通过引用来传递。它们永远不会被拷贝:var x=stooge;x.nickname=Curly;var nick=stooge.nickname;/因为x和stooge是指向同一个对象的引用,所以nick为Curlyvar a=,b=,c=;/a,b和c每个都引用一个不同的空对象a=b=c=/a,b和c都引用同一个空对象。3.5 原型每个对象都连接到一个原型对象,它可以继承属性。所有通过对象定义创建的对象都连接到Object.prototype这个JavaScript中标准的对象。当你创建一个新对象时,你可以选择某个对象作为它的原型。JavaScript提供的实现机制杂乱而复杂,但其实它可以被明显地简化。我们将给Object增加一个beget方法。这个beget方法创建一个使用原对象作为其原型的新对象。下章将会有更多关于函数的内容。if(typeof(Object.beget!=function)Object.beget=function(o)var F=function();F.prototype=o;return new F();var another_stooge=Object.beget(stooge);原型链在更新时是不起作用的。当我们对某个对象做出改变时,不会触及到该对象的原型:another_stoogefirst-name=Harry;another_stoogemiddle-name=Moses;another_stooge.nickname=Moe;stoogefirst-name;/不会被更新为Harry原型连接只有在检索值的时候才被用到。如果我们尝试去获取对象的某个属性值,且该对象没有此属性名,那么JavaScript会试着从原型对象中去获取属性值,直到该过程最后到达终点Object.prototype。如果找不到该属性,结果就是undefined。这个过程称为委托。如果我们添加一个新的属性到原型中,该属性会立即对所有基于该原型创建的对象可见。stooge.profession=actor;another_stooge.profession/actor我们将会在第6章中看到更多关于原型链的内容。3.6 反射JavaScript似乎没有直接提供相关反射的处理,所以需要自己编写代码实现,检查对象并确定对象有什么属性。我们可以使用typeof来确定属性的类型。typeof(flight.number)/numbertypeof(flight.status)/stringtypeof(flight.arrival)/objecttypeof(flight.manifest)/undefined但原型链中的任何属性也会产生一个值。我们可以使用hasOwnProperty方法,如果对象拥有独有的属性,它将返回true。hasOwnProperty方法不会检查原型链。flight. hasOwnProperty(number)/trueflight. hasOwnProperty(constructor)/false3.7 枚举for in语句可用来遍历一个对象中的所有属性名。该枚举过程将会列出所有的属性包括函数和你可能不关心的原型中的属性所以有必要过滤掉那些你不想要的值。最为常用的过滤器是hasOwnProperty方法,以及使用typeof来排除函数:for(var name in another_stooge)if(typeof(another_stoogename)!=function)document.writeln(name+”:”+another_stoogename);for in无法保证属性名出现的顺序,因此要对任何可能出现的顺序有所准备。如果你想要确保属性以特定的顺序出现,最好的办法就是完全避免使用for in语句,而是创建一个数组,在其中以正确的顺序包含属性名。再通过使用for而不是for in,可以得到我们想要的属性,而不用担心可能发掘出原型链中的属性,并且我们按正确的顺序取得了它们的值。var i;var properties = first-name, middle-name, last-name, profession;for (i = 0; i properties.length; i += 1) document.writeln(propertiesi + : + another_stoogepropertiesi); 3.8 删除delete运算符可以用来删除对象的属性。它将会移除对象中确定包含的属性。它不会触及原型链中的任何对象。删除对象的属性可能会让来自原型链中的属性浮现出来:another_stooge.nickname /Moe/删除another_stooge的nickname属性,从而暴露出原型的nickname属性delete another_stooge.nickname;another_stooge.nickname/Curly3.9 减少全局变量污染JavaScript可以很随意地定义那些可保存所有应用资源的全局变量。不幸的是,全局变量削弱了程序的灵活性,所以应该避免。最小化使用全局变量的一个方法是在你的应用中创建唯一一个全局变量:var MYAPP=;该变量此时变成了你的应用的容器:MYAPP.stooge=“first-name”:”Joe”,“last-name”:”Howard”;MYAPP.flight=airline:”Oceanic”,number:815,departure:IATA:”SYD”,time:”2004-09-22 14:55”,city:”Sydney”,arrival:IATA:”LAX”,time:”2004-09-23 10:42”,city:”Los Angeles”;只要把多个全局变量都整理在一个名称空间下,你将显著降低与其他应用程序、组件或类库之间产生糟糕的相互影响的可能性。你的程序也会变得更容易阅读,因为很明显MYAPP.stooge指向的是顶层结构。在下一章中,我们将会看到使用闭包来进行信息隐藏的方式,它是另一个有效减少全局污染的方法。第4章 函数(8)函数调用模式var add=function(a,b) return a+b;当一个函数并非一个对象的属性时,那么它被当作一个函数来调用:var sum=add(3,4); /sum的值为7当函数以此模式调用时,this被绑定到全局对象。这是语言设计上的一个错误。倘若语言设计正确,当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。幸运的是,有一个很容易的解决方案:如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this。按照约定,我给那么变量命名为that/给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.getValue(); /6(9)当函数被调用时,会得到一个“免费”奉送的参数,那就是arguments数组。通过它函数可以访问所有它被调用时传递给它的参数列表,包括那些没有被分配给函数声明时定义的形式参数的多余参数。这使得编写一个无须指定参数个数的函数成为可能:/构造一个将很多个值相加的函数/注意该函数内部定义的变量sum不会与函数外部定义的sum产生冲突。/该函数只会看到内部的那个变量。var sum=function() var i,sum=0; for(i=0;iarguments.length;i+) sum+=argumentsi; return sum;document.writeln(sum(4,8,15,16,23,42); /108因为语言的一个设计错误,arguments并不是一个真正的数组。它只是一个“类似数组”的对象。arguments拥有一个length属性,但它缺少所有的数组方法。我们将在本章结尾看到这个设计错误导致的后果。=with 语句为一个或一组语句指定默认对象。用法:with () ;with 语句通常用来缩短特定情形下必须写的代码量。在下面的例子中,请注意 Math 的重复使用:x = Math.cos(3 * Math.PI) + Math.sin(Math.LN10);y = Math.tan(14 * Math.E);当使用 with 语句时,代码变得更短且更易读:with (Math) x = cos(3 * PI) + sin(LN10);y = tan(14 * E);this 对象返回“当前”对象。在不同的地方,this 代表不同的对象。如果在 JavaScript 的“主程序”中(不在任何 function 中,不在任何事件处理程序中)使用 this,它就代表 window 对象;如果在 with 语句块中使用 this,它就代表 with 所指定的对象;如果在事件处理程序中使用 this,它就代表发生事件的对象。=1父框架到子框架的引用知道了上述原理,从父框架引用子框架变的非常容易,即:window.framesframeName;这样就引用了页面内名为frameName的子框架。如果要引用子框架内的子框架,根据引用的框架实际就是window对象的性质,可以这样实现:window.framesframeName.framesframeName2;这样就引用到了二级子框架,以此类推,可以实现多层框架的引用。2子框架到父框架的引用每个window对象都有一个parent属性,表示它的父框架。如果该框架已经是顶层框架,则window.parent还表示该框架本身。3兄弟框架间的引用如果两个框架同为一个框架的子框架,它们称为兄弟框架,可以通过父框架来实现互相引用,例如一个页面包括2个子框架: 在frame1中可以使用如下语句来引用frame2:self.parent.framesframe2;4不同层次框架间的互相引用框架的层次是针对顶层框架而言的。当层次不同时,只要知道自己所在的层次以及另一个框架所在的层次和名字,利用框架引用的window对象性质,可以很容易地实现互相访问,例如:self.parent.frameschildName.framestargetFrameName;5对顶层框架的引用和parent属性类似,window对象还有一个top属性。它表示对顶层框架的引用,这可以用来判断一个框架自身是否为顶层框架,例如:/判断本框架是否为顶层框架if(self=top) /dosomething改变框架的载入页面对框架的引用就是对window对象的引用,利用window对象的location属性,可以改变框架的导航,例如:window.frames0.location=1.html;这就将页面中第一个框架的页面重定向到1.html,利用这个性质,甚至可以使用一条链接来更新多个框架。 link=创建一个文档元素:document.createElement(),document.createTextNode()单选按钮组(单选按钮的名称必须相同):取单选按钮组的长度document.forms0.groupName.length下拉列表框的值:document.forms0.selectName.optionsn.value (n有时用下拉列表框名称加上.selectedIndex来確定被选中的值)Math.max(value1,value2)返回两个数中的最在值,Math.pow(value1,10)返回value1的十次方,Math.round(value1)四舍五入函数,Math.floor(Math.random()*(n 1)返回隨机数添加到收藏夹:external.AddFavorite( ;, jaskdlf );JS中碰到脚本错误时不做任何操作:window.onerror = doNothing; 指定错误句柄的语法为:window.onerror = handleError;JS中的self指的是当前的窗口document.body.noWrap=true;防止链接文字折行.第4章 函数JavaScript中最好的特性就是它对函数的实现。它几乎无所不能。但是,想必你也能预料到,函数在JavaScript里也并非万能药。函数包含一组语句,它们是JavaScript的基础模块单元,用于代码复用、信息隐藏和组合调用。函数用于指定对象的行为。一般来说,所谓编程就是将一组需求分解成一组函数与数据结构的技能。4.1 函数对象在JavaScript中函数就是对象。对象是“名:值”对的集合并拥有一个连到原型对象的隐藏连接。对象定义产生的对象连接到Object.prototype。函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype)。每个函数在创建时附有两个附加的隐藏属性:函数的上下文和实现函数行为的代码(JavaScript创建一个函数对象时,会给该对象设置一个“调用”属性。当JavaScript调用一个函数时,可理解为执行了此函数的“调用”属性。具体参阅ECMAScript规范的13.2 Creating Function Object)。每个函数对象在创建时也随带有一个prototype属性。它的值是一个拥有constructor属性且值即为该函数的对象。这和隐藏连接到Function.prototype完全不同。这个令人费解的构造过程的意义将会在下个章节中揭示。因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以存放在变量,对象和数组中,函数可以被当作参数传递给其他函数,函数也可以再返回函数。而且,因为函数是对象,所以函数可以拥有方法。4.2 函数定义函数对象可以通过函数定义来创建:/创建一个名为add的变量,并用来把两个数字相加的函数赋值给它。var add=function(a,b)return a+b;函数定义包括四个部分。第一个部分是关键字function第二部分是函数名,它可以被省略。函数可以用它的名字来递归地调用自己。此名字也能被调试器和开发工具来识别函数。如果没有给函数命名,比如上面这个例子,它会被认为是匿名函数。函数的第三部分是包围在圆括号中的一组参数。其中每个参数用逗号分隔。这些名称将被定义为函数中的变量。它们不像普通的变量那样将被初始化为undefined,而是在该函数被调用时初始化为实际提供的参数的值。第四部分是包围在花括号中的一组语句。这些语句是函数的主体。它们在函数被调用时执行。函数定义可以出现在任何允许表达式出现的地方。函数也可以被定义在其他函数中。一个内部函数自然可以访问自己的参数和变量,同时它也能方便地访问它被嵌套在其中的那个函数的参数与变量。通过函数定义创建的函数对象包含一个连到外部上下文的连接。这被称为闭包。它是JavaScript强大表现力的根基。4.3 调用调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的形式参数,每个函数接收两个附加的参数:this和arguments。参数this在面向对象编程中非常重要,它的值取决于调用的模式。在JavaScript中一共有四种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。调 用运算符是一对圆括号。圆括号内可包含零个或多个用逗号隔开的表达式。每个表达式产生一个参数值。每个参数值被赋予函数声明时定义的形式参数名。当实际参 数个数与形式参数的个数不匹配时不会导致运行时错误。如果实际参数过多,超出的参数值将被忽略。如果实际参数值过少,缺失的值将会被替换为undefined。对参数值不会进行类型检查:任何类型的值都可以被传递给参数。方法调用模式当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果一个调用表达式包含一个属性存取表达式(即一个点(.)表达式或下标()表达式),那么它被当作一个方法来调用。/创建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到对象的绑定发生在调用的时候。这个“超级”迟绑定使得函数可以对this高度复用。通过this可取得它们所属对象的上下文的方法称为公共方法。函数调用模式当一个函数并非一个对象的属性时,那么它被当作一个函数来调用:var sum=add(3,4);/sum的值为7当函数以此模式调用时,this被绑定到全局对象。这是语言设计上的一个错误。倘若语言设计正确,当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。幸运的是,有一个很容易的解决方案:如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this。按照约定,我给那么变量命名为that/给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.getValue();/6构造器调用模式JavaScript是一门基于原型继承的语言。这意味着对象可以直接从其他对象继承属性。该语言是无类别的。这偏离了当今编程语言的主流。当今大多数语言都是基于类的语言。尽管原型继承有着强大的表现力,但它并不被广泛理解。JavaScript本身对其原型的本质也缺乏信心,所以它提供了一套和基于类的语言类似的对象构建语法。有类型化语言编程经验的程序员们很少有愿意接受原型继承的,并且认为借鉴类型化语言的语法模糊了这门语言真实的原型本质。真是两边都不讨好。如果在一个函数前面带上new来调用,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将会被绑定到那个新对象上。new前缀也会改变return语句的行为。我们将会在后面看到更多相关的内容。/创建一个名为Quo的构造器函数。它构造一个带有status属性的对象。var Quo=function(string)this.status=string;/给Quo的所有实例提供一个名为get_status的公共方法。Quo.prototype.get_status=function()return this.status;/构造一个Quo实例var myQuo=new Quo(“confused”);document.writeln(myQuo.get_status();/confused按照约定,需要结合new前缀调用的函数被称为构造器函数,它们保存在以首字母大写的变量里。如果调用构造器函数时没有在前面加上new,可能会发生非常糟糕的事情,既没有编译时警告,也没有运行时警告。个人不推荐使用这种形式的构造器函数。下一章会看到更好的替代方式。apply调用模式因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法。apply方法让我们构建一个参数数组并用其去调用函数。它也允许我们选择this的值,apply方法接收两个参数,第一个是将被绑定给this的值,第二个就是一个参数数组。var array=3,4;var sum=add.appl
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 各类标准


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

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


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