js高级程序设计第三版 学习总结
一、第一章js简介二、在HTML中使用javaScript三、基本概念四、变量、作用域和内存问题五、引用类型六、面向对象的程序设计七、函数表达式十六、HTML5脚本编程十七、错误处理与调试二十、JSON二十二、高级技巧二十四、最佳实践二十五、新兴的API附录AECMAScript Harmony附录B严格模式附录CJavaScript库附录DJavaScript工具................
一、第一章js简介(专为与网页交互而设计的脚本语言)
(1)起源:为解决在交互时客户端(用户端)提前校验等而开发的一种客户端语言。
(2)JavaScript 与 ECMAScript 之间的关系:
ECMAScript :它规定了这门语言的语法、类型、语句、关键字、保留字、操作符、对象等,它是标准。
JavaScript:包含 ECMAScript、文档对象模型(DOM)、浏览器对象模型(BOM)
(3)DOM(文档对象模型):是操作于HTML的API,DOM是将整个页面映射为一个多层节点结构,即dom树,开发人员利用dom提供的api可以轻松的删除、添加、替换、修改等任何节点
<html>
// 头部元素
<head>
<title>Sample Page</title> // 浏览器中显示的标题,是必须设置的
<script></script> // 引入脚本
<style></style> // 引入样式,指示浏览器在哪里找到样式表
<meta></meta> // 有关页面的元信息
<base href=''/> // 设置页面所有链接的默认地址或默认目标 a/link/img/form,访问时两者的拼接
<link rel="stylesheet" type="text/css" href="/html/csstest1.css" />
</head>
<body>
<p>Hello World!</p>
</body>
</html>
// 获取dom节点(dom树中的任何节点都可以通过api获取)
(1)删除
(2)增加
(3)修改
(4)替换
(4)BOM(浏览器对象模型):用于操作浏览器相关属性的功能 (第8章深入讲解)
弹出新浏览器窗口的功能;
移动、缩放和关闭浏览器窗口的功能;
提供浏览器详细信息的 navigator 对象;
提供浏览器所加载页面的详细信息的 location 对象;
提供用户显示器分辨率详细信息的 screen 对象;
对 cookies 的支持;
像 XMLHttpRequest 和 IE 的 ActiveXObject 这样的自定义对象。
(5)ECMA(js标准,提供核心语言功能)和W3C标准(万维网联盟,负责制定web通信标准,支持html)
浏览器只是ECMAScript实现的宿主之一,宿主环境不仅提供ECMAScript实现,同时还提供该语言的扩展以便语言与环境间对接交互、操作。例如DOM
Node宿主环境(服务端js平台)
JavaScript 的这三个组成部分,在当前五个主要浏览器(IE、Firefox、Chrome、Safari 和 Opera)中都得到了不同程度的支持。
其中,所有浏览器对ECMAScript第 3 版的支持大体上都还不错,而对ECMAScript 5 的支持程度越来越高,但对 DOM 的支持则彼此相差比较多。
对已经正式纳入 HTML5 标准的 BOM 来说,尽管各浏览器都实现了某些众所周知的共同特性,但其他特性还是会因浏览器而异。
二、第二章在HTML中使用javaScript
(1)script元素(即在html中中使用js,可以直接在script元素中嵌入代码也可以在script中通过src引入外部js)
// script 中的js会从上到下逐行依次解析,放在head中会阻断页面的加载及渲染
<script
src="https://lib.baomitu.com/jquery/3.5.1/jquery.min.js" // 外部文件地址源
type="application/x–javascript" // MIME类型(js的内容),一般不设,可能会导致脚本被忽略。默认text/javascript
async // 只针对外部脚本文件有效,表示立即下载,但不会妨碍页面当前其他操作,例如下载其他资源或等待加载其他脚本(说明是异步的) 不设置时下载和解析js时页面的处理也会暂时停止(例如放到head中,body会暂缓执行,因为按前后顺序)。 问题点:不能确保按序执行,因此🉐️确保js外部脚本间相互不依赖。
defer // 只针对外部脚本文件有效,脚本延迟到dom文档完全被解析和显示后再执行(即立即下载但延迟执行) 问题点:多个延迟脚本不能保证按序执行
>
</script>
⚠️ :(1)在嵌入的js(即在标签内部写代码)中不能再出现现"“字符串,它会认为是js的结束。可通过转义解决”<\ /script>"。
(2)通过引入外部js时,但标签中间不应该再有代码,里面不要写嵌套的js,会被忽略。
(3)src可以引入外部域(直接指定完整url)的js文件。
(4)如果不设只要不存在 defer 和 async 属性,浏览器都会按照
<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>
三、第三章基本概念:介绍js脚本语言最基本的工作原理(语法、操作符、数据类型、内置功能)
(1)语法
区分大小写(变量函数名操作符)
标识符(变量、函数、属性、函数参数):字母或_或 开头,剩下可以是字母、数字 、 、 开头,剩下可以是字母、数字、_、 开头,剩下可以是字母、数字、、(即不能是数字开头)。但不能是关键字、保留字、true、false、null (采用驼峰命名比较好)
语法间分号结尾
(2)变量:js的变量可以用来存放任何类型的值(字符串、数字等均可),不像别的语言整型需要整型声明等区分 var let const int具体来区分
(3)数据类型:6种简单的数据类型(值类型)、3种复杂数据类型(引用类型)
检测数据类型的方法 :typeof是操作符,不是函数,不写括号可以
typeof a => 'undefined' 如果变量未定义或未赋值(没声明或只声明) 未声明的使用会报错 Uncaught ReferenceError: jjj is not defined,未声明的变量只可执行typeof操作符
typeof a =>'boolean' 只有这个值是布尔值 true falae
typeof a =>'string' 这个值是字符串时 ‘aaa’或`aaa${}`
typeof a =>'number' 这个值是数值 正常的数字及NaN(NaN实在运算过程中产生的,原本期望得到数字的值但结果是非数字时 -‘a’ /'a'等,但+作用于字符串时认为是字符串链接符)
转换为数值Number()、parseInt()和 parseFloat()后两个用于将字符串转为数值
typeof a =>'symbol' 每个symbol值都是唯一的,它可以作为对象的键名(属性,不会跟别人重)但Symbol不是一个构造函数,使用new 报错 TypeError
typeof a =>'object' 这个值是对象或null或数组
typeof a =>'function' 这个值是函数
⚠️ 对于未声明的变量只能执行typeof操作符
⚠️ null值一般是用来声明的变量将来保存对象,这样 判断是否为null就知道是否存了对象(开发时的惯例)null==undefined true NaN==NaN false
⚠️浮点数的最高精度是17为小数,若再多直接忽略。但算数计算时精度不准 0.1+0.2==0.3 false
⚠️js保存的最小数Number.MIN_VALUE 最大数 Number.MAX_VALUE,若超出则为无穷大Infinity -Infinity isFinite()判断是否有穷的数
Number(undefined) NaN 若字符串不是只包含数字则NaN,它会忽略8进制认为是10进制。此转换字符串时不合理有时规则模糊,用parseInt()它会识别8、16进制
parseInt("10", 2) 第二个参数代表按几进制解析 parseFloat()只解析10进制值,没有第二个参数 10.toString(2) 10转为2进制的字符串,不能转换null、undefined。 找有效的整数
String() 可将任意类型转为字符串,若有toString方法调用转,若是null、undefined无toString则转为对应的字符串即可
操作符有:typeof、一元二元操作符(递增、递减、&、|、!、~、^(相同为0不同为1)、<<、>>、&&、||、!、+-*/、%等)
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值
if语句
for语句
for-in语句:一般用于遍历对象的 属性 for(prop in obj)
label语句:用时查不太使用
break和continue语句
with语句:可以整合写代码,将都包含的语句提出来,一般不适用
switch语句
函数参数没有多少的限制,不用跟行参一致,函数的参数会在内部 的arguments伪数组中。参数传递的是值,即不管值类型、引用类型都会复制一份给实参变量,通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。 函数没有重载,即同名函数后面的会覆盖前面的函数
构造函数:Object()、Number()、String()、Boolean()、Function()
// Object内置构造函数包含的属性(它具有的属性和方法所有的实例也具备)
(1)construction 构造函数 (实例谁创建出来的就是谁 p1.constructor得到构造函数的名字)
(2)hasOwnProperty(propertyName) 用于检查属性是否在实例中而不是在实例的原型链中
o.hasOwnProperty("name") 返回true或false
(3)isPrototypeOf(object) 一个对象是否存在于另一个对象的原型链中 a.isPrototypeOf(b)
(4)propertyIsEnumerable(propertyName)用于检查给定的属性是否能够使用 for-in 语句来枚举
(5)toLocaleString()返回对象的字符串表示,该字符串与执行环境的地区对应'[object Object]'
(6)toString() 返回对象的字符串表示 '[object Object]'
(7)valueOf()返回对象的字符串、数值或布尔值表示,一般与toString()返回同. {aa:1}
// 函数的属性(完了补充)
四、第四章变量、作用域和内存问题(基本、引用、执行环境、垃圾回收)
(1)基本类型和引用类型的值:
基本类型是按值访问的,存放在栈内存中。值不可改变,只能给变量重新赋值
引用类型:Object 类型、Array 类型、Date 类型、RegExp 类型、Function 类型。引用类型是按引用访问的,值是可变的,比较是引用地址的比较。引用类型的值是保存在堆内存的Object中,栈内存中保存了变量标识符和指向堆内存中该对象的指针
简单类型使用typeof 检测类型即可(不会有分不清的情况,null返回‘object’)
引用类型检测方法(因为typeof检测null、array、object都是对象,分不清具体是什么对象)
(1)instanceof (根据原型链来识别。判断变量是引用类型的实例吗)
colors instanceof Array {} instanceof Object pattern instanceof RegExp
缺点:所有引用类型的值都是Object的实例,所以根据instanceof判断是否为对象不准,[] instanceof Object // true(value instanceof Array ) 简单类型都false,因为简单类型不是对象
Object.prototype.toString.call(value) == “[object Array]”
(2)执行环境和作用域
全局执行环境:最外围的一个执行环境。Web 浏览器中,全局执行环境被认为是 window 对象(所有全局变量和函数都在此对象上创建)
作用域链:就是一层一层访问变量,一级一级找,最后就是全局环境的变量对象(内部环境可以通过作用域链访问所有的外部环境,即向上搜索)
全局作用域、函数作用域、没有块级作用域(用花括号包的,例如if、for 会当成全局作用域,后加了)
使用var声明的变量会添加在最接近的环境中,函数中 就是函数局部作用域。未使用var声明的直接变量,会添加至全局环境中。所以在写代码时要声明。
补充js的块级作用域:使用let const {}
(3)垃圾收集(js具有自动垃圾收集机制,执行环境会负责管理使用的内存)
原理:找出那些不再继续使用的变量,释放占用的内存(按固定的时间间隔周期性的找出不再继续使用的变量,释放内存),但哪些变量会不会继续使用,需要垃圾收集器追踪标记(办法标记清楚、引用计数)。
标记清楚(常用):垃圾收集器会在运行时给内存中的变量加标记(会略过环境中变量或被环境引用的变量,之后加上清除的标记,垃圾收集器周期执行时释放,进入环境、离开环境)
引用计数(不常用):跟踪记录每个值被引用的次数,当赋值引用类型时标记1,再将值赋值另一变量时标记加1,若变量赋值另一值则减1,直至为0说明没办法再访问这个值了,垃圾收集器执行时就可以回收了。(不常用,例如对象中相互引用的永远不会为0)
性能问题:垃圾回收机制是周期性执行的,但多长时间运行一次,触发条件是什么。动态修正,根据回收内存分配,变量、字面量等跟临界值的动态调整。
管理内存:性能,可将没用的内存手动设置为null释放内存(对相互引用比较管用,否则就会永远释放不了),保证使用最少的内存让页面有更好的性能(应及时解除不使用的全局对象及循环引用的变量)。
五、第五章引用类型(所有的引用类型变量都是Object的实例,用instanceof不准)
1、Object类型
new Object() 或对象字面量{} (更常用)
点访问或方括号访问,方括号可以是变量或 不全是字符串的命名属性
2、Array类型(数组的每一项都可以保存任意类型的数据,数组的大小可随着增删动态调整)
new Array() new Array(10) 长度为10 new Array(“red”, “blue”, “green”) 或 数组字面量 [ ]
数组的length不是只读的,可以设置它的长度移除多余项或增加项(增加的是undefined),最长4 294 967 295 项,若接近这个值会导致时间脚本超长报错
(1)检测数组
value instanceof Array 判断变量是否某构造函数的实例,但此方法假设只有一个全局环境,若网页中包含多个框架(一个页面包含多个 frame),此时构造函数不同,判断不准确,因为Array是window属性,若在别的frame中定义数组就会返回false
Array.isArray(array) // 返回布尔值,不受各个全局环境的影响(但IE9+、Firefox 4+、Safari 5+、Opera 10.5+和 Chrome支持,有些低版本不支持)
Object.prototype.toString.call(value) == “[object Array]” // 该方法底层也是可以修改的,未修改过的是这样
判断其他引用类型也是同样的道理
Object.prototype.toString.call(value) == “[object Function]” 有特殊
Object.prototype.toString.call(value) == “[object RegExp]”
(2)转换方法(所有对象都有toString、toLocalString、valueOf 数组也不例外)
数组的toString()、toLocaleString() 会返回数组的以逗号分隔的字符串,若是空则是空串
valueOf() 返回原数组
join() // 以逗号分隔的字符串,可以规定其他分隔符或没有,数组中的某一项的值是 null 或者 undefined则以空字符串处理
(3)栈方法(LIFO后进先出,在末尾添加在末尾移除(只发生在栈顶),改变初始数据)
array.push(“red”, “green”) // 返回修改后数组的长度
array.pop() // 移除数组的最后一项,返回的是移除项
模拟栈可用数组的pop()、push()
(4)队列方法(FIFO先进先出,前加前出。改变初始数据)
array.unshift(‘red’) // 向数组的最前端添加项,返回的是数组的长度
array.shift() // 移除数组的第一项,返回第一项
模拟队列可以用数组的shift() 最前删除、push() 队尾添加
(5)重排序方法:会改变原数组,返回更改后的
array.reverse() 数组的翻转
array.sort() // 数组的排序,默认升序排列,排序时会调用数组项的toString()方法,字符串间的排序,所以数字的排序会有问题
接受比较函数作为参数,方便我们拿到想要的排序结果
array.sort((a,b)=>a-b)
(6)操作方法concat、slice、splice(删除、插入、替换)
数组的连接concat:返回新的数组,不改变原数组 colors.concat(“yellow”, [“black”, “brown”])
数组的截取slice: 数组的截取,参数是起始、结束的位置,包左不包右,返回截取的项,不改变原数组 (负数时+数组长度来确定,结束<开始,返回[],不传参数截取所有)
数组的增删改splice: 改变原数组,它的返回值是删除项数组,无则是空数据[]
数组的删除(可删除任意数量的项):两个参数,要删的第一项的位置和几项 splice(0,2) 删前两项
数组的增(插入):指定位置插入任意数量项:3个或多个参 起始位置、删除项(0)、插入项 splice(2,0,“red”,“green”) 数组2位置插入
数组的改(替换):可以项指定位置删除任意数量项同时删除任意数量项 splice (2,1,“red”,“green”) splice (2,1,“red”,“green”)
(7)位置方法:indexOf()、lastIndexOf():参数要查找的项、查找起始位置,返回查找项在数组中位置,无-1(不改变原数组) ===查找
array.indexOf(4) 查找不了复杂项(复杂项的查找跟指向有关,只能在志向相同的的堆数据中找)
var person = { name: “Nicholas” };
var people = [{ name: “Nicholas” }];
var morePeople = [person];
alert(people.indexOf(person)); //-1
alert(morePeople.indexOf(person)); //0
(8)迭代方法(5个)参数函数(值、索引、原数组)、this对象
forEach // 每一项运行函数,没有返回值 array.forEach((value,index,arr)=>{// 代码逻辑块}),同for循环 for (let i = 0; i < arr.length; i++)
map // 对数组中每一项运行函数,返回每项在函数中处理后返回的结果组成的数组
const dataShops = R.map(item => ({
…item,
nameAndCode: ${item.displayName}(${item.shopCode})
,
}))(R.clone(this.shopNameList)); // 会在原数组每项上增加 nameAndCode属性
filter // 每项执行函数,返回满足函数的项的成员数组,无[]。 一般用于找出满足某条件的所有项,实现过滤的功能
list = R.filter((x) => {
const { storeName = ‘’, storeId = ‘’, pyName = ‘’ } = x;
return storeName.includes(value) || storeId.includes(value) || pyName.includes(value);
}, shopCodeList) || [];
every // 每项都满足函数则返回true
some // 任意项满足函数则返回true
(9)归并方法:迭代所有项,返回最终值。参数每项执行函数(前一个值、当前值、当前项索引、数组)、归并初始值(可选)
reduce
reduceRight
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); //求和操作 15
3、Date类型(日期构造函数): new Date()。项目中通常使用moment库来进行日期 的处理
Date.parse() 返回日期的毫秒数(自1970.1.1日),传的参数识别不了NaN,格式也没规定,传入的参数不能返回日期则NaN
Date.UTC() 返回毫秒数但参数格式有规定
(1)继承的方法(重写了这些方法)
toLocaleString() 会根据浏览器设置的格式返回日期的字符串形式(一般不带时区)
toString() 带有时区信息的日期和时间
valueOf() 返回的是日期的毫秒
new Date().toLocaleString() // ‘2021/12/9 下午8:10:59’,不同的浏览器返回的不同
new Date().valueOf() // 1639052124945,日期比较时适用
(2)日期格式化方法:需要展示不同的日期格式时适用,不常用
(3)日期/时间组件方法(项目常用):直接获取或设置日期中特定部分的方法,UTC格式的及设置特定的都有类似方法 set
.getTime() // 拿到日期的毫秒数,与valueOf()同 .setTime(毫秒值) 设置时间,返回是毫秒
.getFullYear() // 得到日期对应 的4位数的年份 .setFullYear(年) 重新设置年
.getMonth() // 返回月份 0-11 .setMonth(月) 0-11
.getDate() // 日 1-31 setDate(日)
.getDay() // 返回星期几 0(星期日)-6(星期六)
.getHours() //小时数 0-23 .setHours(时) 设置日期中的小时
.getMinutes() // 分钟数 0-59 .setMinutes(分) 设置日期中的分钟
.getSeconds() // 秒数 0-59 .setSeconds(秒) 设置日期中的秒钟
.getMilliseconds() // 毫秒数 0-999 setMilliseconds(毫秒) 设置日期中的毫秒数
4、RegExp类型(正则构造函数):
定义方法:
(1)new RegExp(“[bc]at”, “i”) 转义时需双重转义。 /[bc]at/ ===“\[bc\]at”
(2)字面量 /(([bw])(\d).)*([A-z0-9]+.)+(com)(:\d+)?/ 域名端口匹配(有或没的匹配)。字面量格式: / pattern / flags
pattern任意简单的复杂的正则表达式,字符类、限定符、分组等
flags 标识符g、i、m
⚠️:toLocaleString()和 toString()方法都会返回正则表达式的字面量
valueOf()方法返回正则表达式本身
正则常用的语法:
简单模式:/abd/ 直接写在正则中,表示必须按顺序完全匹配,即abd都包含,只能多不能少
特殊字符:
\ // 不是特殊字符之前的表示下一个是特殊字符 [ ,特殊字符前的表示它们实际意义的匹配 \d表示数字 0-9任意一个(补充捕获的知识点)
^ // 以什么开始的意思,/^A/ 并不会匹配 “an A” 中的 ‘A’,但是会匹配 “An E” 中的 ‘A’,必须以A开头
$ // 匹配结尾字符 /t$/ 并不会匹配 “eater” 中的 ‘t’,但是会匹配 “eat” 中的 ‘t’
- // 匹配前一个表达式 0 次或多次。等价于 {0,}
- // 匹配前一个表达式1次或多次。等价于{1,}
? // 匹配前一个表达式0次或1次。等价于{0,1}.如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符)
“123abc” 使用 /\d+/ 将会匹配 “123”,而使用 /\d+?/ 则只会匹配到 “1”
. // 匹配除换行以外的任意字符
() // 对字符串进行分组并保存匹配的文本 。对字符或元字符分组 (A\d){2},即A和数字组合匹配两个。或表示可选择性从两个或多个中选择。eg、gr(a|e)y匹配gray和grey,(Doctor|Dr.?)匹配Doctor,Dr,Dr.三种情况。分组或可选择的功能
(?:x) // 匹配x但是它属于非捕获圆括号,可以应用运算符 /(?:abc){1,2}/ 整体abc匹配1或2次,但/abc{1,2}/ 只是针对c字符
x(?=y) // 匹配x仅当x后是y,但是y不会出现在匹配中(类似条件的感觉,先行断言)/Jack(?=Spr|Fro)/匹配‘Jack’仅当它后’Spr’或是‘Fro’
(?<=y)x // 匹配x仅当x前是y,但y不会出现在匹配中(类似条件的感觉,后行断言)/(?<=Ja|To)Sprat/匹配‘ Sprat ’当前是’Ja’或‘To’
x(?!y) // 匹配x仅当其后不是y /\d+(?!.)/ 一个或多个数字后没有小数点的匹配
(?<!y)x // 匹配 x仅当其前不是y /(?<!-)\d+/ 匹配数字前没有-的
x|y // 匹配x或y /green|red/匹配“green apple”中的‘green’和“red apple”中的‘red’
{n} // 前一个表达式匹配几次的意思
{n,} // 前一个表达式匹配至少n次 >=n
{n,m} // 前一个表达式最少n次至多m次
[xyz] // 一个字符集合,匹配方括号中任意一个字符,方括号中对于特殊字符. *等不用转义
[^xyz] // 一个反向字符集,匹配没有包含在方括号中字符。[^abc] 和 [^a-c] 是一样的。他们匹 配"brisket"中的‘r’,也匹配“chop”中的‘h’。
[\b] // 匹配一个退格(U+0008) \n 换行。\r回车
\d // 匹配一个数字 同[0-9]
\D // 匹配一个非数字 同[^0-9]
\s // 匹配一个空白字符 空格、换行等等[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]
\S // 匹配一个非空白字符
\w // 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]
\W // 匹配一个非单字字符。等价于 [^A-Za-z0-9_]。
实例属性不会重置问题(实例都具备的属性):
global:布尔值,表示是否设置了 g 标志。
ignoreCase:布尔值,表示是否设置了 i 标志。
multiline:布尔值,表示是否设置了 m 标志。
lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
source:正则表达式的字符串表示,按照字面量形式返回字符串表示
实例方法 :
(1)reg.exec(‘匹配字符串’) // 返回第一个匹配项信息的数组 /s/.exec(‘sjhavv’) // [‘s’, index: 0, input: ‘sjhavv’, groups: undefined]
(2)reg.test('匹配字符串) // 返回布尔值,一般用于判断某字符串是否满足某正则,返回布尔
var text = “000-00-0000”;
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
alert(“The pattern was matched.”);
}
还可以用于字符串方法: match、matchAll、replace、search和 split 方法
string.match(/abs/) // 它返回匹配结果数组,匹配到就返回。全局’sdfhhhsdf’.match(/sdf/g) // [‘sdf’, ‘sdf’]
string.search(/abs/) // 返回匹配到的位置索引,无-1,只看第一次的匹配
string.replace() // 用指定的内容替换正则匹配到的字串url.replace(/(([bw])(\d).)*([A-z0-9]+.)+(com)(:\d+)?/, window.location.host);
string.split() // 字符串转数组方法,用指定符号分隔或正则匹配分隔
flags标志常用的3个:
g:全局,即不会只匹配第一个,匹配所有
i:忽略大小写
m:多行匹配,,不会行末直接结束,会看下一行是否存在匹配的项
⚠️ 在正则中使用的特殊元字符需要转义 /[bc]at/i // 匹配第一个" [bc]at",不区分大小写,转义了[]
// 补充反向引用、捕获的知识点
5、Function类型(函数构造函数) 函数的名字仅仅是一个包含指针的变量而已
函数都是Function的实例,而函数实际上是对象,它具有属性和方法,函数名实际上是指向函数对象的指针(类似对象名),不带括号的函数名就是访问函数指针
函数声明式定义:function test(){}
函数表达式定义:const test=function(){}
构造函数定义:const test=new Function((“num1”, “num2”, “return num1 + num2”) 最后一个参数始终被当成函数体
(1)没有重载(将函数名理解为指向函数对象的指针 ,不可能存在同名重载的问题,后面的会覆盖前面的)
(2)函数声明与函数表达式:区别函数声明解析器会率先读取,使得在任何地方可用,而表达式需要执行时在解析执行
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
// 可正常执行,因为代码执行前,函数声明已提升解析可使用
alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
};
//. 报错
(3)作为值的函数:可以将函数作为参数传递也可以函数作为另一个函数的结果返回
function callSomeFunction(someFunction, someArgument){
return someFunction(someArgument);
}
// 例子
function add10(num){
return num + 10;
}
var result1 = callSomeFunction(add10, 10); // 第一个参数是函数,第二个参数
alert(result1); //20
// 数组对象中根据某属性排序
function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
var data = [{name: “Zachary”, age: 28}, {name: “Nicholas”, age: 29}];
data.sort(createComparisonFunction(“name”));
alert(data[0].name); //Nicholas
(4)函数内部属性:arguments 和 this
arguments:伪数组对象,保存传入函数的所有参数,它的属性 arguments.callee指向这个函数(直接arguments访问即可)
this:全局环境调用函数时,函数中的this就指向window。对象中方法的调用,this指向对象
(5)函数属性和方法
length:函数希望接收的命名参数的个数
function sayName(name){
alert(name);
}
alert(sayName.length); //1
prototype:原型,实际是引用类型保存它们实例方法的真正所在,主要用于创建自定义的 引用类型实现继承时用
apply方法:在特定的作用域下调用函数,即可以更改this指向,参数运营函数的作用域(this指向)、参数数组
call方法:在特定的作用域下调用函数,即可以更改this指向,参数运营函数的作用域(this指 向)、参数列表(逐个列举的形式)
bind方法:会立即创建函数的实例,this绑定会根据bind绑定的对象,创建的函数就是原函数同(它的实例,所以属性方法都有)
var o = { color: “blue” };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
toLocaleString()和 toString()、valueOf() 都是返回函数代码
js中构造函数(它是一个函数,为了创建实例为定义的):Object、Array、Date、Function、RegExp、 Boolean、String、Number、Error
Object 是一个基础类型,其他所有类型都从 Object 继承了基本的行为
6、基本包装类型:定义基本类型的值时实际都调用了对应的构造函数创建,所以存在实例方法
(1)Boolean
不要使用Boolean创建布尔值,因为保存的时是它的实例对象,转成布尔都是true
var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); //true
(2)Number:
数值转字符串方法:alert(num.toString(2)); //“1010” ,转为几进制的字符串
数值格式化字符串的方法:num.toFixed(2) // ‘’10:00“,转为两位小数的数值字符串,若舍的话,会进行四舍五入
(3)String
字符方法:charAt(),返回索引对应的字符和 charCodeAt()返回索引对应位置上字符的字符编码(e=>‘101’)
字符串操作方法: concat()接受任意多个参数、slice()、substr()和 substring()
字符串位置方法:indexOf()和 lastIndexOf()
字符串大小写转换方法:toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase(),针对地区方法更稳妥
字符串模式匹配方法:match()、search()、replace()
string.trim() 去除前后空格
localeCompare() 字符串比较方法,返回-1|0|1
fromCharCode()方法:接收一或多个字符编码,然后将它们转换成一个字符串 (String.fromCharCode(104, 101, 108, 108, 111) // ‘hello’
⚠️ 使用基本类型构造函数创建的基本类型 typeof 返回‘object’ 直接赋值基本类型返回 ‘number|string|boolean’
7、单体内置对象: ECMAScript 实现提供的、不依赖于宿主环境的对象,程序执行前就存在了。
(1)Global对象(全局对象):全局作用域中属性和方法都在全局对象上,浏览器的全局对象是window(在大多数 ECMAScript
实现中都不能直接访问 Global 对象)
进行编码的方法:encodeURI() 只转换空格
encodeURIComponent() 例如空格 20%等的转换(编码Unicode字符,文字特殊字符等都有,这样不乱码,它会转换任何非标准字符)
// 编码
const url = '/web/CheckWorkBench/Exception/DataList';
const params = {
orderDate: bookingDate,
orderPlanCodes: orderPlanCodeList,
dataType: { key: '3', label: '订货数' },
checkLinkType: { key: '1', label: '首次转单前' },
transferStatus: { key: 0, babel: '预转单失败' },
dimensionType: '28',
};
return `${url}?params=${encodeURIComponent(JSON.stringify(params))}`;
// 对任何非标准字符进行编码 例如 : 空格等
解码:decodeURI()和decodeURIComponent()他们与以上方法一一对应
// 将url中的参数转换成对象的形式再进行解码
import qs from 'query-string';
const parse = () => qs.parse(window.location.search) || {};
this.params = JSON.parse(decodeURIComponent(parse().params));
eval() // 将js字符串转为js并执行(一般不要使用),它与当前作用域环境同,可以定义变量函数也可以调用,但eval中定义不存在变量提升
(2)Math对象:与数学计算相关(不是构造函数,不能使用new)
Math.min() 找一组数中最小值
Math.max() // Math.max.apply(Math, values),将设置正确的this及数组参数
Math.ceil() // 天花板函数,取整,实现的向上取整 Math.ceil(25.1) // 26
Math.floor() // 地板函数,取整,实现的是向下取整 Math.floor(25.9) // 25
Math.round() // 将数值四舍五入到最接近的整数(标准的四舍五入)
Math.random() // 返回大于等于0小于1的随机数
eg.选择1-10间的数值 var num =Math.floor(Math.random() * 10 + 1)
Math.pow(num,power) 幂次方
Math.sqrt(num) 返回num 的平方根
Math.abs(num) 返回num的绝对值
六、第六章面向对象的程序设计(就是有类的概念,可以通过类创建任意多个相同属性和方法的对象。面向对象是以功能来划分问题,而不是以步骤解决)封装、继承、多态(js无)
面向过程与面相对象的 优缺点 :
面向过程任务明确效率高。但需要深入思考扩展差维护性差,每个行为不一样时无法复用
面向对象结构清晰,易扩展维护。但时间空间开销大 ,因为便携时需要更高的逻辑抽象
理解对象:
(1)数据属性:属性默认的特性可不可delete删除、for-in是否可遍历、能否修改属性的值
如何修改默认的特性:Object.defineProperty()
Object.defineProperty(person, “name”, { // 这个对象中只能设置特殊属性的一个或多个configurable、enumerable、writable 和 value
writable: false, // 是否可修改
value: “Nicholas” // 对象中属性的值
configurable: false //一旦把属性定义为不可配置的,就不能再把它变回可配置了,会抛错
});
(2)访问器属性(访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数),不能直接定义,通过用 Object.defineProperty()
// 访问器属性一般用于一个属性的值会导致其他属性发生变化的情况,这样设置比较好,不用自己再去监听
// 只指定get表明只读不能写,只指定set只写不读
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); //2
(3)定义多个属性,以上只能一个个实现
Object.defineProperties()
Object.defineProperties(book,{
_year:{
value:2004,
// 别的属性等
},
(4)读取属性的特性 Object.getOwnPropertyDescriptor() 返回对象描述(可以看访问器属性或数据属性),只能看实例属性描述,直接原型对象上调用就是原型对象属性的描述
vardescriptor=Object.getOwnPropertyDescriptor(book,"year")
alert(descriptor.value);
alert(descriptor.enumerable);alert(typeofdescriptor.get);
创建对象:
(1)工厂模式:将通过参数的形式将对象创建放在函数中并返回对象,通过调用函数时传不同的参数就得到不同的属性(在函数里面创建对象,返回新对象)
// 只是解决了创建多个同属性名对象的问题
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
(2)构造函数模式:利用this,没有return语句,函数名大写(问题:函数定义在内部每次创建不好,外部给全局增加了太多函数变量)
function Person(name, age, job){ // this指向创建的新对象,用新对像替换this
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){ // 定义在全局环境 中防止每次创建都重新创建
alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
(3)原型模式:我们创建的每个函数都有原型属性(prototype)它指向原型对象(一般放共享的属性和方法)
// 原生对象Object、Array等的实例方法都是定义在原型上的方法,所以所有实例都可以访问,除非重写。
// 原型模式缺点:原型模式一般都是共享属性,而经常实例需要各自的属性,所以光原型对象解决不了实际需求
// 将共享的属性和方法放在构造函数的原型对象上,使得由构造函数创建的对象都可以访问
//原型对象有个默认的属性指向构造函数constructor,Person.prototype. constructor 指向 Person
// 不能通过实例修改原型属性,它会创建或修改实例属性
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
// 若直接将Person.prototype设置为对象字面量的形式时,默认的原型对象的属性constructor不再指向构造函数是Object,需手动处理
// 重写原型对象后,之前的实例还是指向旧的原型对象
(4)组合使用构造函数:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
// 常用,在像实例对象添加新属性或重写属性不会影响其他实例的属性值
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
(5)动态原型模式(例如只有共享属性或方法在实例中不存在时在原型上加,因为存在的话添加永远也访问不到,它会有访问顺序)
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
(6)寄生构造函数模式(跟前面的工厂函数类似,只是函数是构造函数,在构造函数中创建对象并返回)
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
(7)稳妥构造函数模式(适合在安全环境中 使用,禁止使用this及 new。当普通函数调用 ),不使用
// 判断属性是否存在在实例中hasOwnProperty
(person1.hasOwnProperty(“name”) // 给定属性存在于对象实例中时,才会返回 true,原型中是false
// in 操作符 1、for in 循环中 2、通过对象能访问属性时true(不管是实例属性还是继承的原型属性) (“name” in person1)// true或false
// 访问对象中所有可枚举的属性 Object.keys(对象) ,得到属性名组成的数组,无即空[]
// 访问对象中属性。 Object.getOwnPropertyNames(Person.prototype),返回属性名组成的数组,不管可不可以枚举都会返回
继承:
(1)原型链:利用原型对象是另一构造函数的实例,原型对象有指向另一构造函数原型的指针,原型又有指向构造函数的指针,层层叠代
别忘记默认原型的问题:所有函数的默认原型都是 Object 的实例,它内部指针指向Object.prototype,默认有的属性方法都在它上
确认原型与实例的关系:1)instanceof 看是否为某构造函数或内置对象的实例instance instanceof Object 2)只要原型链中出现的原型可以认为原型链中派生的实例的原型 Object.prototype.isPrototypeOf(instance) // Object.prototype是实例instance的原型链中的原型吗
⚠️ :通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链
⚠️ :缺点:实现原型链继承时一般将原型设置为实例,此时原实例中的属性就成原型中的属性,这些属性就不是单个实例单独所有,共享
(2)借用构造函数:在子类型构造函数的内部调用父类型构造函数
// 缺点方法在构造函数中定义。做不到函数只定义一次复用,每次相当于重新定义函数
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了 SuperType
SuperType.call(this); // 调用了另一构造函数的属性方法并更改了this指向(确保实例间相互不影响)
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
// 可传参数了
this.name = name;
}
function SubType(){
//继承了 SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age); //29
(3)组合继承:原型链和借用构造函数继承结合
//. 原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承(函数可复用,实例有各自的属性)
function SuperType(name){ // SuperType构造函数
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){ // SuperType原型
alert(this.name);
}
function SubType(name, age){ // SubType构造函数继承了 SuperType构造函数中的属性且可以传参数
//继承属性
SuperType.call(this, name); // 缺点第二次调用 SuperType()
this.age = age;
}
//继承方法
SubType.prototype = new SuperType(); // 第一次调用 SuperType(),继承的构造函数的原型是被继承构造函数的实例
SubType.prototype.constructor = SubType; // 重写,不重写会执行父构造函数
SubType.prototype.sayAge = function(){ // 新增方法
alert(this.age);
};
// 实例1
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
// 实例2
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
(4)原型式继承:在对象中创建构造函数,将传入的对象为构造函数的原型(Object.create())
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
(5)寄生式继承:不常用
(6)寄生组合式继承:不常用
七、第七章函数表达式(定义函数的其中一种方式,在使用前必须先赋值,不存在变量提升)
// 写法1 function后没有函数名,即匿名函数functionName.name ''
var functionName = function(arg0, arg1, arg2){
//函数体
};
(1)递归:递归函数是指通过函数名字调用自己的情况
// 求n!
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1); // return num * arguments.callee(num-1); arguments.callee是指向正在执行函数的指针
// 保证怎样调用函数都不会出错,所以用arguments.callee比使用函数名更保险。也可以用命名函数表达式,这样即使赋值给变量,函数名有效
}
}
(2)闭包:闭包是指有权访问另一个函数作用域中的变量的函数(通常是一个函数中创建另一个函数实现)
// 理解作用域链的概念:当函数被调用时会创建一个执行环境及作用域链(它及更外侧的环境中变量可使用)
// 闭包中的this对象:this对象一般是运行时基于函数的执行环境绑定的,全局指向window 对象的方法指向对象
// 匿名函数一般指向window,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window
var name = "The Window";
var object = {
name : "My Object", // 不在匿名函数的作用域中
getNameFunc : function(){
return function(){ // 匿名函数
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下),先自己函数作用域找,再全局找,不可能在包的对象中找
// 将其放在闭包中,就在访问的作用链中了
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object" 它自己作用域、外部函数作用域、全局作用域
// 调用对象中的方法时
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
这里的 getName()方法只简单地返回 this.name 的值。以下是几种调用 object.getName()的
方式以及各自的结果。
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下,先执行将函数赋值,再调用赋值后的结果,this得不到维持
// 闭包中内存泄漏问题(ie中使用垃圾收集器不同造成)
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){ // 闭包的作用域链中保存html元素则无法销毁,只要匿名函数存在,element 的引用数至少也是 1
alert(element.id);
};
}
// 解决改写
var element = document.getElementById("someElement");
var id = element.id; // 副本保存在变量中
element.onclick = function(){
alert(id);
};
element = null; // 手动释放内存
}
(3)模仿块级作用域(es6中let、const定义的变量具有块级作用域)
function outputNumbers(count){
for (var i=0; i < count; i++){ // i变量不仅在for循环中可用,自定义后在outputNumbers作用域内都可用
alert(i);
}
alert(i); //计数
}
//利用匿名函数模仿块级作用域,将函数声明用圆括号包实际是一个函数表达式
(function(){ // 定义并立即调用匿名函数,不加圆括号报错,因函数声明后不能加圆括号,表达式可以
//这里是块级作用域
})();
//同
var test=function(){
//这里是块级作用域
}
test()
// 一般用于防止往全局中添加太多的变量减少内存的使用及命名冲突
(4)私有变量:js中没有私有成员概念,所有对象属性都是公有的
// js中私有变量指的是:私有变量包括函数的参数、局部变量和在函数内部定义的其他函数,因为不能在外部访问
function add(num1, num2){ // 私有变量num1 num2 sum
var sum = num1 + num2;
return sum;
}
八、第八章Bom(浏览器对象模型):web中使用js,bom也是非常重要的核心内容,浏览器window对象模型提供的对象用于访问浏览器的功能,与任何网页内容无关
主要有:浏览器位置、大小、重设大小、定时器、系统对话框、几大对象属性
(1)window对象:表示浏览器的一个实例,浏览器中的window双重角色即是全局对象,又是浏览器实例即通过它访问浏览器
删除delete区别等(window.name name)
如果页面中包含框架,则每个框架都拥有自己的 window 对象,并且保存在 frames 集合中。在 frames集合中,可以通过数值索引(从 0 始,从左至右,从上到下)或者框架名称来访问相应的 window 对象
// 系统对话框:alert()、confirm()和 prompt()方法可以调用系统对话框向用户显示消息
// 了解top、self、window、parent等
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset rows="160,*">
<frame src="frame.htm" name="topFrame">
<frameset cols="50%,50%">
<frame src="anotherframe.htm" name="leftFrame">
<frame src="yetanotherframe.htm" name="rightFrame">
</frameset>
</frameset>
</html>
// 页面视口的大小
var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
if (document.compatMode == "CSS1Compat"){
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else {
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
// 浏览器目前窗口大小
window.innerWidth、 window.innerHeight、 window.outerWidth 和 window.outerHeight。
// 窗口位置
window.screenLeft 和 window.screenTop 属性,分别用于表示窗口相对于显示屏幕左边和上边的位置
// 更改浏览器大小(宽高)
resizeTo()
// 导航与打开窗口
window.open() 要加载的 URL、窗口目标、一个特性字符串以及一个表示新页面是否取代浏览器历史记录中当前加载页面的布尔值(即打不打开新窗口)
window.open("http://www.wrox.com/","wroxWindow", "height=400,width=400,top=10,left=10,resizable=yes");
// 间歇执行和超时调用
var timeoutId=setTimeout(function() { // 表示再过多长时间把当前任务添加到队列
alert("Hello world!");
}, 1000);
clearTimeout(timeoutId);// 清除定时器
let timeoutId=setInterval (function() { // 间隔10秒执行一次
alert("Hello world!");
}, 10000);
clearInterval(timeoutId) // 清除间隔定时器
(2)location对象:获取跟url信息相关的信息(window.location 和 document.location引用的同一个对象,既是window对象属性又是文档对象属性)可以取到前窗口中加载的文档有关的信息,还提供了一些导航功能
例如:http://w3.opc.dev.wormpex.com/shop-admin#/roster/storerbac
hash #及后的部分,无’’ // #/roster/storerbac
pathname “/WileyCDA/” url中目录或文件名 // /shop-admin
search “?q=javascript” 返回URL的查询字符串。这个字符串以问号开头. (若是路由实现的用路由的方法获取)
// window.location = “http://www.wrox.com”; location.href = “http://www.wrox.com”; 浏览器打开url
location.reload(); //重新加载(有可能从缓存中加载)
location.reload(true); //重新加载(从服务器重新加载),放在代码最后一行好,否则可能导致后的代码不执行
⚠️ :每次修改location的 属性,页面会以新的url重新加载
(3)navigator对象:用于查看网页所在浏览器的相关信息
检测插件、注册处理程序
(4)screen对象:包括浏览器窗口外部的显示器的信息,如像素宽度和高度
(5)history对象:用户上网的历史记录,从窗口被打开的那一刻算起
// history 对象还有一个 length 属性,保存着历史记录的数量
history.go(-1);
//前进一页
history.go(1);
//前进两页
history.go(2);
history.back();
history.forward();
九、第九章客户端检测:一般是为了解决浏览器差异的一些补救措施(能 找到通用的方法就不要使用客户端检测)
(1)能力检测(特性检测:通过判断浏览器的特性来执行功能模块,浏览器是否有某种能力)
1、通过判断浏览器有无某方法,来进行逻辑处理
function getElement(id){
if (document.getElementById){ // IE5.0 之前的版本不支持
return document.getElementById(id);
} else if (document.all){
return document.all[id];
}
else {
throw new Error("No way to retrieve element!");
}
}
// 更可靠的检测方法,使用typeof进行能力检测,因为判断存不存在,并不能知道它是否是一个方法,会不会按照我们预想的进行操作
function hasCreateElement(){ //在 IE8 及之前版本中不行
return typeof document.createElement == "function";
}
(2)怪癖检测:想知道浏览器有什么缺陷 (特殊行为,不一样的表现)
(3)用户代理检测:确定不同 的浏览器,通过检测用户代理字符串来识别浏览器
十、Dom:文档对象模型,是针对 HTML 和 XML 文档的一个 API,描绘了一个层次化的节点树,允许开发人员新增、删除、修改页面的某一部分
1、节点层级
(1)Node 类型:所有节点类型都继承自 Node 类型,因此所有节点类型都共享着相同的基本属性和方法 例如nodeType属性、 nodeName 和 nodeValue等
if (someNode.nodeType == 1){ //适用于所有浏览器
alert("Node is an element.");
}
#每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象(是伪数组),基于 DOM 结构动态执行查询的结果
#每个节点都有一个 parentNode 属性,指向父节点(使用时可以查阅节点的访问,第一个、最后一个、有无子节点,是否是第一个最后一个子节点等)
#所有节点都有的最后一个属性是 ownerDocument,该属性指向表示整个文档的文档节点
// 以上是Node有的属性和方法,虽然所有节点继承自Node,但不是所有节点都有子节点,使用时深入了解
// 操作方法 插入节点、移除节点、
(1) appendChild(newNode)用于向它的 childNodes 列表的末尾添加一个节点。即放的是最后子(新节点可以是原文档存在的也可新创建的)
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true
insertBefore(newNode, someNode.firstChild) 插入的节点 参照的节点
(2)replaceChild(newNode, someNode.firstChild) 替换节点 插入节点和替换节点
(3)removeChild(removeNode) 移除节点 参数要移除的节点
(4)cloneNode()克隆一个完全相同的副本 myList.cloneNode(true) 参数表示深复制还是浅复制。复制后需要将节点使用上面方法插入,否则仅是文档所有没有在节点树中
(5)normalize() 解决文本节点的问题 例如空文本节点或重复的
(2)Document 类型:表示文档,document就是它的实例,也是window的属性
// 获取文档信息
document.documentElement // 获取html节点
document.body // 获取body节点
document.title // 获取设置文档标题
document.URL // 完整url,同location.href
document.domain // 域名,同location.hostname
document.referrer // 取得来源页面的url,无上页就是空串
// 查找元素
document.getElementById("myDiv")
document.getElementsByTagName("img") // 0或多项nodeList
document.getElementsByName("color") // 通过name属性获取
(3)Element 类型
document.createElement("div") // 创建新元素
(4)Text类型:文本
2、Dom操作技术
可以获取节点修改节点属性、样式等
十一、第十一章Dom扩展
1、选择符API
querySelector() // 不分是id还是tagName,可以直接写。返回匹配的node元素
document.querySelector("body")
document.querySelector("#myDiv")
document.querySelector(".selected")
querySelectorAll() // 同querySelector但返回的是nodeList
2、元素遍历
childElementCount:返回子元素(不包括文本节点和注释)的个数。
firstElementChild:指向第一个子元素;firstChild 的元素版。
lastElementChild:指向最后一个子元素;lastChild 的元素版。
previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。
3、HTML5扩展
getElementsByClassName() // 返回NodeList,还提供增删改类型的操作
button.focus() // 手动获取焦点
document.readyState == "complete" // 判断文档是否加载完毕
document.compatMode == "CSS1Compat" // 检查渲染页面的模式是标准模式还是混杂模式
document.head // 获取头部标签的另一种方式
document.charset // 设置文档的字符集
document.defaultCharset // 查询浏览器默认的字符集
// html5规定可以为元素添加自定义的属性(即不是html规范中提供的),要添加前缀 data-
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
var div = document.getElementById("myDiv");
//取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;
//设置值
div.dataset.appId = 23456;
div.dataset.myname = "Michael";
// 插入标记
innerHTML 属性返回开始闭合标签中的内容,返回的是字符串
div.innerHTML = "Hello world!";
div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
// 如何滚动页面也是dom没有
scrollIntoView()方法通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中
scrollIntoViewIfNeeded(alignCenter)
scrollByLines(lineCount)
scrollByPages(pageCount)
4、专有扩展
children属性,同childNodes
contains() // 判断某个节点是不是另一个的后代
alert(document.documentElement.contains(document.body)); //true body是html的后代吗
innerText 属性,插入文本
十二、第十二章Dom2和Dom3
1、Dom变化:Dom2、Dom3在1的基础上提供了更多的交互。1很多都是简单的获取节点,拿节点属性、增删改等。2、3增加交互、修改等属性和方法
2、样式:定义样式的3中形式:link、style元素嵌入样式、style属性定义行内样式
(1)访问元素的样式
var myDiv = document.getElementById("myDiv"); // 获取dom元素
//设置背景颜色
myDiv.style.backgroundColor = "red";
//改变大小
myDiv.style.width = "100px";
myDiv.style.height = "200px";
// 获取样式
myDiv.style.backgroundColor //"blue"
// 获取计算样式
getComputedStyle()方法
(2)操作样式表:获取规则等,使用的是提供的属性和方法
过 document.styleSheets
(3)元素大小
十三、第十三章事件(文档或浏览器窗口发生的交互事件)js与html的交互是通过事件进行的
(1)事件流:描述页面中接收事件的顺序。捕获阶段、
事件冒泡:由点击元素逐级向父节点冒泡,触发对应的事件
// 所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
(1) <div>
(2) <body>
(3) <html>
(4) document
事件捕获:最具体的节点应该最后接收到事件,即事件到达预定目标之前捕获
// document
// html
// body
// div
(2)事件处理程序:给事件添加的函数,函数中有一个局部变量event(事件对象)
1、DOM0 级事件处理程序,通过事件名前加on进行事件的绑定(this指向当前元素 )
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};
btn.onclick = null;
2、DOM2 级事件处理程序,addEventListener('click',事件处理函数,是否捕获,默认不是)与removeEventListener(),可添加多个处理函数,按添加顺序触发(注意匿名函数的无法移除)
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.removeEventListener("click", handler, false); //有效,添加和移除必须同一函数时有效,所以将函数单独提出来定义,匿名函数移除无效,因为实质是不同的韩式
3、IE事件处理程序 attachEvent()和 detachEvent(),只支持冒泡,事件处理程序会在全局作用域中运行(this指向window),添加多个同名事件函数已相反的顺序执行
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
btn.detachEvent("onclick", handler); //. 删除时必须提供同一函数
4、跨浏览器的事件处理程序:自己封装添加事件的方法,似的兼容不同的浏览器(就是能力检测)
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
};
// 调用,需要考虑作用域问题及添加同个多事件问题
var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//这里省略了其他代码
EventUtil.removeHandler(btn, "click", handler);
(3)事件对象:触发 dom事件时会产生事件对象event,对象中包含所有与事件有关的信息
// 1、dom中的事件对象
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){ // event.currentTarget === this this始终指正在处理事件绑定的那个元素
alert(event.type); //"click"
}, false);
//只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为。另外,stopPropagation()方法用于立即停止事件在 //DOM 层次中的传播,即取消进一步的事件捕获或冒泡
// 2、ie中的事件对象
若是通过DOM0添加,事件是window对象的属性
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
};
通过ie自己的方法用 attachEvent实现,既可以window访问也可以直接传event
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
alert(event.type); //"click" // window.event也可以
});
注意:
event ? event : window.event;
event.target || event.srcElement;
event.preventDefault()|| event.returnValue = false;
(4)事件类型:
UI事件:用户与页面交互时触发
// 1、load事件 当页面完全加载后(包括所有图像、JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件
<body onload="alert('Loaded!')"> // 例如实现给dom添加新元素就需页面加载完毕后添加否则报错
</body>
或给window绑定load事件,跟其他事件的实现一致
<img src="smile.gif" onload="alert('Image loaded.')"> // 图像加载完后执行的操作
// 2、 unload 事件 文档被完全卸载后触发(用户从一个页面切换到另一个页面就会触发),一般用来清楚引用,避免内存泄漏
EventUtil.addHandler(window, "unload", function(event){
alert("Unloaded");
});
<body onunload="alert('Unloaded!')">
// 3、 resize 事件(浏览器窗口大小的变化触发)。大多浏览器会变化1像素就触发,随着变化不断触发,此事件中不应加入大量的计算
EventUtil.addHandler(window, "resize", function(event){
alert("Resized");
});
// scroll事件 在文档滚动时不断触发
焦点事件:元素获得或失去焦点时触发
// 1、 blur 失去焦点触发,不冒泡,所有浏览器支持
// 2、 focus 获得焦点时触发,不冒泡,所有浏览器支持
// 3、focusin:在元素获得焦点时触发。同focus 但它冒泡。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。
// 4、ocusout:在元素失去焦点时触发。同blur但不冒泡 。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。
鼠标与滚轮事件:用户通过鼠标在页面上执行操作时触发(鼠标事件都是在特定位置上发生的,信息保存在事件对象的clientX与clientY)
clientX与clientY是相对可视口的。 pageX 和pageY 是相对页面的,没滚动其实与clientX、clientY相等。过 screenX 和 screenY电脑屏幕
pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
event.button // 0鼠标左键1中间 2 右 (mousedown 和 mouseup事件event有button属性)
// 1、click 单击事件(鼠标左键或键盘的回车触发)
// 2、dblclick 双击事件(鼠标左键)
// 3、mousedown 按下任意鼠标键触发
// 4、mouseenter 鼠标光标从外部元素移入时触发(移到后代不触发),不冒泡
// 5、mouseleave 鼠标移出元素触发(移到后代不触发),不冒泡
// 6、mousemove 鼠标在内部元素移动时触发
// 7、mouseout 移到另一元素时触发(可以是子元素)
// 8、mouseover 移入元素触发
// 9、mouseup 释放鼠标按钮时触发
// 10、mousewheel 鼠标滚轮事件
EventUtil.addHandler(document, "mousewheel", function(event){
event = EventUtil.getEvent(event);
alert(event.wheelDelta);
});
⚠️ :ios及安卓的不支持鼠标的设备,在safari开发时注意
不支持 dblclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。
两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheel 和 scroll 事件等
键盘与文本事件:输入文本或通过键盘在页面上执行操作
键码:在键盘事件触发后,事件对象的keyCode 属性会包含键码,每个键都有对应的键码,从而可以知道按的哪些键
// 1、keydown,用户按下任意键时触发
// 2、keypress,用户按下字符键时触发
// 3、keyup,当用户释放键盘上的键时触发
// 4、textInput 文本输入框(以上事件一般都是在文本框中会调用),用户在可编辑的区域输入字符就会触发
复合事件:dom3新增,用于处理IME,可以让用户输入物理键盘上找不到的字符
变动事件:dom2上的变动事件(实现增删改节点)removeChild、appendChild()、replaceChild()或 insertBefore等
HTML5事件:
设备事件:智能手机和平板电脑
触摸与手势事件:方便即没有鼠标有没有键盘的手机、平板操作
// 有特定的个 Touch 对象包含相关信息
touchstart
touchmove
touchend
gesturestart
gesturechange
gestureend
(5)内存和性能:事件处理程序都是函数,都会占用内存,占用越多性能越差,指定的越多访问dom次数越多,会延迟页面交互就绪时间
// 1、解决事件处理程序过多问题=>事件委托(原理事件冒泡)。只指定一个事件处理函数管理一类型的所有事件
// 原理在dom树中尽量高层级添加,,这样里面的子元素触发后一级一级冒泡到外层从而触发
// 2、移除事件处理程序,释放内存btn.onclick = null
(6)模拟事件:用js创建event模拟事件,用时再查
十四、表单脚本:js最初是为了分担服务器处理表单的责任,进行表单验证、处理默认行为等。这里只要讲原生html表单开发方式,复制等
目前项目中表单的实现是通过组件库element、ant或react-native原生组件实现的。复制、粘贴、剪切通过clipboard 实现(剪切板事件)
十五、第十五章使用canvas绘图:可以在页面中设定一个区域(画布)进行2D、3D绘图,利用canvas提供的API,但需考虑浏览器的支持性(使用js脚本语言进行绘制图形)
1、基本用法
(1)创建画布,规定画布的大小,开始结束标签中的内容是后备信息,当浏览器不支持时显示,默认没边框
<canvas id="drawing" width=" 200" height="200">drawing something</canvas>
(2)获取上下文,调用元素的getContext('2d')
var drawing = document.getElementById("drawing");
if (drawing.getContext){
var context = drawing.getContext("2d");
}
// 补充:toDataURL可以导出在<canvas>元素上绘制的图像
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
//取得图像的数据 URI
var imgURI = drawing.toDataURL("image/png");
//显示图像
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
}
2、2D上下文
//(1)填充和描边 fillStyle 和 strokeStyle,设置后所有涉及的填充描边都是这两个直至重新设
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
// (2)绘制矩形fillRect()、strokeRect()和 clearRect() x,y坐标 宽高
绘制填充矩形:fillRect()与fillStyle结合
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50); // 根据画布0,0位置定位 坐标起始位置
//绘制半透明的蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
绘制描边矩形:strokeRect与strokeStyle结合
//绘制红色描边矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10, 10, 50, 50);
//绘制半透明的蓝色描边矩形
context.strokeStyle = "rgba(0,0,255,0.5)";
context.strokeRect(30, 30, 50, 50);
// 描边线条的宽度由 lineWidth属性控制、lineCap、lineJoin(绘制线条使用)
清除矩形
context.clearRect(40, 40, 10, 10);
// (3)绘制路径:先必须调用 beginPath()表示开始绘制新路径
arc(x, y, radius, startAngle, endAngle, counterclockwise)绘制弧线。以x y为圆心,半径、起始结束角度(弧度),是都逆时针
arcTo(x1, y1, x2, y2, radius) 从上一点开始绘制弧线到x2 y2
bezierCurveTo(c1x, c1y, c2x, c2y, x, y):从上一点开始绘制一条曲线,到(x,y)为
止,并且以(c1x,c1y)和(c2x,c2y)为控制点。
lineTo(x, y):从上一点开始绘制一条直线,到(x,y)为止
moveTo(x, y):将绘图游标移动到(x,y),不画线
quadraticCurveTo(cx, cy, x, y):从上一点开始绘制一条二次曲线,到(x,y)为止,并
且以(cx,cy)作为控制点
rect(x, y, width, height) 绘制矩形,这个方法绘制的是矩形路径
路径完成后想填充调用fill()方法,想描路径边调用stroke()
路径关闭前确认某一点是否在画布上:context.isPointInPath(100, 100)返回布尔值
//(4)绘制文本:fillText()和 strokeText()
font 文本样式 大小 字体 context.font = "bold 14px Arial";
context.textAlign = "center"; 文本对齐方式 start end center
context.textBaseline = "middle";基线 top bottom middle
context.fillText("12", 100, 20); 文本 x,y坐标
//(5)变换
rotate(angle) 围绕原点旋转angle弧度
scale(scaleX, scaleY)在x,y方向缩放
translate(x, y):将坐标原点移动到(x,y)
transform(m1_1, m1_2, m2_1, m2_2, dx, dy)
setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy)
//(6)绘制图像:drawImage()将图像绘制在画布上
var image = document.images[0];
context.drawImage(image, 50, 10, 20, 30) 图像,起始点 目标大小
// (7)阴影
shadowColor:用 CSS 颜色格式表示的阴影颜色,默认为黑色
shadowOffsetX:形状或路径 x 轴方向的阴影偏移量,默认为 0
shadowOffsetY:形状或路径 y 轴方向的阴影偏移量,默认为 0
shadowBlur:模糊的像素数,默认 0,即不模糊
// (8)渐变
var context = drawing.getContext("2d");
var gradient = context.createLinearGradient(30, 30, 70, 70); // 从x=>y渐变
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
//(9)模式:重复的图像
var image = document.images[0],
pattern = context.createPattern(image, "repeat");
//绘制矩形
context.fillStyle = pattern;
context.fillRect(10, 10, 150, 150);
//(10)使用图像数据:过 getImageData()取得原始图像数据,可以截取部分片段
// (11)合成
//重置全局透明度
context.globalAlpha = 0; 值0-1
context.globalCompositionOperation 表示后绘制的图形怎样与先绘制的图形结合。这个
属性的值是字符串
3、WebGL(3D上下文,大多浏览器还不支持)
十六、第16章HTML5脚本编程:html新增一些html及jsAPI,简化创建动态web界面的工作
html5新增标签:页面结构标签、多媒体标签audio video、列表表单类型等增加了新属性、拖拽drag等
(1)跨文档消息传递:不同域的页面间传递消息
postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串
var iframeWindow = document.getElementById(“myframe”).contentWindow;
iframeWindow.postMessage(“A secret”, “http://www.wrox.com”);
会触发 window 对象的 message 事件(接收方收到消息时)data origin source
(2)原生拖放:默认情况下,图像、链接和文本是可以拖动的,其他需要添加draggable属性
// 拖拽开始触发的事件
dragstart
drag
dragend
// 当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生
(1) dragenter
(2) dragover
(3) dragleave 或 drop
(3)媒体元素:是和音频视频,src属性必填,可以设置大小,自动播放等属性及事件
(4)历史状态管理:
十七、第17章 错误处理与调试:js报错一般不发生在界面,控制台也不会具体说明报错的上下文,不好定位
(1)浏览器报告的错误
IE:报错时左下角出现黄色的图标,会告诉我们报错的信息。或者可以设置页面有错时弹出对话框(自动弹出)
Firefox:会将错误记录在控制台(为调试 JavaScript、CSS、DOM 和网络连接错误提供了诸多功能)
Safair:windows和mac os平台的默认浏览器。通过设置浏览器让在控制台展示错误(Show develop menu in menubar)
Opera:设置错误在控制台(Error Console)
Chrome:错误展示在控制台
(2)错误处理(客户端应用程序错误处理)
// 1、try-catch,常进行JSON.parse的处理,合理使用try-catch,在明知会抛错使用不合适,例如函数参数类型不一样导致错误时,不应try而应判断类型
try{
// 可能会导致错误的代码
} catch(error){
// 在错误发生时怎么处理
console.log(error.message)
}
eg、、
let content = '';
try { // 发生错误就会立即退出代码(不执行下面赋值语言)执行过程到catch块
const res = JSON.parse(params);
content = res;
} catch (e) { // 会接收包含错误信息的对象,因浏览器而异,但e.message保存着错误消息(跨浏览器时最好只使用message,)
content = '';
} finally{
// 始终会执行的语句,,一般有了catch就可以了,不必这三个都有,catch及finally一个就可,IE早期版本对finally执行存在差异
}
// 错误类型(7种错误类型)
(1)Error 基本型,其他错误类型都继承自该类型,一般少见,有也是浏览器抛出的。该类型目的供开发人员抛出自定义类型。
(2)EvalError 使用eval()函数时发生异常抛出的错误。例如没有直接将eval当成函数调用时new eval()或eval=foo等。(一般不用)
(3)RangeError 数值超出范围时抛错。 new Array(-20)或 new Array(Number.MAX_VALUE等
(4)ReferenceError:引用错误。 在找不到对象的情况下会报ReferenceError。经常发生未定义就给另一变量赋值,未定义变量中取值等
(5)SyntaxError:语法错误,将语法错误的js字符串传入eval()时就会抛此错。eval("a ++ b")。其他语法错误会直接导致js立即停止执行所以一般不会报这个错误
(6)TypeError:最常见。在变量保存意外类型、访问不存在的方法(由于执行特定类型操作时不符合要起所致),例如不是数组执行数组方法等
(7)URIError:使用encodeURI()或 decodeURI()时uri格式不正确时
try {
someFunction();
} catch (error){
if (error instanceof TypeError){
//处理类型错误
} else if (error instanceof ReferenceError){
//处理引用错误
} else {
//处理其他类型的错误
}
}
// 2、抛出错误:throw,用于抛出自定义错误,须指定一个值,值类型没要求。在遇到throw 操作符时,代码会立即停止执行,仅当try catch捕获到是才继续(因为catch就是捕获到错误才执行)
throw '类型错误'
throw new Error("Something bad happened."),项目中常使用throw new Error(err),用于抛错,让catch也能捕获到
throw new SyntaxError("I don’t like your syntax.");
throw new TypeError("What type of variable do you take me for?");
throw new RangeError("Sorry, you just don’t have the range.");
throw new EvalError("That doesn’t evaluate.");
throw new URIError("Uri, is that you?");
throw new ReferenceError("You didn’t cite your references properly.");
// 3、错误(error)事件:任何没有通过try catch 处理的事件都会触发window对象的error事件,但错误事件没有event事件对象,浏览器对发生错误的处理不同有的会继续执行有的会立刻停止
window.onerror = function(message, url, line){
alert(message);
return false; // 可以阻止浏览器报告默认错误的行为
};
// 4、处理错误的策略:即编写兼容错误的代码,尽量让网页可以正常展示,避免错误导致网页无法使用
// 5、常见的错误类型:常见的可能发生错误的代码
(1)类型转换错误:使用== === !== !=== if for、while时不是布尔值时,抛错,因此事先转换或判断类型
(2)数据类型错误:常出现不是预期的类型执行不存在的方法
(3)通信错误:发生在前后端交互,可能传的url错误或没有使用encodeURIComponent等转换,或请求返回错误
function addQueryStringArg(url, name, value){ // 查询参数的拼接。a=1&b=3
if (url.indexOf("?") == -1){
url += "?";
} else {
url += "&";
}
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
// 6、区分致命错误和非致命错误:看是否是主要功能,可以恢复吗等。若无法运行或影响主要操作或会引发连带错误
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init(); // 否则任何模块出错都会引发后续无法进行
} catch (ex) {
//在这里处理错误
}
}
// 7、把错误记录到服务器:推荐将js错误回写到服务器
function logError(sev, msg){
var img = new Image();
img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" +
encodeURIComponent(msg);
}
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex){
logError("nonfatal", "Module init failed: " + ex.message);
}
}
(3)调试技术
// 1、将消息记录在控制台 console
console.log()
console.error()
console.info()
console.warn()
// 2、将消息记录在当前页面:给页面动态增加元素,用于显示消息
function log(message){
var console = document.getElementById("debuginfo");
if (console === null){
console = document.createElement("div");
console.id = "debuginfo";
console.style.background = "#dedede";
console.style.border = "1px solid silver";
console.style.padding = "5px";
console.style.width = "400px";
console.style.position = "absolute";
console.style.right = "0px";
console.style.top = "0px";
document.body.appendChild(console);
}
console.innerHTML += "<p>" + message + "</p>";
}
// 3、抛出错误
function divide(num1, num2){ // 避免非数值计算NaN
if (typeof num1 != "number" || typeof num2 != "number"){
throw new Error("divide(): Both arguments must be numbers.");
}
return num1 / num2;
}
(4)常见的IE错误:IE通常是最难调试
操作终止、无效字符、未找到成员、未知运行时错误、语法错误、系统无法找到指定资源
十八、第18章JavaScript与XML
了解HTML与XML的区别
HTML 超文本标记语言,用于创建静态页面,展示数据而不传输数据
XML 可扩展标记语言,用于传输数据
十九、第19章E4XL:ECMAScript的扩展,是为操作 XML数据提供与标准 ECMAScript 更相近的语法
二十、第20章JSON:它是一种数据格式,不从属js,很多编程语言使用,也有针对JSON的解析器和序列化器。
(1)语法
// 1、简单值:使用与 JavaScript 相同的语法,不支持undefined,JSON的字符串只能双引号包(单引号不可报错),否则会报错
5 "Hello world!" null true
// 2、对象:JSON 中的对象要求给属性加引号(没有声明变量,没有末尾分号,因为不是js语句)
{
"name": "Nicholas",
"age": 29
}
// 3、数组:没有变量和分号
[25, "hi", true]
(2)解析与序列化:可以将JSON 数据结构解析为有用的 JavaScript 对象
// 1、JSON对象的两个方法:stringify()和 parse()
// (1)将js对象序列化为JSON字符串。 JSON.stringify(),默认情况输出的JSON串不包含任何空格字符或缩进
var book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book);
{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,
"year":2011}
// (2)JSON字符串转为js JSON.parse(),所有函数及原型成员都会被有意忽略,不体现在结果中,undefined也会跳过
JSON.parse(jsonText); // 容易报错,一般执行该语句放在try catch中
// (3)给JSON.stringify()传入第二个参数
若是数组则只会过滤出对象中数组中的属性转为JSON字符串
若是函数,参数键值对形式,进行处理
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, function(key, value){
switch(key){
case "authors":
return value.join(",")
case "year":
return 5000;
case "edition":
return undefined;
default:
return value;
}
});
{"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}
(4)JSON.stringify()方法的第三个参数用于控制结果中的缩进和空白符
// 2、序列化选项:可以给JSON.stringify()传参数。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, ["title", "edition"]); // 数组时表示只包含数组中列出的属性{"title":"Professional JavaScript","edition":3}
// 函数时根据函数的key看返回什么
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, function(key, value){
switch(key){
case "authors":
return value.join(",")
case "year":
return 5000;
case "edition":
return undefined; // 会略过
default:
return value;
}
});
{"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}
// 第3个参数,控制结果中的缩进和空白符
// 3、解析选项:可以给JSON.parse()传参数,该参数是一个函数(还原函数),不常用
二十一、第21章Ajax与Comet:XMLHttpRequest 对象,用 XMLHttpRequest 事件,跨域 Ajax 通信的限制(用于交互,摆脱点击、等待,以异步的方式从服务器获取数据)
ajax:异步JavaScript + XML
ajax的核心是XMLHttpRequest 对象
// XMLHttpRequest 对象,ajax5步走进行页面交互,拿到数据,将数据插入html页面,实现了不需要刷新页面的情况下实现交互
// 跨域问题:通过 XHR(XMLHttpRequest) 实现 Ajax(实现不用刷新页面的通信) 通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。
(1)CORS跨源资源共享,定义了在访问跨域资源时,浏览器与服务器间如何沟通(使用自定义的http头部,让浏览器与服务器沟通),需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应
Origin: http://www.nczonline.net
如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源
信息(如果是公共资源,可以回发"*")。例如:
Access-Control-Allow-Origin: http://www.nczonline.net
// IE对CORS的实现,引入XDR,类似XMLHttpRequest的使用
var xdr = new XDomainRequest();
xdr.onload = function(){ // 返回内容在原始文本中responseText
alert(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
// 其他浏览器对CORS的实现,都通过 XMLHttpRequest对象实现了对 CORS 的原生支持, open()方法中传入绝对 URL
xhr.open("get", "http://www.somewhere-else.com/page/", true);
注意:默认情况下跨域请求不提供凭据(cookie、Http、ssl认证),可以通过将withCredentials 属性设置为 true,若服务器接收带凭据的请求,会在http的头部Access-Control-Allow-Credentials: true响应,若不支持,responseText 中将是空字符串
(2)其他跨域技术
// 图像Ping,利用img标签,一个网页可以从任何网页中加载图像,不用担心跨域问题
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";
// JSONP:应用JSON的一种新手法,两部分回调函数和数据,回调函数就是响应回来时页面执行的函数,一般在请求中指定,数据就是传入函数的JSON
http://freegeoip.net/json/?callback=handleResponse,利用动态script标签的src属性实现
function handleResponse(response){ //能够直接访问响应文本
alert("You’re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);
// Comet:更高级的 Ajax 技术。长轮询和短轮询(就是立即响应还是待有数据时响应,一直保持连接状态)XHR 对象和 setTimeout()实现
// 服务器发送事件SSE
// Web Scokets:使用的是Web Socket 协议(而不是Http协议),必须支持这种协议的服务器才能正常工作
var socket = new WebSocket("ws://www.example.com/server.php"); // 必须是绝对的url
socket.send("Hello world!");
// 有响应的状态及事件
socket.onopen = function(){
alert("Connection established.");
};
socket.onerror = function(){
alert("Connection error.");
};
socket.close();
二十二、第22章高级技巧
(1)高级函数(使用函数的高级方法)
// 1、安全的类型检测 typeof、instanceof(多个frame时不适用)
Object.prototype.toString.call(value) == "[object Array]"
Object.prototype.toString.call(value) == "[object Function]"
Object.prototype.toString.call(value) == "[object RegExp]"
// 2、作用域安全的构造函数,当构造函数没有使用new调用,this指向问题,直接调用构造函数时指向window,先确认 this 对象是正确类型的实例。如果不是,那么会创建新的实例并返回
function Person(name, age, job){ // 都会返回它的实例,避免在全局等环境中添加额外的属性
if (this instanceof Person){ // 如果this指向的对象不是构造函数的的实例,说明不是new调用
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name, age, job);
}
}
var person1 = Person("Nicholas", 29, "Software Engineer");
alert(window.name); //""
alert(person1.name); //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby"
// 3、惰性载入函数:因为浏览器兼容的不同,每次函数功能封装都判断浏览器的支持性,每次调用前都判断。这样更多次的判断会导致慢
// 判断一次就够了,如果浏览器支持那就一直支持了,惰性载入表示函数执行的分支仅会发生一次
方式1 通过判断后重写函数,这样以后就根据需要执行新赋值的函数了
方式2 在声明函数时就指定适当的函数(根据判断返回指定的函数,只是第一次时调用,拿到函数,最好使用匿名自执行函数) ()()
// 4、函数绑定
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message + ":" + event.type);
}
};
function bind(fn, context){
return function(){
return fn.apply(context, arguments);
};
}
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
或
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler)); // 加bind是因为没有保存handler.handleClick的环境
// 5、函数柯里化:使用一个闭包返回一个函数
function curry(fn){
var args = Array.prototype.slice.call(arguments, 1);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}
(2)防篡改对象(一旦把对象设为防篡改就无法撤销了)
(1)不可扩展对象
Object.preventExtensions(person); // person对象是不可扩展对象,不可动态的再添加属性和方法。严格下再添加抛错,非严格undefined
Object.istExtensible() 判断对象是否可扩展
(2)密封的对象:密封对象不可扩展且已有成员的[[Configurable]]特性将被设置为 false,不可删除。属性值是可以修改的
Object.seal(person);
Object.isSealed() 判断是否为密封对象
(3)冻结的对象:即不可扩展又是密封对象 ,[[Writable]]特性会被设置为 false
Object.freeze(person);
Object.isFrozen()
(3)高级定时器:定时器只是到达规定的时间后添加到队列,待js主线程空闲时按序在进行执行(何时将代码添加到队列)
setTimeout()
setInterval() ::(1) 某些间隔会被跳过;(2) 多个定时器的代码执行之间的间隔可能会比预期的小。某个 onclick 事件处理程序使用 setInterval()设置了一个 200ms 间隔的重复定时器。如果事件处理程序花了 300ms 多一点的时间完成,同时定时器代码也花了差不多的时间,就会同时出现跳过间隔且连续运行定时器代码的情况.解决:链式setTimeout(),函数执行时再创建一个新的定时器
setTimeout(function(){
//处理中
setTimeout(arguments.callee, interval);
}, interval);
// 运行在浏览器的js都被分配到确定数量的资源防止占用太多资源把用户计算机搞挂,一般脚本运行过久或资源过多会有警告对话框
// 函数节流:连续尝试进行过多的 DOM 相关操作可能会导致浏览器挂起,函数节流是控制某些代码不可以在没有间断的情况连续重复执行
(5)自定义事件:事件是js与浏览器的交互的主要途径,事件是一种叫做观察者的设计模式。主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体
涉及dom时,dom元素是主体,事件处理函数就是观察者
function EventTarget(){
this.handlers = {}; //用于储存事件处理程序
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){ 用于注册给定类型事件的事件处理程序
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function(event){ 用于触发一个事件
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){ 用于注销某个事件类型的事件处理程序
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
function handleMessage(event){ // massage事件的处理函数
alert("Message received: " + event.message);
}
//创建一个新对象
var target = new EventTarget();
//添加一个事件处理程序
target.addHandler("message", handleMessage);
//触发事件
target.fire({ type: "message", message: "Hello world!"});
//删除事件处理程序
target.removeHandler("message", handleMessage);
//再次,应没有处理程序
target.fire({ type: "message", message: "Hello world!"});
(6)拖放
// 将页面指定元素移到鼠标指针的位置
EventUtil.addHandler(document, "mousemove", function(event){
var myDiv = document.getElementById("myDiv");
myDiv.style.left = event.clientX + "px";
myDiv.style.top = event.clientY + "px";
});
二十三、离线应用及客户端存储:离线应用就是设备不能上网的情况下仍然可以运行的应用。
(1)离线检查
navigator.onLine 或window上的online 和 offline事件
if (navigator.onLine){
//正常工作
} else {
//执行离线状态时的任务
}
(2)应用缓存:使用离线缓存,专门为开发离线web应用设计的
(3)数据存储:在浏览器中保存数据
cookie(document.cookie 可以访问 cookie) 、本地存储、会话存储
二十四、最佳实践
(1)可维护性:可理解性、直观性、可适应性、可扩展性、可调试性
函数和方法应该有注释,描述功能
用于描述单个任务的多行代码前面增加注释
复杂的算法需要解释如何做的
变量名和函数名尽量语义话,变量名名词,函数名动词,布尔值is开头等
给变量赋值初始值或备注,从而知道是什么类型的值
避免全局量
不用给实例或原型增加属性等
不要重定义已存在的方法
(2)性能
注意作用域避免全局查找,因为访问全局变量比局部变量慢很多(因为涉及作用链查找慢)var doc = document; 可以变成局部变量
避免with语句:with语句会创建自己的作用域,会增加作用域链的长度(with语句主要用于消除额外的字符,大多可以用局部变量解决)
function updateBody(){
with(document.body){
alert(tagName);
innerHTML = "Hello world!";
}
}
// 替换以上代码
function updateBody(){
var body = document.body
alert(body.tagName);
body.innerHTML = "Hello world!";
}
避免不必要的属性查找:因为js的算法复杂度决定时间的长短
优化循环:减值迭代、简化终止条件、简化循环体、使用后测试循环、避免双重解释、原生方法较快、Switch 语句较快、位运算符较快
最小化语句数:语句越少越快(可将声明变量放在一起)let count=5,color
插入迭代值:尽可能的合并语句。var name = values[i];i++; => var name = values[i++];
使用数组和字面量(不用构造函数创建,否则会导致有更多的语句):可以用一句语句创建和初始化数组或对象
优化dom交互:最耗费性能,因为需要重新渲染页面或一部分。最小化现场更新、 使用 innerHTML
var list = document.getElementById("myList"),
fragment = document.createDocumentFragment(),
item,
i;
for (i=0; i < 10; i++) {
item = document.createElement("li");
fragment.appendChild(item);
item.appendChild(document.createTextNode("Item " + i));
}
list.appendChild(fragment);
// 优化var list = document.getElementById("myList"),
html = "",
i;
for (i=0; i < 10; i++) {
html += "<li>Item " + i + "</li>";
}
list.innerHTML = html;
使用事件代理:如果多个操作使用相同的功能,利用冒泡的原理给给相同的祖元素添加事件
(3)部署:部署到web应用的过程,开发环境走出来并进入web阶段
构建过程:不应该将自己写的代码原封不动放入浏览器中,因涉及知识产权问题(别人再利用,找安全漏洞)、文件大小(浏览器不会从原始代码额外的空白字符、冗长的函数名、变量名有什么区别或好处)、代码组织问题(将js分类存放,而不是所有在一个)
所以进行代码构建:在源控制中定义用于存储文件的逻辑结构,一般不是将所有js放在一个文件中等其他逻辑,并将源代码合并成一个或多个归并文件
==验证:==大多数开发人员还是要在浏览器中运行代码以检查其语法,不好排查及移植,很多问题都是在执行代码时遇到,我们使用JSLint可以查找代码中的语法错域及常见的编码错误
eval()的使用、未声明变量的使用、遗漏的分号、不恰当的换行、错误的逗号使用、语句周围遗漏的括号、switch 分支语句中遗漏的 break、重复声明的变量、with 的使用、错误使用的等号(替代了双等号或三等号)、无法到达的代码
==压缩:==代码长度和配重(配重指服务器传送到浏览器的字节数mod_gzip和 mod_deflate进行HTTP压缩)
文件压缩:删除额外的空白、换行,删除所有注释、缩短变量名
HTTP压缩:
二十五、新兴的API:未来要实现的API,动画、地理定位、直接访问计算机中的文件
附录A ECMAScript Harmony:
// const 声明常量,声明后初始赋值后就不能再赋值了,也修改不了
// 块级作用域:使用let的for循环
// 剩余参数(三个点后跟一个标识符)和分布参数(分布参数在调用函数的时候使用,而剩余参数在定义函数的时候使用)
剩余参数可将多余的参数放进数组中,使用剩余参数后,函数默认的参数伪数组arguments不再存在
分布参数:可以向函数传入数组,数组中的每个参数会映射到函数的每个参数上
sum(a,b,c,d,e,f)
var result = sum(...[1, 2, 3, 4, 5, 6])===var result = sum.apply(this, [1, 2, 3, 4, 5, 6]);
// 函数参数默认值:开发人员不用检查是否给某个参数传参了,没传就是默认值(默认值在为undefined时生效)
// 数组、对象解构赋值:[value2, value1] = [value1, value2]例如交换变量
var person = {
name: "Nicholas",
age: 29
};
var { name: personName, age: personAge } = person;
alert(personName); //"Nicholas"
alert(personAge); //29
//对象的新方法
// 类
// 模块
附录B 严格模式:严格模式可以提早知道代码中存在的错误,“use strict”;支持的就按严格模式,不支持就只是一个未赋值的字符串
// 全局作用域中使用,表示整个脚本都使用严格模式
// 在函数中使用严格模式
function doSomething(){
//严格模式下不允许意外创建全局变量,例如未声明直接给变量赋值,不可以对变量调delete操作符,不能使用保留字为变量名
// 对象的操作报错情况多,一般非严格失败的都会报错且不能有重名属性
// 函数的参数命名必须唯一,且arguments的行为也不一致,淘汰arguments.callee 和 arguments.caller,函数名不能保留字
// 抑制this,非严格模式下调用函数的apply、call时null 或 undefined 值会被转换为全局对象。而在严格模式下,函数的 this 值始终是指定的值
// 严格模式抛弃了 with 语句,去除8进制
"use strict";
//其他代码
}
附录C JavaScript库:通用库及专用库
例如:jQuery: JavaScript 提供了函数式编程接口的开源库。它是一个完整的库,其核心是构建于CSS 选择器上的,用来操作 DOM 元素。通过链式调用
附录D JavaScript工具:各种压缩器校验器,提高开发效率
https://www.layuiweb.com/doc/index.htm Layui web组件库
更多推荐
所有评论(0)