《JavaScript权威指南》阅读笔记(上)
断续读完了 JavaScript 开发者眼中的圣经,总之,真是厚厚的一本手册,上千页的非常详细的介绍,大部分是基于 API 的细节,因此很多东西其实是没必要做笔记的(没必要的意思是,随着阅历的增加,很多特性你也许已经了然于胸,但不管怎样,圣经就是圣经,我觉得有空多看两遍核心部分是很有裨益的),因此这里只列出一些核心点和容易被疏漏的地方,首先是第一部分,关于语言的核心

作者 David Flanagan,封面动物:爪哇犀牛 - Javan rhinoceros
词法结构
- JavaScript 使用
unicode编码 - 分号解析规则:缺少分号无法解析代码的时候(因此以
(或[开始的代码很大可能与前面的语句一起解析)
类型、值和变量
- 两种数据类型
- 基本类型(Primitive),也是不可变类型
- Number
- String
- Boolean
- Null
- Undefined
- Symbol
- 对象(Object),也是可变类型
- 数组 Array
- 函数 Function
- 其他(包括 Date、RegExp 等)
- 基本类型(Primitive),也是不可变类型
字符串换行技巧
123456789101112131415// 通用var str1 = ''+'line1'+'line2'// ES5var str2 = '\line1\line2'// ES6var str3 = `line1line2`转换为
false的六个值,其他均为true- undefined
- null
- 0
- -0
- NaN
- “”(空字符串)
null与undefined12typeof null === 'object' // true, 表明是一个空对象// 而 undefined 则指代一种更深层次的空 => 未定义或不存在
表达式和运算符
前后自增量
12var i = 1, j = ++i // i和j的值都是2,前增量 pre-increment,赋值前自增var i = 1, j = i++ // i是2,j是1,后增量 post-incrementNaN与任何值不相等,包括本身in操作符用来检测对象的属性是否存在12var data = [7,8,9]'0' in data === true // true 因为存在索引0,可以转换成字符串 '0'通过
delete操作符删除数组元素并不会修改数组长度
语句
- 函数或声明不应该放在除函数外的代码块中
- 在函数中,
switch中的break可以用return实现 break不同通过标签跳转到函数外部catch语句中花括号是有块级作用域的
对象
|
|
- 每一个对象都具有原型(
null与Object.prototype除外)
数组
稀疏数组,数组的长度大于数组项的数量
1234567// 产生稀疏数组var a1 = new Array(5);var a2 = []a2.length = 5var a3 = [, , ,]var a4 = [1, 2, 3, 4]delete a4[1] // 等同于[1, , 3, 4]压缩数组
12345678910var a = [1, undefined, , , 3, null]// b === [1, undefined, 3, null], 稀疏数组被压缩b = a.filter(function () {return true})// c === [1,3], 进一步压缩 undefined 和 nullc = a.filter(function (x) {return x !== undefined && x !== null})空数组调用
.every()/.some()总是返回true/false如何判断一个未知对象是否为数组(只有两种方法)
123var arr = new Array()Array.isArray(arr) //true, ES5+Object.prototype.toString(arr) === '[object Array]' // ES3字符串可看成是只读的类数组对象,方便进行各种操作
函数
- 不要将函数声明在代码块中(可换成表达式)
非严格模式中,直接调用函数时其内部的
this指向全局对象,作为对象的方法调用时则指向方法所属对象12// 判断函数是否在严格模式中运行var strict = (function () { return !this }())方法的链式调用——当方法不需要返回值时,直接返回
thiscallee指向当前正在执行的函数,而caller则指向调用当前函数的函数(严格模式中禁用)函数是一种特殊的对象,可以拥有属性(类似构造函数)
1234567891011121314// 如下例子,直接使用函数的属性存储外部初始值(每次调用函数都是0), 而省去一次变量声明来存储初始值uniqueInterger.counter = 0 // 函数声明提前function uniqueInterger () {return uniqueInterger.counter++}// 函数同样可以把自己当成一个数组来使用,如下例子// 计算阶乘,并将结果缓存到函数的属性中function factirual (n) {if (!(n in factorial)) { // 如果没有缓存结果factorial[n] = n * factorial(n-1) // 计算结果并缓存}return factorial[n] // 返回缓存的结果}技术角度上讲,所有的函数都是闭包。一般认为闭包是在函数中返回的另一个函数
- 函数的
length为期待的形参个数,而argument.length则为实际传入实参个数 call()和apply()方法类似,第一个参数会绑定为this的值,apply参数传入放在一个数组中;而bind()是ES5+方法,绑定所有实参到对象123456789// f.call(o) 或 f.apply(o) 的实际调用类似下面o.m = f; // 将f存储为o的临时方法o.m(); // 调用临时方法delete o.m; // 将临时方法删除// 以对象o的方法调用函数f(), 并传入两个参数f.call(o, 1, 2); // 直接传入f.apply(o, [1, 2]); // 参数包装成数组形式,方便传入任意数量参数// bind 对比 call 和 apply,不仅将第一个实参绑定至 this,也将其他实参绑定至引用对象(柯里化)
|
|
- 函数的
toString()方法返回定义该函数的源码 - 一般不用
Function()构造函数生成函数 - 高阶函数即操作函数的函数,接收函数作为参数
类和模块
类的所有实例对象都从同一个原型对象上继承属性,原型对象是类的核心
- 构造函数调用的一个重要特征是,构造函数的原型会被用作新对象的原型
- 构造函数是类的公共标识,原型对象是类的唯一标识
constructor属性123456789101112function F () {}F.prototype.constructor === F // true//var o = new F()o.constructor === F // true,一般类的实例的 constructor 指向类的构造函数//function G () {}G.prototype = {} // 直接赋值原型会导致 constructor 丢失G.prototype.constructor === G // falseG.prototype = {'constructor': G // 显式设置构造函数反式引用修复上面问题}instanceof检测的实际是对象的继承关系,而不是创建对象的构造函数iframe中一个Array的实例不是另一个iframe中的Array的实例- 一般通过闭包的方式实现私有状态
- 抽象类是指一种在最高祖先级别定义这个类应该具有的方法,但这些方法没有具体实现,如果在子孙类中没有通过重载复写,一般在调用时会触发祖先定义的方法提示
- ES3 中,对象的读写等属性并不能自行配置,因此不能将自定义类模拟成内置对象(
for...in会遍历到)
犀牛书对于继承这一方面的介绍一开始就非常的深奥难懂,其实不是特别良好的教程,推荐阅读《JavaScript 高级程序设计》中关于原型与继承的部分
正则表达式的模式匹配
JavaScript 的正则是 Perl 的大型子集
- ES3 中,对于同样的正则表达式直接量,对应的是同一个正则对象。在 ES5 中,则跟对象等引用类似,不会因为同样是空对象就认为是同一个对象。(由于即使是 IE 的实现也符合 ES5 标准,因此实际上只需要考虑 ES5 的规定即可)
- 对于特殊的字符,需要用反斜杠进行转义
- 常见字符匹配语法
[abc]=> 方括号内任意字符(这里是 a,b,c)[^abc]=> 除方括号的任意字符(这里指不含 a 或 b 或c的任意一个).=> 除了换行符外的其他字符\w=> 等价于[a-zA-Z0-9],指字母和数字\W=> 上一条的取反\s=> 一般匹配空格\S=> 上一条的取反,一般指所有非空字符,范围比\w大一些\d=> 等价于[0-9],匹配数字\D=> 上一条取反,匹配非数字
- 重复语法
{n, m}=> 至少出现n次,但最多m次{n,}=> 至少出现n次{n}=> 只能出现n次?=> 出现1次或不出现,等价于{0, 1}+=> 至少出现1次,等价于{1,}*=> 可以随便出现(不出现,1次或多次),等价于{0, }
- 选择分组
|选择符号,区分可选的分组,这些分组会短路判断,只有在前面的分组不匹配时才尝试后面的分组()分组符号,将模式打包,定义子模式或子表达式(同时后面可以使用快捷写法)
- 指定位置
^=> 指定作为开头$=> 指定作为结尾\b=> 匹配边界\B=> 上一条的取反
- 修饰符
i=> 忽略大小写g=> 全局匹配m=> 多行模式
- String 方法
.search()=> 返回匹配字符串所在的位置,若无匹配则返回-1.replace(reg, string)=> 执行检索和替换,参数可以是函数.match()=> 全局检索时返回匹配结果的数组,否则返回首次匹配到的带自模式的.split()=> 根据匹配项分割字符串
- 一般推荐使用直接量法构造正则