JavaScript高级程序设计笔记(一)
JavaScript简介
JavaScript由三部分组成:
- 核心(ECMAScript),提供核心语言功能;
- 文档对象模型(DOM),提供访问和操作网页内容的方法和接口;
- 浏览器对象模型(BOM),提供与浏览器交互的方法和接口。
script
<script>定义了下列6个属性:
- async:可选。表示应该立即下载脚本,但不应该妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。只对外部脚本文件有效。
- charet:可选。表示通过src属性指定的代码的字符集。由于大多数浏览器会忽略它的值,因此这个属性很少有人用。
- defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。IE7及更早版本对嵌入脚本也支持这个属性。
- language:已废弃。
- src:可选。表示包含要执行代码的外部文件。
- type:可选。可以看成是language的替代属性;表示编写代码使用的脚本语言的内容类型(也称为MIME类型)。其默认值为text/javascript。
使用CData片段来包含Javascript代码。这个区域中可以包含不需要解析的任意格式的文本内容。
<script type="text/javascript">
//<![CDATA[
function compare(a, b){
if (a < b)
alert("A is less than B");
} else if (a > b){
alert ("A is greater than B");
} else {
alert ("A is equal to B");
}
//]]>
</script>
使用<noscript>元素可以指定在不支持脚本的浏览器中显示的替代内容。但在启动了脚本情况下,浏览器不会显示<noscript>元素中的任何内容。
基本概念
标识符
标识符,就是指变量,函数,属性的名字,或者是函数的参数。标识符可以是按照下列格式规则组成起来的一或多个字符。
- 第一个字符必须是一个字母、下划线(_)或一个美元符号($)
- 其他字符可以是文字、下划线、美元符号或数字
标识符中的字母也可以包括扩展的ASCII或Unicode字母字符,但不推荐这么做。
不能把关键词、保留字、true、false和null用作标识符。
注释
// 单行注释
/*
* 这是一个多行
* (块级)注释
*/
变量
ECMAScript的变量是松散类型的,所谓缩松类型就是可以用来保存任何类型的数据。
省略var操作符可以定义全局变量,但是不推荐。因为在局部作用于中定义的全局变量很那尾部。
数据类型
ECMAScript中由5中简答数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String。还有一种复杂数据类型——Object,Object本质上由一组无序的名值对组成。
typeof操作符
用来检测给定变量的数据类型。
typeof是一个操作符而不是函数。
Undefined类型
Undefined类型只有一个值,即特殊的undefined。在使用var声明变量但卫队其加以初始化时,这个变量的值就是undefined。
对未初始化的变量执行typeof操作符会返回undefined值,而对未声明的变量执行typeof操作符同样会返回undefined值。
Null类型
Null类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回”object”的原因。
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值。这样一来,只要检查null值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子所示:
if (car !=null){
// 对car对象执行某些操作
}
实际上,undefined值是派生自null值的,所以他们的相等性测试要返回true:
alert(null == undefined); //true
Boolean类型
Boolean类型是ECMAScript中使用得最多的一种类型,改类型只有两个字面值:true和false。
这两个值与数字值不是一回事,因此true不一定等于1,而false也不一定等于0。
数据类型 | 转化为true的值 | 转化为false的值 |
Boolean | true | false |
String | 任何非空字符串 | ”“(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
Undefined | n/a | undefined |
Number类型
在默认情况下,ECMAScript会将那些小数点后面带有6个零以上的浮点数值转换以为e表示法表示的数值(例如0.0000003会被转换成3e-7)。
注意,永远不要测试某个特定的浮点数值。例如:
if (a + b == 0.3){
alert("You got 0.3");
}
如果测试的两个数是0.25和0.05则可以通过,但是如果是0.1和0.2,那么测试将无法通过。
NaN,即非数值是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况。
可以用isNaN()函数来帮我们确定这个参数是否“不是数值”。
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10是一个数值)
alert(isNaN("10")); //false(可以被转换为10)
alert(isNaN("blue")); //true(不能转换为数值)
alert(isNaN(true)); //false(可以被转换为1)
数值转换有3中方法:Number()、parseInt()、parseFloat()。
Number()可以用于任何数据类型,而另外两个函数则专门用于把字符串转换成数值。
parseInt()不会忽略前导零,而且能识别各种整数格式。直到找到第一个非空格字符。
不过ECMAScript3和5存在分歧。所以最好添加第二个参数。
比如:
var num1 = parseInt("10", 2); //2(按二进制解析)
var num2 = parseInt("10", 8); //8(按八进制解析)
var num3 = parseInt("10", 10); //10(按十进制解析)
var num4 = parseInt("10", 16); //16(按十六进制解析)
parseFloat()是解析到遇见一个无效的浮点数字符为止。所以第一个小数点是有效的。而且它始终忽略前导零。
String类型
转换为字符串有两种方法,一个是toString(),但null和undefined值没有这个办法。他可以传递输出数值的基数,如二进制十进制。
另一个是String(),String()函数会返回null和undefined的字面量。
Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。
Object类型所具有的的任何属性和方法也同样存在于更具体的对象中。
操作符
操作符包括算数操作符(如加号和减号)、位操作符、关系操作符和相等操作符。
一元操作符
只能操作一个值的操作符叫做一元操作符,一元操作符是ECMAScript中最简单的操作符。
- 递增和递减操作符
- 一元加和减操作符
位操作符
位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。
对于有符号的整数,32位中的前31用于表示整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。
负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列三个步骤:
- 求这个数值绝对值的二进制码。
- 求二进制反码,即将0替换为1,将1替换为0。
- 得到的二进制反码加1.
位操作符包括:
- 按位非(NOT)
- 按位或(OR)
- 按位与(AND)
- 按位异或(XOR)
- 左移
- 有符号的右移
- 无符号的右移
左移中:左移不会影响操作符的符号位。
对正数来说,无符号右移的结果与有符号右移相同。但对于负数来说不同,无符号右移操作符会把负数的二进制码当做正数的二进制码,因此会导致负数无符号右移后的结果非常之大。
布尔操作符
布尔操作符一共有3个:非(NOT),与(AND),和或(OR)。
逻辑或和逻辑与都属于短路操作符,即如果第一个操作符能够决定结果,那么就不会再对第二个操作符求值。
对于逻辑与,如果第一个操作符是false,则无论第二个操作符是什么值,结果都不可能为true。
对于逻辑或,如果第一个操作符是true,则无论第二个操作符是什么值,结果都不可能为false。
乘性操作符
ECMAScript定义了三个乘性操作符:乘法、除法和求模。
如果有一个操作数是NaN,则结果是NaN。
加性操作符
包括加法和减法两个操作符。
在加法运算中,如果只有一个操作符是字符串,则将另一个操作符转换为字符串,然后再将两个字符串拼接起来。
如:
var result2 = 5 + "5";
alert(result2); //是"55"而不是55
关系操作符
关系操作符包括:小于(<)、大于(>)、小于等于(<=)、大于等于(>=)。
大写字母的字符编码全部小于小写字母的字符编码,因此在进行字符比较的时候会出现大写字母开头的单词小于小写字母开头的单词。如果要真正按字母表顺序比较字符串,就必须把两个操作数转换为相同的大小写形式,可以用 单词.toLowCase() 方法都转为为小写形式。
还有一个奇怪的现象是”23”小于”3”,因为”2”的字符编码是50,”3”的字符编码是51。不过只要把其中一个数改成数值形式而不是字符串形式就能解决这个问题,因为在比较数值和字符串时,字符串都会被转换为数值,然后再以数值方式与另一个数值比较。
相等操作符
相等操作符包括:相等和不相等——先转换再比较、全等和不全等——仅比较不转换。
不相等:!=
不全等:!==
注意:null和undefined是类似的值,但不是同类型的值,所以null==undefined,但是null!==undefined。
如果有一个操作数是NaN,则相等操作符返回false。但是即使两个操作数都是NaN,相等操作符也返回false,因为按照规则,NaN不等于NaN。
条件操作符、赋值操作符、逗号操作符
语句
- if语句
- do-while语句
- while语句
- for语句
- for-in语句
- label语句
- break和continue语句
- with语句
- switch语句
代码块:以一对花括号括起来的多行代码。
使用while循环做不到的,使用for循环同样也做不到。也就是说,for循环只是把与循环有关的代码集中在了一个位置。
break和continue语句的差异:break语句会立即退出循环,不执行循环后面的语句。continue语句也是立即退出循环,但退出循环后会从循环的顶部继续执行。
由于大量使用with语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with语句。
switch语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串”10”不等于数值10)。
可以通过省略break关键字来合并集中情况。
函数
JS中的函数没哟重载,所以如果出现了两个相同名字的函数,那么名字只属于后定义的函数。
变量、作用域和内存问题
基本类型和引用类型的值
ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指的是那些可能由多个值构成的对象。
其中有五种基本数据类型:Undefined、Null、String、Number、Boolean。
理解引用:
function setName(obj){
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nichloas"
函数里的obj其实引用的是另一个引用,当函数执行结束后,第二个创建的obj对象就被毁销毁。
用typeof用来检测数据类型,instanceof也可以检测,他可以检测原型链。
执行环境和作用域
当代码在一个环境中执行时,会创建变量对象的一个作用域链。(请注意和原型链的区别)
全局执行环境的变量对象始终都是作用域中最后一个对象。
延长作用域链
有两种方法,with语句和try-catch语句。
对于with语句来说,会将指定的对象添加到作用域中。对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
没有块级作用域
因为js没有块级作用域(ES6中可以用let创建),所以if、for语句中定义的变量即使在语句执行完毕后,变量也依旧会存在于语句外部的执行环境中。
查询标识符中,如果局部函数存在需要的变量,就不会再往上去查找上级作用域的变量,只有当当前作用域中没找到所需要的变量时,它会一级一级的往上查找。
垃圾收集
Javascript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
常用的有两个方法:标记清除和引用计数。
一旦数据不再可用,最好通过将其值设置为null来释放其引用,这个做法叫做解除引用(适用于大多数全局变量和全局对象的属性)。
解除引用的真正作用是让值脱离执行环境,一边垃圾收集器下次运行时将其回收。
小结
- 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中。
- 引用类型的值时对象,保存在堆内存中。
- 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向改对象的指针,因此改变该变量的值并不是改变对象。
- 确定一个值时哪种基本类型可以使用typeof操作符,而确定一个值时哪种引用类型可以使用instanceof操作符。
- 当代码中存在循环计数现象是,”引用计数”算法就会导致错误。
引用类型
引用类型是一种数据结构,用于将数据和功能组织在一起。
Array类型
虽然可以用instanceof操作符用来检测数组,但是如果网页中包含多个亏昂家,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不用版本的Array构造函数,如果使用instanceof只能检测只有一个全局执行环境的情况下,所以这个时候就需要使用Array.isArray()方法。
栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构。
对垒数据结构的访问规则是FIFO(I)
数组的方法:
- join()可以在改变分割符的情况下重现toString()方法的输出。
- push()方法可以接受任意数量的参数,把它们逐个添加到数组末尾,并返回修改后的数组的长度。
- pop()方法则是从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
- shift()方法能够移除数组中的第一个项并返回该项。
- unshift()方法可以在数组前端添加人一个项并返回新数组的长度。
- reverse()方法会反转数组项的顺序。
- sort()方法按升序排列数组项——即最小的值排在最前面,最大的只排在最后面。(有bug,因为比较的是字符串)
- concat()方法会先创建一个当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
- slice()方法可以接受一或两个参数,即要返回项的起始和结束位置的一个新数组。不会改变原数组,而是返回一个子数组。
- indexOf()方法可以从数组的开头(位置0)开始向后查找项和(可选的)表示查找起点位置的索引。(没找到的情况下会返回-1,可以比较使用的全等操作符,严格匹配,同lastIndexOf)
- lastIndexOf()方法可以从数组的末尾开始向前查找项和(可选的)表示查找起点位置的索引。
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
- filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成数组。
- forEach():对数组中的每一项运行给定函数,这个方法没有返回值。
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
- reduce():从数组的第一项开始,逐个遍历到最后,然后构建一个最终返回的值。
- reduceRight():从数组的最后一项开始,向前遍历到第一项,然后构建一个最终返回的值。
这里的filter()函数可以用回来过滤数据。
Data类型
许多的data的方法在P102 0.0。
RegExp类型
其基本的表达式为 ‘var expression = /pattren(模式) / flags(标志) ;’’
正则表达式U的匹配模式支持3个标志:
- [] g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
- [] i:表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写;
- [] m:表示多行模式,即在达到一行文本末尾是还会继续查找下一行中是否存在于模式匹配的项。
RegExp的实例属性
- [] global:布尔值,表示是否设置了g标志。
- [] ignoreCase:布尔值,表示是否设置了i标志。
- [] lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起。
- [] multiline: 布尔值,表示是否设置了m标志。
- [] source:正则表达式的字符串表示,按照字谜纳凉形式而非传入构造函数中的字符串模式返回。
DOM
Node类型
对于元素节点,nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终为null。
为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行标记。
对于arguments对象使用Array.prototype.slice()方法可以将其转换为数组。
如:’var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);’’
Document类型
文档信息:document.URL、document.domain、document.referrer。
文档写入:write()会鸳鸯写入,而writeIn()则会在字符串的末尾添加一个换行符。
写入一些JavaScript文件是,必须要注意字符串的转移,jS的标签。
Element类型
在取得特性的时候,注意传递给getAttribute()的特性命于实际的特性名相同。因此要想到要得到class特性值,应该传入”class”而不是”className”,后者只有在通过对象属性访问特性时才用。
DOM扩展
选择符API
querySelector()返回的是改模式匹配的第一个元素,querySelectorAll()返回的是一个NodeList。
HTML5
新增了getELementByClassName()方法和classList()方法。
焦点管理中,有focus()方法和hasFocus()方法。
readyState属性有两个值:loading和complate。
兼容模式也有两种:标准模式下,document.compatMode的值等于”CSS1Compat”,而在混杂模式下,document.compatMode的值等于”BackCompat”。
插入标记的时候,如果该元素与某个事件绑定,但是该元素被删除后,其绑定关系在内存中并没有一并删除。所以如果这种情况出现,页面占用的内存数量就会明显增加。所以当使用innerHTML(),outerHTMl(),insertAdjacentHTMl()方法是,最好先收工删除要被替换的元素的所有时间处理程序和JavaScript对象属性。
DOM2 和 DOM3
元素大小
偏移量:包括元素在屏幕上占用的所有可见的空间。
偏移量有四个属性:
- offsetHeight
- offsetWidth
- offsetLeft
- offsetTop
所有这些偏移量都是只读的,而且每次访问它们都需要重新计算。因此,应该尽量避免重复访问这些数据,如果需要重复使用其中欧冠某些属性的值,可以将他们保存在局部变量中,以提高性能。
客户区大小:指的是元素内容及其内编剧所占据的空间大小。有关客户去大小的属性有两个:cliendWidth和cliendHeight。都不算边框。包含内边距和内容。
范围
createRange()方法,可以选择文档中的一个区域。
如果要创建复杂的范围就得使用setStart()和setEnd()方法。
事件
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。
事件流
IE的时间流叫做事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
Netscape Communicator团队提出的另一种事件流叫做事件捕捉。事件捕捉的用意在于在时间到达预定目标之前捕捉他。
事件处理程序
DOM0级事件处理程序
DOM2级事件处理程序:addEventListener()和removeEvenetListener()。
IE时间处理程序:attachEvent()和detachEvent()。
在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用于衷运行,因此this等于window。
attachEvent()和addEvevtListener()不同的是,前者不是以添加他们的顺序添加,而是以相反的顺序被触发。
跨浏览器的事件处理程序:开发人员会创建一个方法,addHandler(),它的职责是视情况分别使用DOM0级方法,DOM2级方法或IE方法来添加事件。这个方法属于一个名叫EvenetUtil的对象。
要注意的是,使用IE和DOM2级方法定义的方法的时候是使用圆括号来定义,而DOM0级方法用的是方括号。
事件对象
在触发DOM上的某个事件时,会产生一个时间对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定时间相关的信息。
DOM中的事件对象,P355
注意event.target、event.currentTarget、this的值的区别:当event.eventPhase等于2时,这三个相等,当event.eventPhase等于1或者3时,event.currentTarget等于this,而target则只包含事件的事件目标。
在阻止特定事件的默认行为时,可以使用preventDefaule()方法。但是只有cancelable()属性设置为true的事件,才可以使用prevenetDefalt()来取消其默认行为。
另外,stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕捉或冒泡。
cancelBubble属性与DOM中的stopPropagation()方法作用相同,都是用来停止事件冒泡的。由于IE不支持事件捕捉,因此只能取消事件冒泡;但stopPropagation()方法可以同时取消事件捕捉和冒泡。
只有在时间处理程序执行期间,event()对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。
事件类型
各个事件类型 P362
- UI事件,当用户与页面上的元素交互时触发;
- 焦点事件,当元素获得或市区焦点时触发;
- 鼠标事件,当用户通过鼠标在页面上执行操作时触发;
- 滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
- 文本事件,当在文档中输入文本时触发;
- 键盘事件,当用户通关过键盘在页面上执行操作时触发;
- 合成事件,当IME输入字符时触发;
- 变动事件,当底层DOM结构发生变化时触发。
内存和性能
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
移除事件处理程序也很重要,不要让过多的内存被占用而没有被释放。
模拟事件
1.模拟鼠标事件 2.模拟键盘事件 3.模拟其他事件 4.自定义DOM事件
IE中的事件模拟思路与DOM中的模拟事件的思路相似,但是在实现灭个步骤时都采用了不一样的方法。
表单脚本
表单的基础知识
重置表单:使用type特性值为”reset”的<input>或者<button>都可以创建重置按钮。