说明
1. 部分参考O'Reilly Media出版的JavaScript Programming_A Brain-Friendly Guide
一、基本概念与简介
JavaScript工作原理
JavaScript最初是解释型的,这意味着浏览器执行(execute)它遇到的每行JavaScript代码。浏览器厂商对现代JavaScript采取即时编译的做法,使其既可获得脚本语言的便利性,又可享受编译型语言的性能。
编译型语言如C、C++和Java在执行代码前必须进行编译(compile),编译是将代码转换为适合计算机的表达方式,通常可改善运行阶段的性能。
JavaScript的使用范围不仅仅在网页上,还用在各种应用程序(如Photoshop)以及服务器端编程中。
使用HTMl和CSS时,两者主要都是声明型的(declarative),JavaScript负责计算,利用它能在网页中添加行为。
语言 内容 作用 HTMl markup标记 statements结构 CSS rules规则 styles样式 JavaScript statements语句 computation/behavior计算和行为
如何将JavaScript代码加入网页
可在HTML文档中的任何地方使用<script>
标签添加JavaScript代码
嵌入在
<head>
里嵌入在
<body>
里- 浏览器加载网页时,先加载
<head>
元素内的所有内容,再加载<body>
元素。如果将代码放在<head>
元素中,用户可能需要等一会儿才能看到网页,而放在<body>
元素中,则用户在等待这些代码加载时,就能看到网页的内容。
- 浏览器加载网页时,先加载
代码放在独立的文件中,并在
<head>
元素中链接该文件- 用
<script>
标签的src属性来指定JavaScript文件的URL
- 用
代码放在独立的文件中,并在
<body>
元素中链接该文件<script>
元素1
2<script type="text/javascript"> alert("Hello world!");
</script>- type可以省略
1
2<script src="myJavaScript.js">
</script>- 引用独立的JavaScript文件时,
<script>
元素不能包含任何JavaScript代码
二、语法
语法基础
- 每条语句以
;
结尾 - 单行注释用
//
两个斜杠打头 - 空格不重要
- 字符串必须使用单引号或者双引号括起,而布尔值
true
和false
则不能 - JavaScript区分大小写,故变量名、关键字和函数名等几乎所有一切的大小写都很重要。
变量Variables和值values
JavaScript语句通常包含变量,变量用来存储值(如数字、字符串和布尔值等)
布尔值无需用引号括起
var关键字用来声明变量,声明变量时可以给它赋值,也可以不指定初始值。
1
2var winners = 2;
var losers;变量名的命名规则和建议
以字母、下划线或美元符号
$
开头,后接任意数量的字母,数字,下划线或美元符号$
。不能使用任何内置的关键字作为变量名。
不能包含空格和引号。
推荐使用有意义的变量名,如currentPressure和passedExam
用多个单词做变量名时通常采用骆驼式拼写法(camel case),即将每个单词的首字母大写(第一个单词除外),如:twoHeadedDragonWithFire。
通常不要用
_
和$
开头的变量名关键字keyword:是JavaScript的保留字reserved word,JavaScript将其用于特殊目的。
表达式expressions
表达式用来计算结果,根据结果的值的不同可以分为数值表达式,字符串表达式,布尔表达式、数组表达式、对象表达式和函数表达式。
1
2
3
4
5数值表达式 var total = price - (price * (discount / 100));
字符串表达式 "Dear " + "Reader" + ","
布尔表达式 cost >= 3.99
循环
while循环
(scoops > 0)称为条件测试(简称为条件),包含一个布尔表达式
如果条件测试为true,就执行代码块中所有的代码。如果条件测试为false,就结束循环。
有些代码的条件是一个变量,其值为字符串而不是布尔值。任何包含非空字符串的变量都被视为
true
而没有设置值的变量都被视为false
仅使用布尔变量本身时,只要该变量为true,条件测试就为true,进而执行相应的代码块。
if (inStock){...}
如果inStock为false,条件测试将失败
代码块即用花括号
{}
括起来的一组语句,代码块中的所有语句被视为一个整体,要么都执行,要么都不执行。1
2
3
4while(scoops > 0){
document.write("Another scoop!");
scoops = scoops - 1;
}
for循环
- for循环以关键字for打头
- 圆括号中有三部分
- 第一部分是初始化循环变量,初始化只在循环开始前执行一次
- 第二部分是条件测试,每次循环时都将执行该测试,如果为false就结束循环
- 第三部分将计数器加1。每次循环都这么做,这是在执行完循环体的所有语句后进行的。
for in循环
forEach循环
do while循环
- 工作原理与while几乎相同,只是先执行循环体中的语句,再检查循环条件。
if语句进行决策
在if语句中还可以添加一个或多个else if语句,以执行多重检查。
还可在最后添加一条else语句,以处理所有条件都不满足的情形。
1
2
3
4
5
6
7if (scoops >= 5) { alert("Eat faster, the ice cream is going to melt!"); }
else if (scoops < 3) { alert("Ice cream is running low!"); }
else {
alert("Still lots of ice cream left, come and get it.");
}
与用户交互的常见方式
创建提醒框
- 调用函数alert()创建包含消息的提醒框
- 仅当你要停止一切并提醒用户时,才应使用它
直接写入文档
- 调用函数document.write()将任何HTML和内容写入网页
使用控制台console
- 调用函数console.log(),并传入要写入的字符串
- 可将console.log()视为杰出的故障排除工具
- console实际是一个对象,而log则是console的一个方法
- 仅用于在开发网页期间调试代码troubleshoot,用户可能根本看不到控制台
- 可将控制台视为具有特定功能的对象,其功能之一是写入日志,其语法为console.log,并将用圆括号括起的输出传递给它。
- 如何打开控制台?
直接操作文档
- 利用文档对象模型(document object model,DOM)操作文档
获取用户输入
- 函数prompt()用于获取用户输入,它会弹出一个简单的对话框,包含传入该函数的字符串参数并且返回用户输入的字符串值。如果用户取消了对话框或没有输入任何响应,返回的将是null(表示没有值)。
显示输出
- 函数alert()
三、函数
常用函数
Math.random()用来生成[0, 1)的随机数
只要调用函数,都需要使用括号()
Math.floor()用来对一个小数取整(直接舍弃小数位),即向下化整
函数语法
定义函数/声明函数
1
2
3function functionname(parameter1,parameter2) {
这里是要执行的代码
}- 以关键词function打头,接下来指定函数名。函数名的命名规则与变量命名规则一样。
- parameters为函数的形参,将其放在函数名后面的括号内,将在调用函数时,给形参赋值
{}
之间的内容被称为函数体(the body of the function)
调用带参数的函数
1
myFunction(argument1,argument2)
- argument为实参,调用函数时,传给函数的实参的值被赋给相应的形参
- 可以通过实参传递任何JavaScript值,如字符串、布尔值、数字、变量甚至表达式(对于表达式,先计算它的值,再将值传递给函数)。
函数表达式(statement)
没有函数名称,且位于一条赋值语句的右边,被赋给一个变量,是一个指向函数的引用。
1
2
3
4
5
6
7var fly = function(num) {
for (var i = 0; i < num; i++) {
console.log("Flying!");
}
};
fly(3);
# 通过变量fly来调用函数
函数声明和函数表达式之间的区别
- 在分析(parses) 网页期间(执行任何代码之前),浏览器查找函数声明,找到函数声明时,浏览器创建相应的函数,并将得到的函数引用赋给与函数同名的变量。在处理完所有的函数声明后,浏览器回到代码开头,开始按从头到尾的顺序执行代码。换言之,使用函数声明时,函数将在执行代码前创建,而使用函数表达式时,函数将在运行阶段执行代码时创建。
- 函数声明是完整的语句,而函数表达式只是语句的一部分。可以认为函数声明包含一条隐藏的赋值语句,这条语句将函数引用赋给一个变量。
- 函数表达式返回一个引用,该引用指向函数表达式创建的函数。
按值传递实参(pass-by-value)
- 即把每个实参的值复制给形参,其意义在于,在函数中修改形参的值时,只会影响形参,而不会影响传递给函数的变量。
- 在函数中处理形参时,其影响范围将限制在函数中。
错误地调用函数
- 传入的实参不够:将没有相应实参的形参设置为未定义undefined
- 传入的实参太多:将忽略多余的实参
函数可返回值return
- 函数体中遇到return语句,函数会直接返回return语句中的值,并停止函数运行,哪怕return语句之后还有语句。
- 如果return语句没有指定值(空的),则返回undefined。
- 如果一个函数没有return语句,则它的返回值是undefined。
作用域(scope)
- 全局变量
- 在函数外使用关键字var声明。
- 只有一个全局作用域,也就是说不同的JavaScript脚本共享同一个全局作用域。如果在网页链接到了其他的脚本,这些脚本也能够看到这些全局变量,而你也可以看到它们的全局变量。
- 全局变量的寿命和网页一样长。重新加载网页时,将销毁并重新创建所有的全局变量。
- 使用没有关键字var声明的变量,会被自动视为全局变量,即使在函数中首次使用它,亦如此。
- 局部变量
- 在函数中声明的变量叫局部变量,这样的变量只在函数中可见,也只能用在函数中
- 局部变量是在函数被调用后创建的,并一直活到函数返回(无论函数是否返回值)
- 如果局部变量使用了与全局变量相同的变量名,那么在这个局部作用域中,全局变量是不可见的。即同名局部变量遮住(shadow)了全局变量
- 将函数作为实参传递给另一个函数时,传入的函数引用将被复制到所调用函数的一个形参变量中,与其他形参一样,存储函数引用的形参也是局部变量。
- 变量提升
JavaScript引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明提升至当前作用域的顶端,然后进行接下来的处理。
预编译之后的代码逻辑是这样的:1
2
3
4
5
6
7
8
9
10var foo = 3;
function hoistVariable() {
var foo = foo || 5;
console.log(foo); // 5
}
hoistVariable();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var foo = 3;
//预编译之后
function hoistVariable() {
var foo;
foo = foo || 5;
console.log(foo); // 5
}
hoistVariable();
#只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。
#只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。
- 全局函数
- 在代码顶层定义的函数
- 局部函数
- 在函数中定义的函数(nested,嵌套)
- 在函数内部,如果你使用函数声明创建了一个嵌套函数,那么在这个函数的函数体的任何地方,嵌套函数都是已定义的;
- 如果使用函数表达式创建了一个嵌套函数,则在这个函数的函数体内,仅当函数表达式执行后,嵌套函数才是已定义的。
- 词法作用域(lexical scope)的工作原理
词法(lexical)意味着只需查看代码的结构就可确定变量的作用域,而不是等到代码执行时才明白。换而言之,即JavaScript的作用域规则完全基于代码的结构,而不是一些动态的运行阶段属性。
因此,对于在函数中引用的变量,要确定它是在哪里定义的,可从最里面(当前函数)开始依次向最外面进行查找,直到找到它为止。
实例
1
2
3
4
5
6
7
8
9
10
11
12
13#实例1
var justAVar = "Oh, don't you worry about it, I'm GLOBAL";
function whereAreYou() {
var justAVar = "Just an every day LOCAL";
return justAVar;
}
var result = whereAreYou();
console.log(result);
#显示Just an every day LOCAL1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#实例2
var justAVar = "Oh, don't you worry about it, I'm GLOBAL";
function whereAreYou() {
var justAVar = "Just an every day LOCAL";
function inner() {
return justAVar;
}
return inner();
}
var result = whereAreYou();
console.log(result);
#显示Just an every day LOCAL1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#实例3
var justAVar = "Oh, don't you worry about it, I'm GLOBAL";
function whereAreYou() {
var justAVar = "Just an every day LOCAL";
function inner() {
return justAVar;
}
return inner;
}
var innerFunction = whereAreYou();
var result = innerFunction();
console.log(result);
#显示Just an every day LOCAL环境(environment)存储了局部作用域内定义的所有变量。每个函数都有与之相关联的环境,其中包含它所处作用域内的局部变量。当返回这个函数时,返回的不仅仅是函数,还有与之相关联的环境。
- 每个嵌套函数都有自己的小环境,其中包含它自己的变量,这将形成一个环境链,从内到外依次为各个嵌套函数的环境。在环境中查找变量时,也需要从最近的环境着手,沿环境链不断往下查找,直到找到变量为止。如果环境链中没有找到,再在全局环境中查找。
- 可将形参视为函数的局部变量,因此形参也包含在环境(environment)中。
- JavaScript函数都是在定义它的环境中执行的。
闭包(closure)
闭包(closure) 的定义:闭包指的是函数和引用环境。
自由变量(free variables)的定义:不在本地作用域内定义的变量。
可敲定函数(closes the function)的定义:给所有自由变量都提供了值的环境。
从函数返回函数来创建闭包:计数器实例
在全局作用域中,根本看不到变量count,但每次调用doCount时,都可以使用变量count。
除了调用doCount外,没有其他任何办法能够获取变量count。
1
2
3
4
5
6
7
8
9
10
11
12
13
14function makeCounter() {
var count = 0;
function counter() {
count = count + 1;
return count; }
return counter;
}
var doCount = makeCounter();
console.log(doCount()); #显示1
console.log(doCount()); #显示2
console.log(doCount()); #显示3
将函数表达式用作实参来创建闭包
我们传递给makeTimer的函数是一个闭包,带有将自由变量
doneMessage
绑定到字符串"Cookies are done!"的环境。1
2
3
4
5
6
7
8
9function makeTimer(doneMessage, n) {
setTimeout(function() {
alert(doneMessage);
}, n);
}
makeTimer("Cookies are done!", 1000);
闭包包含的是实际环境,而非环境的副本
- 环境引用的是实时变量,如果闭包函数外面的代码修改了变量,闭包函数执行时看到的将是变量的新值
函数提升
浏览器分两遍读取网页:
- 第一遍读取所有的函数定义(由函数声明创建的函数)
- 第二遍开始执行代码
可将函数放在JavaScript脚本的任何地方。
1
2
3
4
5
6
7
8var radius = 5;
var area = circleArea(radius);
alert(area);
function circleArea(r) {
var a = Math.PI * r * r;
return a;
}
函数是一等值(first class values)
一等值(first class values)的特点(数值、字符串、布尔值、函数和对象都是一等值)
- 将其赋给变量或存储在数组和对象等数据结构中
- 将其传递给函数
- 从函数中返回它们
一等值的定义:在编程语言中,可像对待其他任何值一样对待的值,包括赋给变量、作为实参传递给函数以及从函数中返回。
向函数传递函数
只需将函数名用作实参即可
传递的是指向函数的引用,可将这种引用视为指针(pointer),它指向函数本身的内部表示。引用本身可存储在变量中,可赋给变量,还可作为实参传递给函数。
1
2
3
4
5var allCanFly = processPassengers(passengers, checkNoFlyList);
if (!allCanFly) { console.log("The plane can't take off: we have a passenger on the no-fly-list.");
}
#checkNoFlyList即传入的函数
从函数返回函数
1
2
3
4
5
6
7
8
9
10
11
12
13function createDrinkOrder(passenger) {
var orderFunction;
if (passenger.ticket === "firstclass") {
orderFunction = function() {
alert("Would you like a cocktail or wine?");
};
} else {
orderFunction = function() {
alert("Your choice is cola or water.");
}; } return orderFunction;
}
匿名函数(anonymous functions)
匿名函数是没有名称的函数表达式,用于通常需要函数引用的地方。
window.onload = function() { alert("Yeah, that page loaded!"); };
- 如果需要将一个函数作为实参,可将实参指定为函数表达式
1
2
3setTimeout(function() {
alert("Time to take the cookies out of the oven");
}, 600000); - 如果需要从函数返回一个函数,也可返回一个函数表达式
- 如果需要将一个函数作为实参,可将实参指定为函数表达式
四、数据结构
数组(Arrays)
创建数组
用数组的字面量(array literal)创建数组(即详细地指定了该数组包含的元素)
var flavors = ["vanilla", "butterscotch", "lavender", "chocolate", "cookie dough"];
- 在
[]
中依次输入每个数据,用,
隔开即可。 - 数组中每一项的数据类型可以是任意的,字符串,布尔值,数值,甚至数组,函数和对象都可以。
- 数据的个数也没有限制,但受制于计算机内存。
- 不要求一个数组中所有值的类型都相同。
- 在
访问数组元素
var flavorOfTheDay = flavors[2];
[]
中的数字表示数组的索引位置,数组的索引从0开始
修改数组元素
flavors[3] = "vanilla chocolate chip";
确定数组的长度
var numFlavors = flavors.length;
- 每个数组都有一个length属性,它的值也就是数组的长度,等于数组的最大索引值+1
给数组添加新元素
只需给指定索引处的元素赋值即可,但必须小心地指定索引,否则数组将是稀疏的(sparse)
1
2
3
4var genres = [];
genres[0] = "Rockabilly";
genres[1] = "Ambient";
var size = genres.length;稀疏数组是指有些索引处有值,而其他索引处没有值的数组。
1
2
3var sparseArray = [ ];
sparseArray[0] = true;
spraseArray[100] = true;犹如创建了99个变量,但没有对其初始化。这些变量虽然没有值,但是仍占用计算机的内存空间。
检查元素的值是否是undefined,可编写如下代码:
1
2
3if (myarray[i] == undefined) {
...
}undefined是一个值,而不是字符串,因此不能在前后加上引号。
push方法
1
2
3var genres = [];
genres.push("Rockabilly");
genres.push("Ambient");
平行数组(parallel arrays)
数组方法
sort
的工作原理根据一个知道如何对两个数组元素进行比较的函数对数组进行排序。
- 对于数字,使用
<
、>
、和==
进行比较; - 对于字符串,按字母顺序进行比较
- 对于对象,采用自定义的方式根据对象的属性进行比较
- 对于数字,使用
sort
方法要求我们的函数在第一个数字大于第二个数字时返回一个大于0的值,在它们相等时返回0,并在第一个数字小于第二个数字时返回一个小于0的值。1
2
3
4
5
6
7
8
9
10
11function compareNumbers(num1, num2) {
if (num1 > num2) {
return 1;
} else if (num1 === num2) {
return 0;
} else {
return -1;
}
}
numbersArray.sort(compareNumbers);sort
方法是破坏性的,将就地修改数组,而不是返回一个排序后的新数组。
五、对象(Objects)
创建对象
- 定义并创建对象的实例
添加对象变量声明
花括号
{}
内定义对象的所有属性(properties)每个属性的定义都包含名称、冒号和值,用逗号分隔属性名和属性值对,冒号分隔属性名和属性值。
包含空格的字符串用作属性名时,必须用括号将其括起
1
2
3
4var widget = {
cost$: 3.14,
"on sale": true
};属性名可以是任何字符,但通常遵循变量命名规则
对象变量声明也以分号结束
1
2
3
4
5
6
7
8var chevy = {
make: "Chevy",
model: "Bel Air",
year: 1957,
color: "red",
passengers: 2,
convertible: false,
mileage: 1021 };
属性的工作原理
访问属性(access a property)
句点表示法(dot notation)
fiat.mileage
串接(chaining)
var index = ship.locations.indexOf(guess);
方括号表示法(bracket notation)
chevy["color"]
chevy["co" + "lor"]
- 可将任何表达式放在方括号内,只要其结果为表示属性名的字符串即可
访问不存在的属性
if (fiat.make) { ... }
fiat.make
的结果将为undefined
修改属性
fiat.mileage = 10000;
添加新属性
fiat.needsWashing = true;
将属性用于计算
1
2
3
4
5if (fiat.year < 1965) {
classic = true;
} for (var i = 0; i < fiat.passengers; i++) {
addPersonToCar();
}增删属性
- 增加属性,只指定一个新属性并给它赋值
- 删除属性,可使用关键词delete:
delete fido.dogYears;
- 删除属性时,同时删除属性值和属性本身
- 成功删除属性,delete表达式返回true
- 当属性无法删除时,delete表达式返回false
- 如某个对象属于浏览器而受到保护
- 即便你要删除的属性在对象中不存在,delete表达式仍返回true
变量存储对象的原理
变量并不实际存储对象
变量存储指向对象的引用(reference),故此对象变量也被称为引用变量(reference variables)
引用就像指针(pointer),是对象的存储地址
当我们使用句点表示法时,JavaScript解释器将负责根据引用获取对象并访问其属性
基本类型变量(primitive variable)表示的是实际值,而对象变量(object variable)表示一种获取对象的途径
当使用句点表示法用于引用变量时,相当于使用句点前的引用来获取相应的对象,再访问句点后指定的属性。
向函数传递对象
调用函数并向它传递一个对象时,传递的是对象引用,而不是对象本身。根据按值传递的语义,传递给形参的是改引用的副本,而原来的引用依然是指向原始对象的指针。
如果在函数中修改对象属性,修改的将是原始对象的属性。因此,函数结束时,在函数中对对象所做的修改都依然有效。
给对象添加行为
给对象添加行为,就是将函数定义赋给属性。在函数定义中没有指定函数名,而只是在关键词后面提供了函数体,属性名就是函数名。
1
2
3
4
5
6
7
8
9
10
11
12var fiat = {
make: "Fiat",
model: "500",
year: 1957,
color: "Medium Blue",
passengers: 2,
convertible: false,
mileage: 88000,
drive: function() {
alert.log("Zoom zoom!");
}
};对象内的函数,通常被称为方法(method)。
用句点法(串联,chaining)调用对象内的方法。
fiat.drive();
var index = ship.locations.indexOf(guess);
关键词this
的工作原理
- 可将
this
视为一个变量,指向其方法被调用的对象。
如何知道对象包含哪些属性
- 用
for in
迭代对象的属性for in
以每次一个的方式遍历对象的属性,并依次将每个属性赋给变量prop- 用方括号表示法通过prop来访问当前属性
1
2
3for (var prop in chevy) {
console.log(prop + ": " + chevy[prop]);
}
JavaScript提供的内置对象
字符串对象
- 属性
length
:指出字符串包含多少个字符 - 方法
charAt
:获取字符串中指定索引处的字符。- JavaScript没有字符类型(character type),通过返回一个字符串来返回字符,其中只包含特点的字符
1
2
3
4
5
6var input = "jenny@wickedlysmart.com";
for(var i = 0; i < input.length; i++) {
if (input.charAt(i) === "@") {
console.log("There's an @ sign at index " + i);
}
} - 方法
indexOf
将一个字符串作为第一个参数,并在字符串中该参数首次出现的位置返回该参数中第一个字符的索引
第二个参数是一个索引,指定从什么位置开始查找
如果没有找到指定的字符串,将返回索引-1
1
2index = phrase.indexOf("the", 5);
console.log("there's a the sitting at index " + index);
- 方法
substring
将两个索引作为参数,提取并返回这两个索引之间的子串
var val = data.substring(5, 10);
返回从索引5到索引10(不包括)的子串
可以省略第二个参数,在这种情况下,
substring
将提取从指定索引到字符串末尾的子串。
- 方法
split
将一个用作分隔符(delimiter)的字符作为参数,并根据这个分隔符将字符串分成多个部分。
1
2
3
4
5var data = "name|phone|address";
var vals = data.split("|");
console.log("Split array is ", vals);
//Split array is ["name", "phone", "address"]
- 方法
toLowerCase
- 将字符串中的所有大写字符都转换为小写,并返回结果。
- 方法
slice
- 删除字符串的一部分,并返回结果。
- 方法
lastIndexOf
- 查找最后一个子串
- 方法
match
- 查找与正则表达式匹配的子串
- 方法
trim
- 删除字符串开头和末尾的空白字符
- 方法
replace
- 查找子串并将其替换为另一个字符串
- 方法
concat
- 将字符串拼接起来
- 方法
toUpperCase
- 将字符串中的所有小写字符都转换为大写,并返回结果。
六、浏览器与文档对象模型(document object model,DOM)
网页与JavaScript通过文档对象模型(DOM)交互
document
是一个全局对象,表示浏览器显示的整个网页,包含完整的DOM。getElementById
是document
的一个方法,查找id为指定值的元素对象。- 若根据id从DOM获取元素时,如果指定的id不存在,getElementById将返回null。
getElementsByClassName
是document
的一个方法,查找类名为指定值的元素对象集合。getElementsByTagName
是document
的一个方法,查找与指定标签名匹配的元素对象集合。
- 返回的是一个NodeList对象。但可以像处理数组一样处理它(length属性、索引访问)。NodeList是一个Node集合,而Node指的其实就是DOM树中的element对象。
innerHTML
是元素对象的一个属性,表示包含在元素中的内容(除了元素的HTML标签外)。- 可以通过
innerHTML
来读取或替代元素的内容 innerHTML
表示元素对象包含的所有内容,包括嵌套的元素/从对象的起始位置到终止位置的全部内容,不包括HTML标签。- 例如,段落元素除文本外,还可能包含
<em>
和<img>
元素
- 例如,段落元素除文本外,还可能包含
- 可以通过
outerHTML
是元素对象的一个属性,表示包含在元素对象中的全部内容1
2
3<div id="test">
<span style="color:red">test1</span> test2
</div>innerHTML
的值是<span style="color:red">test1</span>test2
outerHTML
的值是<div id="test"><span style="color:red">test1</span>test2</div>
处理DOM时,要确保代码在网页完全加载后,再执行,否则代码执行时,DOM很可能尚未创建。
网页加载完毕后执行代码的方法
- 将代码放置于
<body>
元素末尾 - 将函数赋给对象window的属性onload
window是一个JavaScript内置对象,表示浏览器窗口
网页加载事件的触发时间点是:浏览器加载完网页,显示网页的所有内容并生成表示网页的DOM
1
2
3
4
5
6<script> function init() {
var planet = document.getElementById("greenplanet");
planet.innerHTML = "Red Alert: hit by phaser fire!";
}
window.onload = init; #千万不要在函数名init后面添加括号,这里不是调用函数,而是将函数作为一个值赋给属性window.onload </script>
设置DOM中元素对象的特性
setAttribute
方法添加新特性或修改既有特性planet.setAttribute("class", "redtext");
getAttribute
方法获取HTML元素的特性的值如果指定的特性不存在,将返回null。
1
2
3
4
5
6
7
8var scoop = document.getElementById("raspberry");
var altText = scoop.getAttribute("alt");
if (altText == null) {
console.log("Oh, I guess there isn't an alt attribute.");
} else {
console.log("I can't see the image in the console,");
console.log(" but I'm told it looks like " + altText);
}设置图像的属性(property)
src
与使用getAttribute
和setAttribute
设置特性(attribute)src
相同么?- 对于
<img>
元素而言,效果是一样的。但是并非所有的HTML特性(attributes)都有对应的对象属性(object properties),因此对于这些特性,必须使用getAttribute
和setAttribute
来设置和获取。
- 对于
七、类型(types)
类型的种类
- 基本类型(primitives)
- 数值(作为基本类型)
- 布尔值(作为基本类型)
- 字符串(作为基本类型)
null
undefined
- 对象(objects对象有属性和方法)
- 字符串(作为对象/JavaScript解释器会在需要时替你创建字符串对象,无需显式地这么做)
- 数值(作为对象)
- 布尔值(作为对象)
undefined
值
对于任何还没有值(即还未初始化)的东西,都将被赋予值
undefined
,undefined
的类型是undefined
。- 没有return语句的函数返回的值
- 未赋值的变量的值
- 稀疏数组中不存在的数组元素的值
- 不存在的属性的值
- 已删除的属性的值
可以通过
undefined
来判断是否给变量(属性或数组元素)赋值了。###1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var x;
if (x == undefined) {
// x isn’t defined! just deal with it!
}
var customer = {
name: "Jenny"
};
if (customer.phoneNumber == undefined) {
// get the customer's phone number
}
#注意不要将值undefined与字符串"undefined"混淆null
值表示”无对象/对象不存在“,在应该提供一个对象,但无法创建或找到时,将提供null,在变量未初始化,对象没有指定属性或数组没有指定元素时,将返回undefined。
null
以前的类型是object
,不过在最新的规范中null
的类型是null
。
NaN
值
表示JavaScript无法表示的数值结果,即(Not a Number)
1
2
3var a = 0/0;
var b = "food" * 1000;
var c = Math.sqrt(-9);JavaScript中唯一一个与自己不相等的值:
NaN != NaN
* 因为NaN与任何东西都不相等,因此不能以任何方式检查变量与NaN是否相等。需要使用特殊函数isNaN。 * NaN指的是无法表示的数字,但并非所有无法表示的数字都相等。 * 如果变量存储的是NaN或其他任何不是数字的值,将其传递给isNaN时,都将返回true
,否则将返回false
。1
2
3if (isNaN(myNum)) {
myNum = 0;
}NaN
值的类型是"number"NaN
值可以认为是一个数字,只是无法表示(至少对计算机来说如此而已)
Infinity
值
- 任何超过浮点数上限(1.7976931348623157E+10308)或下限(-1.7976931348623157E+10308,-Infinity)的值。
Infinity
值的类型是"number"Infinity
与它自己相减时,结果为NaN
检测类型
typeof
是一个内置的JavaScript运算符(operator),可用于探测其操作数(operand)的类型操作数:要对其进行操作的东西
1
2
3var subject = "Just a string";
var probe = typeof subject;
console.log(probe);typeof
使用字符串来表示类型- "string"
- "boolean"
- "number"
- "object"
- "undefined"
- "function"
类型转换
- 拼接和加法运算符
+
用于数字时,运算符
+
表示加法运算,用于字符串时,运算符+
表示拼接(concatenation)将数字和字符串相加时,数字转换为字符串,再进行拼接,顺序为从左至右
1
2var order = 1 + 2 + " pizzas";
3 pizza布尔值与字符串相加时,结果为字符串。
true + “ love”
为“true love”
- 其他算数运算符
- 如乘法、除法和减法运算,JavaScript将认为你要执行的是算术运算,而不是字符串运算。
- 运算符
-
对数字取负,将其用于true
,结果将为-1
- 将字符串转换为数字的函数
Number
- 如果指定的实参无法转换为数字,Number将返回NaN。
八、运算符
算术运算符
- 后递增运算符(post-increment operator):
++
myImportantCounter++;
这条语句执行完以后,myImportantCounter的值比原来大1。
- 后递减运算符(post-decrement operator):
--
###
###
###
###
九、比较
比较运算符
<
- 可以比较布尔值、字符串和数字,
true
转换为1 - 字符串比较规则
- 取决于计算机中用于表示各个字符的Unicode值的排列顺序
- 可以比较布尔值、字符串和数字,
>
- 可以比较布尔值、字符串和数字,
true
转换为1
- 可以比较布尔值、字符串和数字,
==
表示等于
- 如果两个值的类型相等,就直接进行比较
- 如果两个值的类型不同,则尝试将它们转换为相同的类型,再进行比较
比较数字和字符串
把字符串转换为数字,再对两个数字进行比较
字符串不能转换为数字时,结果为
NaN
,和数字的比较结果为false
99 == "vanilla"
→99 == NaN
→false
比较布尔值和其他类型
把布尔值转换为数字,再对两个数字进行比较
true
将被转换为1,而false
将被转换为01 == true
→1 == 1
→true
比较
null
和undefined
undefined == null
→true
其他情况
空字符串转换为数字的结果是0
1 == ""
→1 == 0
→false
判断两个对象是否相等
如果两个操作数都是对象,
==
和===
都可以使用,工作原理完全相同检查两个对象变量是否相等时,比较的是指向对象的引用
- 这些对象包含什么不重要,只要两个引用指向的对象不同,他们就不同
当且仅当两个引用指向的是同一个对象时,它们才相等
===
表示正好等于/严格相等- 当且仅当两个值的类型和值都相等时,它们才是严格相等。
- 有些开发人员称其为等同运算符
“identity” operator
<=
- 只能比较字符串和数字
>=
- 只能比较字符串和数字
!=
表示不等于!==
表示严格不等于
逻辑运算符
||
表示OR(或)&&
表示AND(与)!
表示NOT(非),仅当表达式为false时,结果才为true
真值(truthy)和假值(falsey/falsy)
定义:有些值并非
true
或false
,但用于条件表达式中时,被视为true
或false
。这些值被称为真值(truthy)和假值(falsey/falsy)。因为严格地说它们并非true
或false
,但用于条件表达式时,它们的行为像true
或false
。假值(falsey/falsy)
undefined
null
0
- 空字符串
NaN
真值(truthy)
除了5个假值以外,其他都是真值
十、事件处理程序(event handler)/回调函数(callback)及异步编码(asynchronous coding)
基本概念
- 有些事件(events)是浏览器生成的,如加载。有些事件是用户与网页交互时生成的。有些事件是JavaScript代码生成的。还有定时器事件。
- 事件处理程序(event handler)/回调函数(callback)/监听器(listener)。从代码的角度说,就是一个函数。
- 异步编码(asynchronous coding),是一种以响应事件的方式组织代码的代码编写方式。 > 相对应的线性编码,就是从上到下的顺序逐步编写代码。
事件对象(event object)的工作原理
每次调用单击事件处理程序时,都将向它传递一个事件对象(event object),更准确地说是触发事件的元素,这被称为目标(target)。例如,当用户单击特定的图像时,目标就是这幅图像的元素对象。加载事件处理程序传递的一个事件对象的
target
是window
对象。target
就是事件对象的一个属性(property)
在事件处理程序中,你可以使用这个事件对象来获取有关事件的信息
- 常规信息
属性
target
,它储存了一个引用,指向触发事件的元素对象(可以是各种不同的对象,但通常是元素对象)1
2
3function showAnswer(eventObj) {
var image = eventObj.target;
}属性
type
,是一个字符串,如click
或load
,指出了发生的是哪种事件属性
timeStamp
,事件是何时发生的
- 具体信息(取决于事件的类型)
- 用户单击鼠标时,获得单击位置的坐标
- 属性
keyCode
,用户刚按下了哪个键 - 属性
clientX
和clientY
,确定用户点击的位置离浏览器窗口的左边缘和上边缘的距离 - 属性
touches
,在触摸设备上,确定用户使用了多少根手指来触摸屏幕
- 常规信息
浏览器把事件都放入队列,再遍历这个队列(queue),并在必要时调用相应的处理程序。浏览器只有一个队列和一个控制线程(thread of control),只能去逐个处理事件。
具体示例
将按钮(类型为button)的属性onclick设置为一个事件处理程序
可以给网页中任何元素的onclick属性设置为一个函数。
不会在网页中显示出来的元素(如
<script>
和<head>
)不支持单击等事件1
2
3
4
5
6
7
8
9
10function init() {
var fireButton = document.getElementById("fireButton");
fireButton.onclick = handleFireButton;
}
function handleFireButton() {
// 从表单中获取值的代码
}
window.onload = init;- 通过迭代批量增加单击事件处理程序
1
2
3
4
5
6function init() {
var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
images[i].onclick = showAnswer;
}
};
处理回车键事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function init() {
var fireButton = document.getElementById("fireButton");
fireButton.onclick = handleFireButton;
var guessInput = document.getElementById("guessInput");
guessInput.onkeypress = handleKeyPress;
# 按键
}
function handleKeyPress(e) {
var fireButton = document.getElementById("fireButton");
if (e.keyCode === 13) {
fireButton.click();
return false;
}
}
# 如果用户按下的是回车键,事件对象的属性keyCode将为13mousemove事件
- 当鼠标在特定元素上移动时,
mousemove事件通知相应的处理程序。要指定处理程序,可使用元素的属性
onmousemove
。这样会给这种事件处理程序传递一个event
对象。 clientX, clientY
:鼠标相对于浏览器窗口左边缘和上边缘的距离,单位为像素screenX, screenY
:鼠标相对于设备屏幕左边缘和上边缘的距离,单位为像素pageX, pageY
: 鼠标相对于网页左边缘和上边缘的距离,单位为像素
- 当鼠标在特定元素上移动时,
mousemove事件通知相应的处理程序。要指定处理程序,可使用元素的属性
鼠标移动事件
- 属性
onmouseover
:鼠标移入元素 - 属性
onmouseout
:鼠标移出元素
- 属性
其他
resize
:每当用户调整浏览器窗口大小时,都将触发这个事件
play
:单击网页中<video>
元素的播放按钮时,将触发这个事件
pause
:单击网页中<video>
元素的暂停按钮时,将触发这个事件unload
:用户关闭浏览器窗口或切换到其他网页时,将触发这个事件
dragstart
:用户拖曳网页中的元素时,将触发这个事件drop
:用户放下拖曳的元素时,将触发这个事件touchstart
:在触摸设备上,用户触摸并按住元素时,将触发这个事件touchend
:用户停止触摸时,将触发这个事件
基于时间的事件
- 调用函数
setTimeout
并向它传递处理程序,它接受两个参数,事件处理程序和时间间隔(单位为毫秒)。向函数
setTimeout
传递一个指向处理程序函数的引用。函数setTimeout
将这个引用存储起来,供定时器到期后使用。严格地说,应该写作
window.setTimeout
,window
是全局对象,可省略对象名。setTimeout(timerHandler, 5000);
还可以传入第三个参数:触发时间事件时,这个参数将被传递给处理程序(可向处理程序传递任意数量的参数)。
1
2
3
4
5
6
7setTimeout(reblur, 2000, image);
function reblur(image) {
var name = image.id;
name = name + "blur.jpg";
image.src = name;
}将处理程序指定为reblur,将时间间隔设置为2秒,并将第三个参数指定为要重新变模糊的图像
- 调用函数
setInterval
在定时器到期时触发事件,并重启定时器
若要停止函数
setInterval
,可将其传递给另一个函数clearInterval1
2var t = setInterval(ticker, 1000);
clearInterval(t);
- 调用函数