JavaScript简介
待补充
第二章 在HTML中使用JavaScript
2.1 script元素
.js扩展名可以不带,因为浏览器不会检查包含JavaScript的文件的扩展名,但是服务器通常还是需要看扩展名决定为响应应用哪种MIME类型,如果不适用.js扩展名,需要确保服务器能返回正确的MIME类型
<script>
元素会在页面中按照出现的顺序依次解析。文档中<head>
元素中最好不要放大量的js文件,在<head>
中放大量的js文件,会导致浏览器在呈现页面时出现明显的延迟,而延迟期间的浏览器窗口是空白的(浏览器遇到body元素才开始呈现内容)。为了避免这个问题,现在的web应用程序一般把全部的JavaScript引用放到<body>
元素中页面的内容后面。
延迟脚本
在<script>
元素中定义defer属性,效果是立即下载js文件,但是延迟执行。延迟脚本一定会按照顺序执行,因此最好只引入一个延迟脚本。defer
属性只适用于外部脚本文件,对嵌入脚本的defer
属性,html5会忽略。
异步脚本
指定外部脚本的async
属性,和defer
相似,但是标记为async
的外部脚本并不按照前后顺序执行,因此要确保两个指定了async
属性的外部脚本互不依赖。异步脚本不要再加载期间修改DOM元素
XHTML中的用法
主要是一些特殊字符需要使用字符实体表示
2.2 嵌入代码和外部文件
可维护性、可缓存、适应未来
2.3 文档模式
混杂模式和标准模式
2.4 noscript元素
包含在<noscript>
元素中的内容只有在两种情况下可以显示出来:浏览器不支持脚本;浏览器支持脚本,但脚本被禁用。
第三章 基本概念
3.1 语法
3.2 关键字和保留字
3.3 变量
在函数中使用var
定义局部变量
在函数中不使用var关键字直接定义的是全局变量(不推荐这样做)
严格模式下给未经声明的变量赋值将会抛出ReferenceError错误
可以使用一条语句声明多个变量:
var bianliang1 = "1",
bianliang2 = "2",
bianliang3 = 3;
只要使用逗号分隔开即可。
3.4 数据类型
5种简单基本类型:undefined、null、boolean、number和String
1种复杂类型:Object(Object本质上由一组无序的名值对组成)
不支持自定义数据类型
typeof
操作符 检测给定变量的数据类型(还可以是数值字面量)
typeof (null);
返回”Object”
从技术角度讲,函数是对象而不是一种数据类型,可以使用typeof
区分出函数和对象。
Undefined
只有一个值undefined
,它是使用了var
关键字声明了某个变量但是并未对其进行初始化。
注意:使用typeof
检测未初始化和未声明的变量时,都会返回undefined
。这在技术角度上逻辑是合理的,因为这两种变量都不能执行真正的操作。
null
表示一个空对象指针
实际上,undefined的值是派生自null值的,因此使用”==”检测时会返回true
alert(null == undefined); //true
Boolean
Boolean类型的值只有两个:true
和false
, 区分大小写
空字符串(”“)会再进行Boolean运算时转换为false:
var message = "";
if(message) {xxx} //条件是false,所以xxx不会执行
Number
前导零表示八进制数,当变量中出现了8或者大于8的数字,则忽略前导零,解析为十进制数字。(八进制字面量再严格模式下是无效的,会导致JavaScript引擎抛出错误)
十六进制前面必须是0x
保存浮点数所需的内存空间是整数的两倍。小数点后没有数字或者小数点后都为0则解析为整数
浮点数值的最高精度是17位小数
浮点数值计算产生的舍入误差:0.1+0.2 != 0.3
数值范围
负无穷和正无穷
isFinite()
函数可以确定某个Number类型的数值是否在最大和最小数值之间
Nan:非数值,是一个特殊的数值,用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。在其他语言中,任何数值除以0都会导致错误,代码停止执行,但是在ECMAScript中,会返回Nan
。
任何设计Nan的操作都返回Nan,Nan和任何值都不相等,包括Nan本身:
alert(Nan == Nan); //false
isNan()
函数:任何可以被转换成数值的值都返回false,只有不能被转换为数值的值会返回true
数值转换的三个函数:Number()
、parseInt()
、parseFloat()
3.5 操作符
相等操作符:
相等和不相等:进行强制转换后比较两个操作数
全等和不全等:不进行强制转换,直接比较两个操作数 ===
逗号操作符:
var num = (0,1,2,3,4,5); //num为5
逗号操作符用于赋值时,返回表达式最后一项。
3.6 语句
for-in语句
用来枚举对象的属性:
for(var property in window) {
document.write(property);
}
上面的示例使用了for-in循环显示了BOM中window
对象的所有属性。
ECMAScript在迭代的对象的变量值为Nan
和undefined
时,不会抛出错误,而只是不执行循环体。
label语句
定义一个标签,和break
、continue
语句一起使用。一般使用在循环嵌套下。
一般情况下单独使用break
或者continue
只能退出当前循环体,使用了label
则可以指定退出到label
所在的地方(一般是退出多层循环)。
with语句
了解即可,大量使用会导致性能下降和调试困难,不建议使用。严格模式下还会报错……
switch语句
和其他语言的switch语句相比,JavaScript的switch语句没有类型限制,甚至可以使用表达式。
注意:switch语句在比较时使用的是 全等 ,不进行类型转换。
3.7 函数
函数通过函数名调用,后边跟着一对圆括号和参数列表。
由于JavaScript没有严格的数据类型,所以函数的参数不必用var关键字声明。
函数在定义时不必指定返回值,这一点和java不同,java即使没有返回值也要指定为void
。但是在函数内部可以使用return返回任意值。
未指定函数返回值时,实际上函数返回了一个undefine
d值。
严格模式下,函数名有一些限制,不能是eval
和arguments
理解参数
ECMAScript不介意函数传递进来多少参数,也不在乎参数是什么数据类型,因为参数在内部是通过一个arguments
数组实现的,在函数体内可以通过arguments
对象访问参数数组。其实,arguments
只是和数组类似而不是数组的一个实例。
关于arguments
对象的行为,有一点很有意思,就是它的值永远和对应的参数值相同(保持同步)。这并非说明这两个值实际上存储在相同的命名空间。当对其中一个修改时,这种修改是单向的,并不影响另一个值,但是很快另一个值将会修改以保持同步。
arguments
对象的length
由传入的参数个数决定,而非函数定义时的参数个数。
最后一点:没有传递值的参数将会被赋值为undefined
值。
严格模式下对arguments
做出了一些限制,arguments
对象中的某个值不可以被重写,否则会报错;修改了参数值后,对应的arguments
中的值不会再保持同步
注意:ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。
没有重载
ECMAScript中没有方法签名(接受参数的类型和数量),所以不可能完成真正的重载。
但是通过检测传入参数的数量和数据类型,可以模仿重载。
第四章 变量、作用域和内存问题
4.1 基本类型和引用类型的值
ECMAScript变量可能有两种数据类型的值:基本类型值和引用类型值。基本类型值是简单的数据段,引用类型值是指由那些可能由多个值构成的对象。
引用类型的值是保存再内存中的对象,与其他语言(例如c语言)不同,JavaScript不允许直接访问对象在内存中的位置,也就是说不能直接操作对象的内存空间,在操作对象时,实际上操作的是对象的引用。
在很多语言中(例如Java),字符串以对象的形式来表示,因此被认为是引用,JavaScript则放弃了这一传统。
动态的属性 省去了java中定义属性和使用setter方法的烦恼……
复制变量值
对于基本类型的变量,复制变量值相当于添加了一个副本,两个变量在内存中处于不同的位置,只是值相同,操作上互不影响。
对于引用类型的变量,复制变量值也可以认为是添加了一个副本,不过这个副本中存储的不是值,而是一个指针(被复制的引用类型的变量同样存储了一个指针),指针指向存储在堆中的一个对象,被复制的变量和复制的变量实际上引用同一个对象,操作上会相互影响。
传递参数
ECMAScript中所有函数的参数都是按值传递的。ECMAScript函数中的参数都可以认为是局部变量,只在函数内有效,函数执行结束后即销毁。
再赞同不过的一句话:
很多开发人员认为,在局部作用域内修改的对象会在全局作用域中反应出来,就说明参数是按引用传递的。这种说法是错误的,在ECMAScript中,在函数内部修改了参数的值,其原始的引用仍未改变,在函数内部重写参数(引用类型的参数)的值时,这个变量(即参数)引用的就是一个局部对象了,而局部对象会在函数执行完毕后立即销毁。
关于上面这个说法,可以看看下面这个例子:
function setName(obj) {
obj.name = "Ethan";
obj = new Object();
obj.name = "小明";
}
var person = new Object();
setName(person);
alert(person.name); //"Ethan"
检测类型
typeof
操作符可以确定一个变量类型是String、number、boolean或者是undefined值。如果检测null时,则返回Object,这在技术角度也是说得通的,null代表了一个空对象指针,即变量类型为一个Object。
使用typeof
操作符检测RegExp正则表达式时,不同的浏览器有不同的结果。
通常,我们使用typeof
操作符检测基本类型的变量,检测对象的具体类型则使用instanceof
操作符。
alert(person instanceof Object); //person是Object吗
alert(colors instanceof Array); //colors是Array吗
alert(pattern instanceof RegExp); //pattern是RegExp吗
识别原理:根据对象的原型链来识别
基本类型并非对象,使用instanceof
检测基本类型,返回false
4.2执行环境及作用域
执行环境的概念
window、function
作用域链(scope chain)
延长作用域链
try……catch语句和with块
重点:
没有块级作用域
和其他类c语言不一样,ECMAScript没有块级作用域。在for循环中尤其需要注意:
for(var i=0;i<10;i++) {
xxxx……
}
对于有块级作用域的语言来说,初始化的i
变量只会存在于循环的环境当中。而在JavaScript中,创建的i
变量会一直存在,不会因为循环结束被销毁。
使用var
声明的变量会被自动的添加到最接近的环境当中。在函数内部,最接近的环境就是函数的局部环境;在with
语句中,最接近的环境也是函数环境;如果变量没有使用var
声明,则变量会被自动添加到全局环境。
4.3 垃圾收集
JavaScript的自动垃圾回收机制
方式一:标记清除(marked and sweep)
方式二:引用计数(reference counting)循环引用带来的麻烦
性能问题
管理内存
手工解除引用:一般用在全局环境变量中。解除引用(dereferencing)不代表会回收变量所占用的内存,只是切断了变量与执行环境的
联系,便于下次垃圾回收器自动运行时回收这一部分内存。
小结:栈内存中保存的是基本类型变量的值,堆内存中保存的是引用类型的值(对象)。