2019读书计划(4)——《Javascript高级程序设计(第三版 )》
《Javascript高级程序设计(第三版 )》这本书从刚毕业就在手上,到这篇文章之前,始终在前几章徘徊。现在抽空把看过的重要章节记录下,方便快速复习巩固
第1章 Javascript简介
1.2 Javascript实现
- 一个完整的Javascript实现由三个不同的部分组成:核心(ECMAScript)、文档对象模型(DOM)和浏览器对象模型(BOM)。
- 常见的 Web 浏览器知识 ECMAScript 实现的宿主环境之一,其他环境包括 Node、Adobe Flash
- DOM:是针对 XML 经过扩展用于 HTML 的API(提供访问和操作网页内容的方法和接口 ;DOM级别:DOM1、DOM2、DOM3)
- BOM:控制浏览器显示的页面以外部分(提供与浏览器交互的方法和接口)
第2章 在HTML中使用Javascript
2.1 <script>
元素
- async:可选。表示==不阻塞页面,下载并且执行脚本==。不能保证执行的顺序。只对外部脚本文件有效。
- defer:可选。表示==立即下载脚本,脚本可延迟到文档被全部解析和显示后再执行==。规范要求,按照脚本加载顺序执行(现实当中不一定)。只对外部脚本文件有效。
script
脚本中不要嵌入出现”“ 字符串,会被错误识别为结束标签。正确写法是:”</script>”。
2.4 <noscript>
元素
浏览器不支持脚本或者脚本被禁用,会显示<noscript>
中的内容。
第3章 基本概念
3.1 语法
3.1.4 严格模式
ECMAScriipt 5 引入严格模式,在脚本或者函数顶部添加:
1 | ; |
3.3 变量
- 严格模式下,给未经声明的变量赋值,会抛出「ReferenceError」错误
- var声明变量存在变量提升
3.4 数据类型
5种简单数据类型(也称基本数据类型、原始数据类型):Undefined、Null、Boolean、Number和String。
一种复杂数据类型:Object
Undefined只有一个值,即Undefined。var声明变量但未初始化时,变量的值就是Undefined
null只有一个值,即null,null值表示一个空对象指针
3.4.1 typeof操作符(不是函数)
检测给定变量的数据类型,返回值有:
- undefined——变量值未定义
- boolean——变量值是布尔值
- string——变量值是字符串
- number——变量值是数值
- object——变量值是对象或者null
- function——变量值是函数
3.4.5 Number类型
NaN:
- 任何涉及NaN的操作都会返回NaN
- NaN与任何值都不相等,包括NaN本身
isNaN(params)函数:任何不能被转换成数值的值都会返回true
数值转换:
- parseInt()应该在第二个参数指明进制。
- parseFloat()只解析十进制值
3.4.7 Object类型
Object 的每个实例都具有下列属性和方法:
- constructor()——保存着用于创建当前对象的函数
- hasOwnProperty()——检查给定的属性在当前对象实例中(而不是在实例 的原型中)是否存在,参数为字符串
- isPrototypeOf()——检查传入的对象是否是传入对象的原型,即obj1.isPrototypeOf(obj2): obj1 是不是在 obj2 的原型链上
- propertyIsEnumerable(name)——检测name是否能够使用 for-in 语句来枚举
- toLocaleString()——返回对象的字符串表示,该字符串与执行环境的地区对应
- toString()——返回对象的字符串表示
- valueOf()——返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值相同。
3.5 操作符
3.5.1 一元操作符
1.递增和递减操作符
执行前置递增(递减)操作时,变量的值先改变再求值
1
2
3
4var age = 29;
var AgeNext = --age + 2
console.log(age) // 28
console.log(AgeNext) // 30执行后置递增(递减)操作时,变量先求值在执行后置操作
1 | var num1 = 2; |
2.一元加减操作符
- 对非数值类型会执行类型转换
3.5.2 位操作符
ECMAScript中的所有数字都以EEE-754 64 位格式存储。但操作符不直接操作64位的值,而是先把64位转换成32位的整数,然后执行操作,最后将结果转换回64位
负数使用二进制补码存储,计算补码分三步:1、求这个数绝对值的二进制码;2、求二进制反码(即将 0换成1,将1换成 0);3、得到的二进制反码加1.
1.按位非(NOT)
~
: 返回数值的反码(本质:操作数的负值减1)
1 | var num1 = 25; // 二进制 00000011001 |
2.按位与(AND)
&
:按位与就是将两个数值的每一位对齐,两个数值的对应位==都为1==时才返回1,任何一位是0,都返回0;
1 | var result = 25 & 4 // 输出 1 |
3.按位或(OR)
|
:两个数值的对应位==都为0时==才返回0,任何一位是1,都返回1;
1 | var result = 25 & 4 // 输出 27 |
4.按位异或(XOR)
^
:对应位上==只有一个 1== 时才返回 1,如果对 应的两位都是 1 或都是 0,则返回 0
1 | var result = 25 ^ 4 // 输出 26 |
5.左移
<<
:将数值的所有位向左移动指定的位数。左移操作会==以 0 来填充这些空位==.左移不会影响操作数的符号位
1 | var oldValue = 2; // 等于二进制的10 |
6.有符号的右移
>>
:数值向右移动,==用符号位的值来填充所有空位==
1 | var oldValue = 64; // 等于二进制的1000000 |
7.无符号右移
>>>
:将数值的所有 32 位都向右移动,对正 数来说,无符号右移的结果与有符号右移相同。对负数来说,首先,无符号右移是以 0 来填充空位.其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。
1 | var oldValue = 64; |
3.5.3 布尔操作符
1. 逻辑非
!
:将它的操作数转换为一个布尔值,然后再 对其求反
!!
==>得到该值的真正布尔值
3.5.10 逗号操作符
1 | // 声明多个变量: |
3.6.5 for-in语句
精准的迭代语句,可以用来枚举对象的属性,由于ECMAScript中对象的属性没有顺序,所以输出的属性名的顺序是不可预测的
使用 for-in 循环之前,先检测==确认该对象的值不是 null 或 undefined==,ECMAScript 5 之前会报错。
3.6.6 label语句
使用 label 语句可以在代码中添加标签,以便将来使用
break 和 continue 语句与 label 语句联合使用,多用在循环嵌套的情况下
1 | // 与break配合使用 |
==注意:== 虽然联用 break、continue 和 label 语句能够执行复杂的操作,但如果使用过度,也会给调试 带来麻烦。在此,我们建议如果使用 label 语句,一定要使用描述性的标签,同时不要嵌套过多的循环。
3.6.8 with语句
with 语句的作用是将代码的作用域设置到一个特定的对象中
1 | var qs = location.search.substring(1); |
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此
在开发大型应用程序时,不建议使用 with 语句
3.7 函数
return 语句可以不带任何返回值,默认返回值是undefined
arguments对象是类数组对象,通过length获取传入参数的个数,用中括号语法访问它的每一个参数。严格模式下,重写 arguments 的值会导致语法错误(代码将不会执行)。
第4章 变量、作用域和内存问题
4.1 基本类型和引用类型的值
5 种 基本数据类型:Undefined、Null、Boolean、Number 和 String,==按值访问==
引用类型的值是按引用访问的
4.1.2 复制变量值
如果从一个变量向另一个变量==复制基本类型的值==,会在变量对象上==创建一个新值==,然后把该值复制 到为新变量分配的位置上
当从一个变量向另一个变量==复制引用类型的值==时,值的副本实际上是一个==指针==,指向存储在堆中的一 个对象,复制操作结束后,两个变量实际上将==引用同一个对象==,==改变其中一个变量,就会影响另 一个变量==
**==ECMAScript 中所有函数的参数都是按值传递的==**:在向参数==传递基本类型==的值时,被==传递的值==会被复制给一个局部变量。在向参数==传递引用类型的值==时,会把 这个值==在内存中的地址复制给一个局部变量==,因此这个局部变量的变化会反映在函数的外部
1 | function setName(obj) { |
局部对象会在函数执行完毕后立即被销毁
4.1.4 检测类型
基本数据类型:typeof;引用类型检测:instanceof
4.2 执行环境和作用域
作用域链的用途:保证对执行环境有权访问的所有变量和函数的有序访问
4.2.1 延长作用域链
在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。
- try-catch 语句的 catch 块;
- with 语句
对 with 语句来说,会将指定的对象添加到
作用域链中。对 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
4.3 垃圾收集
4.3.1 标记清除(常用的)
所有变量打标记;去掉环境中变量的标记,以及被环境中变量引用变量的标记;之后,清除还有标记的变量
当变量进入环境(例如,在函
数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其 标记为“离开环境”。
4.3.2 引用计数
跟踪每个变量引用次数,被引用的变量就加 1;如果此变量又取了另一个变量,减 1。
4.3.3 性能问题
IE 的垃圾收集器是根据内存分配量运行的,达到临界值就会运行.如果一个脚本中包含那么多变 量,那么该脚本很可能会在其生命周期中一直保有那么多的变量.造成垃圾收集器频繁地运行
IE7后,临界值是动态修正,如果 垃圾收集例程回收的内存分配量低于 15%,临界值就会加倍;如果 例程回收了 85%的内存分配量,则将各种临界值重置回默认值。
4.3.4 管理内存
解除引用(dereferencing):一旦数据不再有用,通过将其值设置为 null 来释放其引用。
解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离 执行环境,以便垃圾收集器下次运行时将其回收。
第5章 引用类型
5.1 Object类型
创建object实例:
- new 操作符后跟 Object 构造函数:var person = new Object();
- 使用对象字面量表示法: var person = {};
5.2 Array类型
创建数组:
- 使用 Array 构造函数:var colors = new Array();
- 使用
[]
数组的 length 属性可读写。数组不存在的位置的值为undefined
5.2.1 检测数组
ES5使用Array.isArray()
。instanceof不适用于网页包含多个框架,两个以上不同的全局执行环境,存在两个以上不同版本的 Array 构造函数,从一个向另一个传入数组构造函数,严格意义上并不相等
5.2.3 栈方法
栈是一种 LIFO(Last-In-First-Out, 后进先出)的数据结构,插入和移除只发生在栈顶。
- push()方法接收任意数量的参数,添加到数组末尾,==并返回修改后数组的长度==
- pop()方法从数组末尾移除最后一项,==返回移除的项==
5.2.4 队列方法
队列是一种 FIFO(First-In-First-Out, 先进先出)的数据结构,发生在列表的前端和末端
- shift()方法==移除==数组中的第一个项并==返回该项==
- unshift()方法在数组前端==添加==任意个项并==返回新数组的长度==
5.2.5 重排序方法
- reverse()方法会反转数组项的顺序
- sort()不传参数时按照字符编码的顺序进行排序,传入一个函数时,函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等 则返回 0,如果第一个参数应该位于第二个之后则返回一个正数
1 | // arr元素为数值类型 |
5.2.6 操作方法
前两个方法不影响原数组,第三个方法会影响原数组。
- concat()会==创建一个新的数组==,参数会被自动展开
- slice()会==创建一个新数组==,可进行删除(2个参数),插入(3个参数,第二个为0),替换(3个参数)操作
- splice()方法返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何 项,则返回一个空数组)
5.2.8 迭代方法
以下方法均不会修改数组中的包含的值
- every():==每一项==都返回true,则返回true。
- some():==任一项==都返回true,则返回true
- filter():返回执行结果为true的项组成的数组
- forEach():没有返回值
- map():返回执行后的结果组成的==新数组==
5.2.9 归并方法
使用 reduce()还是 reduceRight(),主要取决于要从哪头开始遍历数组。除此之外,它们完全 相同。
- reduce():数组从第一个开始,逐个向后遍历
- reduceRight(): 数组从最后一个开始,向前遍历
1 | var values = [1,2,3,4,5]; |
5.4 RegExp类型
RegExp 构造 函数的模式参数是字符串,某些情况下要对字符进行双重转义,例如\n
(字符\
在字符串中通常被转义为\\
)
RegExp 的每个实例都具有下列属性:
- global:布尔值,表示是否设置了 g 标志
- ignoreCase:布尔值,表示是否设置了 i 标志。
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
- multiline:布尔值,表示是否设置了 m 标志。
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回
5.5.2 函数声明与函数表达式
1 | // 函数声明 |
解析器在向执行环 境中加载数据时:
- 解析器会率先读取函数声明。
- 函数表达式,则必须等到解析器执行到它所在的代码行,才会真 正被解释执行
对代码求值时,声明函数会被js引擎提升到顶部
5.5.4 函数内部属性
在函数内部,有两个特殊的对象:
arguments
和this
arguments
的主要用途是保存函数参数,该对象有个callee
的属性,指向拥有arguments
对象的函数。
1 | // 阶乘 |
this
引用的是函数据以执行的环境对象
函数名仅仅是一个包含指针的变量
ECMAScript 5 也规范化了另一个函数对象的属性caller
,这个属性中保存着==调用当前函数的函数的引用==, 如果是在全局作用域中调用当前函数,它的值为 null
严格模式下:访问 arguments.callee
和 arguments.caller
会导致错误。
严格模式下: 不能为函数的 caller 属性赋值,否则会导致错误。
非严格模式下: arguments.caller
的值是undefined
5.5.5 函数属性和方法
每个函数都包含两个 属性:
length
和prototype
prototype
是不可枚举的,不能用 for-in 枚举
每个函数都包含两个非继承而来的方法:apply()
和 call()
5.6 基本包装类型
ECMAScript提供3个特殊的引用类型:Boolean、Number 和 String
1 | var s1 = "some text"; |
引用类型与基本包装类型的主要区别就是对象的生存期
自动创建的基本包装类型的对象,只存在于代码执行的瞬间,然后立即被销毁。
1 | // 例子 |
注意!!:使用new
调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的;
1 | var value = "25"; |
5.6.1 Boolean类型
基本类型与引用类型的布尔值有两个区别:
- typeof 操作符对基本类型返回”boolean”, 而对引用类型返回”object”
- 由于 Boolean 对象是 Boolean 类型的实例,所以使用 instanceof 操作符测试 Boolean 对象会返回 true,而测试基本类型的布尔值则返回 false
1 | var falseObject = new Boolean(false); |
==建议永远不要使用Boolean对象==
5.6.2 Number类型
- toFixed(小数位数),能够自动舍入的特性,可以表示带有 0 到 20 个小数位的数值(IE8 及之前版本不能正确 舍入范围在{(-0.94,-0.5],[0.5,0.94)}之间的值)
- toExponential(小数位数),转化为科学计数法,返回字符串。
5.6.3 String类型
- String.fromCharCode(…charcodes): 将字符编码转化为字符串。
- String.charCodeAt(index): 将 index 的字符转化为字符编码。
string.match(pattern) 返回一个数组
第6章 面向对象的程序设计
ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”
6.1.1 属性类型
通过Object.defineProperty(属性所在的对象,属性的名字,一个描述符对象)方法,修改属性默认的特性(在IE8以上的版本中使用)
1 | var person = {}; |
- 数据属性
可以多次调用
Object.defineProperty()
修改下列属性,除了将 configurable 设置为 false。
Configurable
:表示能否通过 delete 删除属性,默认值为 trueEnumerable
: 表示能否通过 for-in 循环返回属性,默认值为 trueWritable
: 表示能否修改属性的值,默认值为 trueValue
: 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
- 访问器属性
访问器属性不能直接定义,必须使用 Object.defineProperty()来定义,只设置
Get(Set)
时,意味着属性是不能写(意味着属性是不能读)
Configurable
:表示能否通过 delete 删除属性,默认值为 trueEnumerable
: 表示能否通过 for-in 循环返回属性,默认值为 trueGet
: 在读取属性时调用的函数。默认值为 undefinedSet
: 在写入属性时调用的函数。默认值为 undefined。
6.2 创建对象
使用Object 构造函数或对象字面量创建对象,缺点:使用同
一个接口创建很多对象,会产生大量的重复代码
6.2.1 工厂模式
解决了创建 多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
1 | function createPerson(name, age, job){ |
6.2.2 构造函数模式
与工厂函数相比的区别:
- 没有显式地创建对象
- 直接将属性和方法赋给了 this 对象
- 没有 return 语句。
创建 Person 的新实例,必须使用 new 操作符
1 | function Person(name, age, job){ |
构造函数的主要问题:每个方法都要在每个实例上重新创建一遍。
person1 和 person2 都有一个名为 sayName()的方法,但那 两个方法不是同一个 Function 的实例
为了解决上述问题,将上述代码修改如下:
1 | function Person(name, age, job){ |
该方法出现了新的问题:sayName()
是定义在全局作用域中的函数, 在全局作用域中定义的函数实际上只能被某个对象调用。于是出现了原型模式
6.2.3 原型模式
每个函数都有一个prototype(原型)属性,它是一个指针,指向一个对象,包含可以由特定类型的==所有实例==共享的属性和方法
- 理解原型对象
创建一个新函数时,会默认创建一个prototype
属性,指向函数的原型对象,默认所有原型对象会有一个constructor
属性,constructor
属性包含一个指向 prototype
属性所在函数的指针.
1 | // 例子 |
可以通过对象实例访问保存在原型中的值,但不能通过对象实例重写原型中的值。
1 | function Person(){ |
用person1.hasOwnProperty(属性名)
方法可以检测一个属性是存在于实例中(返回true)还是原型中(返回false)。
- 原型与 in 操作符
两种方式使用 in 操作符:单独使用和在 for-in 循环中使用
单独使用时,in 操作符会在通 过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中
1 | function Person(){ |
同时使用 hasOwnProperty()方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于 原型中
1 | function hasPrototypeProperty(object, name){ |
for-in 循环,返回的是==所有能够通过对象访问的、可枚举的(enumerated)属性==,其中 既包括存在于==实例中==的属性,也包括存在于==原型中==的属性,屏蔽了原型中不可枚举属性(即将 [[Enumerable]]标记为 false 的属性)的实例属性也会在 for-in 循环中返回.
使用 ECMAScript 5 的 Object.keys()
方法,取得对象上==所有可枚举==的实例属性,返回一个包含所有可枚举属性的字符串==数组==
- 原型的动态性
实例与原型 之间的连接只不过是一个指针,而非一个副本,在实例中没有找到的属性会继续去原型中搜索
1 | var friend = new Person(); |
如果创建实例后再重写原型,会有不一样的情况
注意:实例中的指针仅指向原型,而不是指向构造函数。
1 | function Person(){ |
6.2.4 组合使用构造函数模式和原型模式
构造函数: 用于定义实例属性。
原型模式: 用于定义共享属性和方法
6.2.6 寄生构造函数模式
除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实 是一模一样的
说明: 返回的对象与构造函数或者与构造函数的原型属 性之间没有关系,不能依赖 instanceof 操作符来确定对象类型
6.2.7 稳妥构造函数模式
稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。
稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:
- 一是新创建对象的 实例方法不引用 this;
- 二是不使用 new 操作符调用构造函数。
在JavaScript构造函数中:如果return值类型,那么对构造函数没有影响,实例化对象返回空对象;如果return引用类型(数组,函数,对象),那么实例化对象就会返回该引用类型;
1 | function Super(a){ |
6.3 继承
接口继承:继承方法签名
实现继承:继承实际的方法
ECMAScript 只支持实现继承,主要是依靠原型链 来实现的
第7章 函数表达式
定义函数的 方式有两种:一种是函数声明,另一种就是函数表达式
函数声明,它的一个重要特征就是==函数声明提升==
7.1 递归
经典递归
1 | function factorial(num){ |
上述代码会出现一个问题
1 | var anotherFactorial = factorial; |
将以上代码再次修改:
arguments.callee 是一个指向正在执行的函数的指针
1 | function factorial(num){ |
但在严格模式下,不能通过脚本访问 arguments.callee
于是再次修改:
1 | var factorial = (function f(num){ |
7.2 闭包
闭包是指:有权访问另一个函数作用域中的变量的函数。作用域得到了延长。
7.3 模仿块级作用域
1 | (function(){ |
整理完毕!!!