不同于HTML标记,JavaScript区分大小写,这意味着大小写很重要。counter和Counter是两个不同的变量。
代码块中的所有语句被视为一个整体,要么都执行,要么都不执行。
如何将JavaScript代码加入网页
可将代码嵌入<head>元素中。
也可以将代码内嵌在网页的<body>元素中
:浏览器加载网页时,将先加载元素内的所有内容,再加载元素。因此,如果将代码放在中,用户可能必须等一会儿才能看到网页。如果将代码放在的HTML后面,则用户等待这些代码加载时就能看到网页的内容。
还可将代码放在独立的文件中,并在<head>中链接该文件
:放在外部文件中,代码更容易维护(独立于HTML),
还可用于多个网页。但这种方法也有缺点,那就是所有代码都将在网页的部分之前加载。
可在网页的<body>元素中链接到外部文件
。这是鱼和熊掌兼得的最佳方式,既有一个可用于任何网页且易于维护的JavaScript文件,又在网页末尾引用它,这样它将在网页加载完毕后再加载。
不能在引用JavaScript文件的同时内嵌JavaScript代码。
使用src特性时,不能在
函数prompt显示的对话框有一个取消按钮。如果用户单击该按钮,函数prompt将返回什么呢?
答: 如果用户单击这个对话框中的取消按钮,函数prompt将返回null(而不是一个字符串)。别忘了,null表示没有值;就这里而言,这是合适的,因为用户取消了而没有输入值。我们可利用这一点,通过检查返回值是否为null来判断用户是否单击了取消按钮;
函数Math.random
通过调用这个函数,可获得一个随机的小数:我们需要的是0~4的整数(即0、1、2、3或4),而不是小数(如0.34)。为此,可将Math.random返回的数字乘以5,以获得更接近目标的数字。
现在需要做的是,将小数部分删除,得到一个
整数。为此,可使用另一个数学函数——Math.floor:向下取整
函数
浏览器读取这些代码时,发现这是一个函数,进而查看函数体中的语句。浏览器知道,不应马上执行这个函数的语句,而要等到在代码的其他地方调用了这个函数时再执行
它们向函数传递实参时,首先复制其值,再将这个值赋给相应的形参。
按值传递的真正意义在于,在函数中修改形参的值时,只会影响形参,而不会影响传递给函数的变量
return语句的函数的执行过程
return语句包含一个将作为函数调用结果返回的值或表达式。
问: 如果函数没有return语句,
它将返回什么?
答: 没有return语句的函数返回
undefined。
全局变量和局部变量
全局变量的寿命与网页一样长。 全局变量在JavaScript代码加载到网页之后降生,并在网页消失后死去。重新加载网页时,将销毁并重新创建所有的全局变量。
局部变量通常在函数结束时消失。 局部变量是在函数被调用后创建的,并一直活到函数返回(无论函数是否返回值)。然而,在局部变量的大限到来前,可从函数返回它们的值。
局部变量与全局变量同名
问:
我在网页中使用了一些全局变量,同时加载了其他JavaScript文件。请问这些文件的全局变量是独立的吗?
答:
只有一个全局作用域,因此加载的每个文件看到的变量都相同(它们创建的全局变量位于同一个空间内)。因此,避免使用的变量发生冲突(并尽可能少用甚至不用全局变量)至关重要。
问: 如果形参与全局变量同名,它会遮住全局变量吗?
答:
会。与在函数中声明了与全局变量同名的局部变量一样,如果使用了与全局变量同名的形参,该形参也
会遮住相应的全局变量。只要在函数中不使用该全局变量,遮住它就没有什么关系,但最好使用注释对这一点
进行说明,以免以后阅读代码时感到迷惑。
浏览器分两遍读取网页:第一遍读取所有的函数定义,第二遍开始执行代码。这让你可以将函数放在文件的任何地方。
数组
问:
能更详细地说说稀疏数组吗?
答:
稀疏数组是有些索引处有值,而其他索引处没有值的数组。创建稀疏数组易如反掌,如下所示:
var sparseArray = [ ];
sparseArray[0] = true;
spraseArray[100] = true;
在这个示例中,sparseArray只有两个值,这两个值都是true,分别位于索引0和100处。其他索引处的值都undefined。这个数组虽然只包含两个值,但其长度却是101。而且会占用内存
对象
要删除属性,可使用一个特殊的关键字delete。可以像下面这样使用关键字delete:
delete fido.dogYears;
删除属性时,不仅删除了属性的值,还删除了属性本身。因此,删除属性dogYears后,如果你试图
使用fido.dogYears,结果将为undefined。
若成功删除了属性,delete表达式将返回true。仅当属性无法删除时,delete才返回false;这种情况是可能出现的,例如对象属于浏览器而受到保护。即便你要删除的属性在对象中不存在,delete也将返回true。
与使用一系列变量相比,使用对象有何优势?
答:
对象封装了数据的复杂性,让你能够专注于代码的高层次设计,而不是细枝末节。假设你要编写一个设计10辆车的交通模拟器,你肯定希望专注于汽车、路灯和马路,而不是数百个变量。对象还让你的工作更轻松,因为它们封装(隐藏)对象状态和行为的复杂性,让你无需操心这些东西。这些都是如何实现的呢?等你有了更多对象方面的经验后,就会明白得多。
变量并不实际存储对象。
变量存储指向对象的引用。
引用就像指针,是对象的存储地址。
换句话说,变量并不存储对象本身,而是存储类似于指针的东西。在JavaScript中,我们并不知道引用变量存
储的到底是什么,但我们知道,不管它存储的是什么,它肯定指向相应的对象。
当我们使用句点表示法时,JavaScript解释器将负责根据引用获取对象并访问其属性。
将对象赋给变量时,变量存储的是指向对象的引用,而不是对象本身。再次重申,可将引用视为指向对象的指针
因此,调用函数并向它传递一个对象时,传递的是对象引用,而不是对象
本身。根据按值传递的语义,传递给形参的是该引用的副本,而原来的引用依然是指向原始对象的指针
。
这意味着什么呢?一个最大的不同是,如果你在函数中修改对象的属性,修改的将是原始对象的属性
。因此,函数结束时,在函数中对对象所做的修改都依然有效
。
有一个JavaScript关键字this,可使用它来告诉JavaScript,你说的是当前所处的对象。
关键字this的工作原理
可将this视为一个变量,指向其方法被调用的对象。换句话说,如果你使用fiat.start()调用对象fiat的方法start,并在方法
start中使用this,那么this将指向对象fiat。
每当方法被调用时,在该方法体内都可使用this来引用方法被调用的对象
问:
方法和函数有何不同?
答:
方法是在对象中赋给了属性名的函数。
你通过函数名来调用函数,而调用方法时,你使用对象句点表示法和属性名。在方法中,你还可使用关键字this来引用方法被调用的对象。
问:
this的值在什么时候被设置为相应的对象?是在定义对象时还是调用方法时?
答:
在你调用方法时,this的值被设置为相应的对象。因此,当你调用fiat.start()时,this被设置为fiat,而当你调用chevy.start()时,this被设置为chevy。看起来好像this是在你定义对象时被设置的,因为在fiat.start中,this总是被设置为fiat,而在chevy.start中,this总是被设置为chevy,但你将在本书后面看到,有充分的理由表明this是在你调用方法而不是定义对象时设置的。这一点很重要,我们将反复讨论。
如果有人给了我一个对象,有办法知道它都包含哪些属性吗?
办法之一是迭代这个对象的属性。 为此,可使用还未介绍过的一种迭代方式for in,这种迭代器以随机方式遍历对象的所有属性。下面演示了如何显示对象chevy的所有属性。
for (var prop in chevy) {
console.log(prop + ": " + chevy[prop]);
3.22
了解DOM
DOM,即文档对象模型(document object model)
planet.innerHTML = “Red Alert: hit by phaser fire!”;
我们用来从DOM中获取一个元素的getElementById。方法getElementById接受一个id,并返回该id指定的元素。
从DOM获取的到底是什么
使用getElementById从DOM获取元素时,得到的是一个元素对象,你可使用它来读取、修改或替换元素的内容和特性。神奇之处在于,当你修改元素时,所做的修改也将反映到网页中。
重要的事先办,咱们再来看看刚才从DOM获取的元素对象。我们知道,这个元素对象表示的是网页中id为greenplanet的
元素,该元素的文本内容为All is well。与其他JavaScript对象一样,元素对象也有属性和方法。就元素对象而言,我们可以使用其属性和方法来读取和修改元素。下面是你可对元素对象执行的一些操作。
获取内容(文本或HTML)。
读取特性。
修改内容。
添加特性。
修改特性。
删除特性
查找内部HTML
innerHTML是个重要的属性,使用它可以读取或替换元素的内容。如果你查看innerHTML的值,看到的将是包含在元素中的内容(元素的HTML标签除外),“内部”HTML因此而得名。
问:
调用document.getElementById时,如果传入的id不存在,结果将如何?
答:
根据id从DOM获取元素时,如果指定的id不存在,getElementById将返回null。调用getElementById时,检查返回的是否是null是个不错的主意,这样可确保只在返回了一个元素时才访问其属性。
问:
也可使用document.getElementById根据类来获取元素吗?例如,如果有很多属于planets类的元素,是否可以这样做?
答:
不能,但你的思路是对的。调用g et-ElementById时,只能传入id。但有一个名为getElementsByClassName的DOM方法,可用来根据类名获取元素。调用这个方法时,返回的是一个元素集合,其中包括属于指定类的所有元
素(因为可能有多个元素属于同一个类)。还有一个返回元素集合的方法——getElementsBy-TagName,它返回与指定标签名匹配的所有元素。
处理DOM时,确保代码在网页完全加载后再执行至关重要;否则,代码执行时,DOM很可能还未创建。
来看看到底出了什么问题:我们将代码放在了网页的元素中,因此它执行时浏览器尚未读取网页的其他部分。这是个大问题,因为此时还没有id为greenplanet的段落元素。
这将导致什么样的结果呢?调用getElementById时,将返回null而不是我们希望的元素,进而导致错误。但浏览器沉得住气,对此置若罔闻,依然对网页进行渲染,只是没有修改绿色星球的内容。如何修复这种问题呢?可将代码移到元素的末尾,但有一种更简单的办法可确保代码在正确的时候运行,它让浏览器在加载网页并创建DOM后再
执行代码。下面就来看看如何做。
除了将代码移到元素末尾外,还有一种办法,那就通过代码(有些人认为这种办法更为干净利落)。
具体做法如下。首先,创建一个函数,在其中包含要在网页加载完毕后执行的代码。然后,将这个函数赋给对象window的属性onload。这有何作用呢?对象window将调用你赋给其属性onload的函数,但仅在网页加载完毕后才这样做。
回调函数(事件处理函数)
假设有重大的事件(如网页加载完毕)即将发生,而你必须在其发生后第一时间获悉。对于这种情形,一种常见的处理方式是使用回调函数[callback,也叫事件处理程序(event handler)]。
回调函数的工作原理如下:给了解事件的对象提供一个函数;事件发生后,这个对象将通过调用这个函数来通知你。在JavaScript中,对很多事件都采用了这种处理模式。
知道如何获取DOM中的元素后,咱们来使用代码设置元素的特性。
使用setAttribute设置特性
获取特性
getAttribute,可调用它来获取HTML元素的特性的值。
var scoop = document.getElementById("raspberry");//使用getElementById获取一个指向元素的引用,
//再使用方法getAttribute获取特性的值。
var altText = scoop.getAttribute("alt");//传入要获取其值的特性的名称。
console.log("I can't see the image in the console,");
console.log(" but I'm told it looks like: " + altText);
调用getElementById时指定的id在DOM中不存在,将返回null。getAttribute亦如此。如果指定的特性不存在,将返回null。下面演示了如何检查这种情况:
试图获取元素时,也务必检查返回的是否是null。
系统的讨论类型
undifined
在任何不确定的情况下——使用未初始化的变量、访问不存在(或已删除)的属性、使用不存在的数组元素时——你都将遭遇undefined。
undefined到底是什么?它其实并不复杂。你可以这么认为:对于任he还没有值(即还未初始化)的东西,都会将undefined赋给它。
这样做有什么好处呢?undefined让你能够判断是否给变量(属性或数组元素)赋值了
null
在很多语言中,都有一个表示“无对象”的概念,这挺好。就拿方法document.getElementById来说吧,是不是要求它返回一个对象?如果它无法返回一个对象呢?在这种情况下,我们希望返回一个含义如下的值:要是有对象,我就会是一个对象,可当前没有。这正是null的含义你还可直接将变量设置为null:
var killerObjectSomeday = null;
将变量设置为null是什么意思呢?意思是说我原本要将一个对象赋给这个变量,但现在没有这样的对象。
你可能挠着头说,为何不使用undefined来表达这种意思呢?很多人都这么想。原因是JavaScript刚面世时是这么想的:用一个值表示变量还未初始化,用另一个值表示没有对象可赋给变量。这并不完美,而且显得有些多余,但现实情况就是如此。你只需牢记undefined和null各自的用途,并知道下面一点就行了:
在应该提供一个对象,但无法创建或找到时,将提供null;在变量未初始化、对象没有指定属性或数组没有指定元素时,将返回undefined。
var test10 = null;
console.log(typeof test10);
//返回object
如何使用null
很多函数和方法都返回对象
,你需要确认获得的是货真价实的对象而不是null,以防函数无法找到或创建要返回的对象。
返回null未必意味着出了问题。这可能只是意味着有什么东西不存在,需要创建它或可以忽略它。
假设你的网站有一个天气小部件,用户可以打开它,也可以关闭它。如果用户打开了它,就会有一个id为weatherDiv的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6412bff667558c833aa3daedd2898ee5.png)
null用于表示对象不存在
NaN
nan和任何东西都不相等
处理NaN
返回NaN,但是类型是number
NaN是一个糟糕的名称;与其称之为“不是数字”,还不如称之为“无法表示的数字”(必须承认,这样首字母缩写将不那么出色)。如果你这样想,就可认为NaN是一个数字,只是无法表示而已(至少对计算机来说如此)。
问:
为何NaN与它自己不相等?
答:
如果你对这一点很感兴趣,可参阅IEEE浮点数规范。对于这个问题,简单的答案是,因为NaN指的是无法表示的数字,但并非所有无法表示的数字都相同。例如,sqrt(-1)和sqrt(-2)的结果都是NaN,但它们显然不相同。
问 :
0 / 0 的 结 果 为 N a N , 而 1 0 / 0 的 结 果 为Infinity。Infinity与NaN不是一回事吗?
答:
好眼力。在JavaScript中,Infinity指的是任何超过浮点数上限1.7976931348623157E+10308(对-Infinity而言,是指超过浮点数下限-1.7976931348623157E+10308)的值。Infinity的类型为数字,怀疑某个值太大时,可检查它是否为Infinity:
if (tamale == Infinity) {
alert("That's a big tamale!");
}
将Infinity与它自己相减时,结果为NaN;
问:
既然说到细节,null的类型是什么呢?
答: 要迅速获得答案,可对null使用运算符typeof。结果将为 object 。这合情合理,因为null用于表示不存在的对象。不过这存在很大的争议,在最新的规范中,null的类型为null。你可能发现,浏览器的JavaScript实现没有遵守这种规范,但很少需要在代码中确定null的类型。
理解相等运算符
如果两个值的类型相同,就直接进行比较
如果两个值的类型不同,则尝试将它们转换为相同的类型,再进行比较
假设你要对两个类型不同的值进行比较,如一个数字和一个字符串,JavaScript将把字符串转换为数字,再进行比较,如下所示:
相等运算符如何转换操作数
情形1:比较数字和字符串
比较字符串和数字时,都将把字符串转换为数字,再对两个数字进行比较。这并非总是可行,因为并非所有的字符串都可转换为数字。字符串不能转换为数字时,结果将如何呢?
情形2:比较布尔值和其他类型
将把布尔值转换为数字,再进行比较。这看起来有点怪,但只要记住
true将被转换为1,而false将被转换为0,就容易理解了。另外,你必须明白,在有些
情况下,需要做多次类型转换。
情形3:比较null和undefined
这两个值的比较结果为true。这好像有点怪,但规则就是这样的。你可以这么理解,它们其实表示的都是“没有值”(没有值的变量和没有值的对象),因此认为它们相等。
严格相等
相等运算符有两个,而不是只有一个。我们已经介绍了== (相等),另一个是===(严格相等)。
当且仅当两个值的类型和值都相同时,它们才是严格相等的
如果两个值的类型相同,就对其进行比较;否则,不管它们是什么,结果都为false——不进行转换,也不考虑各种复杂的规则。你只需记住,仅当两个值的类型和值都相同时,===才认为它们相等
问:
有运算符!== 吗?
答: 有。 === 比== 严格,同样! == 也比!=严格。有关=== 的规则也适用于!==,只是它检查是否不等,而非是否相等。
问:
使用<和>比较布尔值和数字(如0 < true)时,也会使用同样的规则吗?
答:
没错。就这个例子而言,将把true转换为1;因为0小于1,所以结果为true。
问:
两个字符串相等好理解,但如何确定两个字符串谁大谁小呢?
答:
问得好。说"banana"<"mango"是什么意思呢?比较两个字符串时,根据字母排列顺序来确定谁大谁小。于"banana"以b打头,"mango"以m打头,而b在字母表中排在m前面,"banana"比"mango"小。"mango"比"melon"小,因为虽然它们的第一个字母相同,但比较第二个字母时,发现a排在e的前面。
然而,这种按字母排列顺序进行比较的方式可能让你犯错。例如,“Mango” < "mango"的结果为true,但你可能以为M比m大,因为前者是大写的。字符串的排列顺序取决于计算机中用于表示各个字符的Unicode值的排列顺序(Unicode是一种以数字方式表示字符的标准),而这种排列顺序并非总是与你的预期一致。想了解有关Unicode的详细信息,请使用关键字Unicode进行搜索。但在大多数情况下,你只需知道基本的字母排列顺序,就能判断两个字符串谁大谁小。
深入探讨类型转换
问:
要让JavaScript将字符串转换为数字,并与另一个数字相加,该如何办呢?
答:
有一个将字符串转换为数字的函数,名为Number没错,其中的N就是大写的)。可像下面这样使用它:
var num = 3 + Number("4");
这条语句导致变量num的值为7。函数Number接受一个实参,并将其转换为数字。如果指定的实参无法转换为数
字,Number将返回NaN。
如何判断两个对象是否相等
检查两个对象变量是否相等时,比较的是指向对象的引用
对象变量存储的是指向对象的引用
仅当两个引用指向的是同一个对象时,它们才相等
真值和假值
再次重申,要知道哪些值是真值,哪些值是假值,只需知道哪
些值是假值,余下的其他所有值就都是真值。
在JavaScript中,假值有5个:
undefined
null
0
空字符串
NaN
因此,前一页的每个条件测试都为false。前面说过,除假值外的其他值都是真值(当然,不包括false)。下面是一些真值示例:
字符串
字符串是如何表现得既像基本类型又像对象的呢?因为JavaScript提供了这样的支持。
换句话说,在JavaScript中,可创建作为基本类型的字符串,也可创建作为对象的字符串(支持大量的字符串操作方法)。本书前面从未谈及如何创建作为对象的字符串;在大多数情况下,你无需显式地这样做,因为JavaScript解释器会在需要时替你创建字符串对象。
字符串方法和属性
属性length
属性length指出了字符串包含多少个字符,为迭代字符串中的字符提供了极大的便利。
方法charAt
方法charAt将一个整数作为参数(这个参数必须在0和字符串长度减1之间),并返回一个字符串,其中包含指定位置处的字符。可以认为字符串有点像数组,其中每个字符都有对应的索引,而索引从0开始(与数组一样)。如果指定的索引大于或等于字符串的长度,这个方法将返回一个空字符串。
方法indexOf
这个方法将一个字符串作为参数,并在字符串中该参数首次出现的位置返回该参数中第一个字符的索引
方法substring
方法substring将两个索引作为参数,提取并返回这两个索引之间的子串
方法split
方法split将一个用作分隔符的字符作为参数,并根据这个分隔符将字符串分成多个部分。
function Duck(sound) {
this.sound = sound;
this.quack = function() {console.log(this.sound);}
}
var toy = new Duck("quack quack");
toy.quack();
console.log(typeof toy);
console.log(toy instanceof Duck);
事件处理
不可以加小括号
以响应事件的方式组织代码是另一种代码编写方式。要以这种方式编写代码,需要考虑可能发生的事件以及代码应如何响应这些事件。在计算机科学中,通常说这种代码是异步(asynchronous的,因为我们编写的代码仅在相应的
事件发生时才会被调用。这种编码方式也改变了我们看待问题的角度,不再逐步地编写代码来实现算法,而是将处理各种事件的众多处理程序整合起来,构成一个应用程序。
要将showAnswer用于所有图像,需要做如下两项工作。
将函数showAnswer指定为网页中每幅图像的单击事件处理程序。
修改函数showAnswer,使其能够显示任何图像的清晰版,而不仅仅是zero.jpg。
一次性获取所有图像,再通过迭代来设置每幅图像的单击事件处理程序。为此,我们将使用一个以前没有介绍过的DOM方法:d oc u m e nt.getElementsByTagName。这个方法将一个标签名(如img、p或div)作为参数,并返回一个列表,其中包含所有匹配的元素。下面就来使用它:
问:
你说过,getElementsByTag-Name返回一个列表。这指的是数组吗?
答:
实际上返回的是一个NodeList对 象 , 但 你 可 像 处 理 数 组 一 样 处理它。NodeList是一个Node集合,而Node指的其实就是DOM树中的element对象。你可以像数组一样迭代这种集合:使用length属性来获取其长度,再通过用方括号括起的索引来访问NodeList的每个项目。然而,数组和NodeList的相似之处仅此而已,因此处理NodeList对象时必须小心。
除非需要在DOM中添加或删除元素,否则不必对NodeList有更深入的了解。