《JavaScript高级程序设计》阅读笔记(上)
堪称JavaScript中的必读圣经,又叫红宝书,第一部分主要讲解了语言特性,尤其是关于原型/继承的部分实在精彩

作者 Nicholas C. Zakas
JavaScript简介
- JavaScript 发展史
- 1995年 JavaScript 1.0
- 1997年06月 - ECMAScript 1.0
- 1998年6月 - ECMAScript 2.0
- 1999年12月 - ECMAScript 3.0
- 2008年7月 - ECMAScript 3.1
- 2009年12月 - ECMAScript 5.0
- 2015年6月 - ECMAScript 6.0
- JavaScript 组成
- 核心 ECMAScript
- 文档对象模型 DOM
- 浏览器对象模型 BOM
- 混杂模式(quirks mode), 标准模式(standards mode)和几乎标准模式(almost standards mode)
基本概念
使用可读性更高的注释
1234/** 这是一个多行注释* (块级注释)*/'use strict'使用严格模式编程,有助于培养良好的代码风格- 显式声明变量(这样使用
typeof返回undefined时可知该变量未声明而非未初始化) - 5种基本数据类型
undefined,null,boolean,number,string和一种复杂数据类型object typeof操作符返回undefined,boolean,number,string,object,function- 只有
0除以0会返回NaN, 整数除以0返回Infinity, 负数除以0返回-Infinity 后置型递增(递减)与前置型递增(递减)的区别
var nThree = nOne++ + nTwo相当于var nThree = nOne + nTwo; nOne++var nThree = ++nOne + nTwo相当于nOne++; var nThree = nOne + nTwo
记忆技巧是,前增是赋值前就增加,后增是赋值后才增加将数字字符串转换成
number的方法var nOne = sOne - 0var nTwo = +sTwovar nThree = Number(sThree)
大写字母的字符编码全部小写字母的字符编码
1var result = 'Brick' < 'alphabet' // true数字字符串之间的比较
123var result1 = '23' < '3'; // truevar result2 = '23' < 3; // falsevar result3 = 'a' < 3; // false, 'a' 被转换成了 NaN, 任何数与 NaN 比较均是 falsefor in出来的顺序不可预测(ECMAScript 中对象属性没有顺序)- 在双层嵌套
for循环中使用label,使得可以控制退出到外部的标记for循环 - 禁止使用
with语句 switch语句比较时使用全等操作符,不发生类型转换
变量、作用域和内存问题
- ECMAScript 变量是松散的,包括基本类型值(简单数据段)和引用类型值(对象)
ECMAScript 中所有参数传递的都是值,不可能通过引用传递参数(原书 71 页)
12345678function setName(obj) {obj.name = 'Nicholas'obj = new Object()obj.name = 'Greg'}var person = new Object()setName(person)alert(person.name) // 'Nicholas'使用
instanceof操作符检测对象类型:result = variable instanceof constructor- JavaScript 的垃圾收集方式是 标记清除
IE8 之前的 DOM 与 BOM 对象是以 C++ 实现的 COM 对象,使用引用计数策略回收垃圾(存在循环引用问题)
引用类型
- 传递参数时对必须值使用命名参数,对可选参数使用对象
aOne.length = 'someValue'等同于aOne.push('someValue')ECMAScript 5 中新增了
Array.isArray(value)方法(IE9+)判断是否为数组instanceof操作符假定只有一个全局环境,多框架传递数组时会与各框架中的原生数组具有不同的构造函数Array.toString()与Array.join(',')得到的结果相同- ECMAScript 5(IE9+)为数组定义了5个迭代方法(这些方法均不改变原数组):
every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回truefilter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。forEach():对数组中的每一项运行给定函数。这个方法没有返回值。map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true
- ECMAScript 5(IE9+)为数组定义了2个归并方法:
reduce(),reduceRight() - 在 ECMAScript 3 中,正则表达式字面量始终共享一个
RegExp实例(但浏览器实现与 ES5 相同) - 严格模式下不能使用
arguments.callee toExponential()方法返回以指数表示法(也称e表示法)表示的数值的字符串形式。IE8+ 支持方括号表示法访问个别字符
123var stringValue = 'hello world'alert(stringValue.charAt(1)) // 'e'alert(stringValue[1]) // 'e',不兼容低版本ECMAScript 5 新增了字符串方法
trim()replace('{字符串或正则}', '{替换字符或函数}'),第一个参数参数是字符串时只替换第一个匹配项, 要全局替换必须用正则且指定全局(g), 第二个参数可以附加特殊字符序列进行高级替换
面向对象的程序设计
理解对象
- 数据属性
- Configurable 默认为
true - Enumerable 默认为
true - Writable 默认为
true - Value 默认为
undefined
- Configurable 默认为
- 访问器属性
- Configurable 默认
true - Enumerable 默认
true - Get 默认
undefined - Set 默认
undefined
- Configurable 默认
- 属性前面包含下划线一般表示只能通过对象方法访问的属性:
_property
创建对象
工厂模式——传入参数并返回参数作为值的对象
123456789function createPerson(name, age, job) {var o = new Object()o.name = nameo.sayName = function () {alert(this.name)};return o}var person1 = createPerson('Nicholas', 29, 'Software Engineer')工厂模式不能解决对象识别的问题
构造函数模式
|
|
- 构造函数的问题在于每个实例都要重建一遍,不能完成共享
原型模式
|
|
- 理解原型对象
- ES5 中使用
Object.getPrototypeOf()可以方便地取得一个对象的原型 - 使用
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,常用语for-in中区分原型(IE8存在 Bug,for-in循环中不会出现[不可枚举]的属性:hasOwnProperty(),propertyIsEnumerable(),toLocaleString(),toString()和valueOf())。 - 原型具有动态性,所做修改立刻在所有实例上体现,跟是否已创建实例无关(指针)
- ES5 中使用
其他模式
- 最常用的默认方式:构造函数与原型模式同时使用
- 动态原型模式可以灵活地初始化原型
- 寄生构造函数模式,使用工厂模式的构造函数(尽量不用)
- 稳妥构造函数模式(安全,没有公共属性,不适用
this和new)
继承
原型链
- 所有函数都是 Object 的实例,最终原型指向
Object.prototype - 通过原型链实现继承时,使用对象字面量创建原型方法会重写原型链
- 组合使用原型链和借用构造函数是最常用的继承模式
Object.create()—— 原型式继承
函数表达式
- 函数声明不要放在语句块中(可使用函数表达式)
递归
- S5 严格模式中不能使用
arguments.callee12345678//命名函数表达式实现递归var factorial = (function f (num) {if (num <= 1) {return 1} else {return num * f(num-1)}})
闭包
- 过度使用闭包会导致内存占用过多
- 闭包只能取得包含函数中变量的最后一个值
- 闭包中的
this指向 window 对象(与匿名函数相同) IE8 以下浏览器在闭包中保存了 HTML 元素,导致这个元素将无法销毁
12345678function assignHandler(){ //IE8-中关于HTML元素闭包无法销毁的解决方法var element = document.getElementById('someElement')var id = element.idelement.onclick = function(){alert(id)}element = null}重新声明变量但不赋值将被忽略
BOM
全局变量不能通过
delete操作符删除,而直接在 window 对象上的定义的属性可以,尝试访问 window 上定义的属性不会抛出错误12345// 这里会抛出错误,因为 oldValue 未定义var newValue = oldValue// 这里不会抛出错误,因为这是一次属性查询// newValue 的值是 undefinedvar newValue = window.oldValue跨浏览器取得窗口左边和上边的位置
1234// IE、Safari、Opera 和 Chrome 支持 window.screenLeft// Firefox、Safari 和 Chrome 支持 window.screenXvar leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenXvar topPos = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY跨浏览器获得页面 ViewPort 大小
12var pageWidth = document.documentElement.clientWidth //IE7+var pageHeight = document.documentElement.clientHeight //IE7+使用超时调用来模拟间歇调用是一种最佳模式(不用手动停止调用)
123456789101112var num = 0var max = 10function incrementNumber() {num++//如果执行次数未达到 max 设定的值,则设置另一次超时调用if (num < max) {setTimeout(incrementNumber, 500)} else {alert('Done')}}setTimeout(incrementNumber, 500)location 对象
document.location与window.location引用同一个对象- 赋值给
location.href实际上调用了location.assign()方法,此方式会生成浏览记录 location.replace('http://www.wrox.com/')则不会在历史纪录中留下痕迹location.reload(true),不传true则可能从缓存中加载
- 赋值给
DOM
- DOM 有12种节点类型
nodeType var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0)这个写法在 IE8 及之前版本中无效123var html = document.documentElement // 取得对 <html> 的引用var body = document.body // 取得对 <body> 的引用var doctype = document.doctype // 取得对 <!DOCTYPE> 的引用document.URL与windows.location.href输出一样,但前者只读- 在 HTML 中,标签名始终都以全部大写表示,但保险起见,比较前应转化大小写123456if (element.tagName == "div"){ // 不能这样比较,很容易出错!//在此执行某些操作}if (element.tagName.toLowerCase() == "div"){ // 这样最好(适用于任何文档)//在此执行某些操作}
DOM扩展
querySelector()和querySelectorAll()(IE8+)readyState属性12345// loading,正在加载文档// complete,已经加载完文档if (document.readyState == "complete"){//执行操作}scrollIntoView()querySelector()和querySelectorAll()(IE8+)