把近期看高程这本书做的笔记摘录整理出来了,总归对原生javascript理论有了一个比较全面的的认识,这次把书中的一些知识要点摘录出来了,便于以后查阅的时候有方向,也更有效率!!

第一章、javascript简介

  • 0101、完整的javascript有三部分组成:核心ECMAScript,DOM和BOM
  • 0102、ECMAScript规定了语言的下列组成部分:语法,类型,语句,关键字,保留字,操作符,对象

第二章、在html中使用javascript

  • 0201、HTML4.01为script定义了下列6个属性:async:可选,表示立即下载脚本,不阻塞;charset:可选,指定字符集;defer:可选,延迟到文档完全被解析和显示之后再执行;language:已废弃;src:可选,包含要执行代码的外部文件;type:可选,脚本语言的内容类型
  • 0202、标签的位置:所有script元素都应该放在页面的<body>元素页面内容的后面
  • 0203、IE8以后不支持defer属性
  • 0204、async属性的目的是不让页面等待脚本下载和执行,从而异步加载页面其他内容
  • 0205、<小于号在XHTML中会被当作开始一个新标签来解析
  • 0206、noscript包含该元素的内容只有在以下情况才会显示出来:不支持脚本,支持脚本但被禁用

第三章、基本概念

  • 0301、标识符,就是指变量、函数、属性的名字,或者函数的参数:第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ );

其他字符可以是字母、下划线、美元符号或数字

  • 0302、严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行
  • 0303、加上这个分号可以避免很多错误,加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了
  • 0304、用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁。虽然省略 var 操作符可以定义全局变量,但这也不是我们推荐的做法。给未经声明的变量赋值在严格模式下会导致抛出 ReferenceError 错误
  • 0305、ECMAScript 中有 5 种简单数据类型(也称为基本数据类型):Undefined 、Null、Boolean 、Number和 String 。还有 1种复杂数据类型—— Object , Object 本质上是由一组无序的名值对组成的
  • 0306、Safari 5 及之前版本、Chrome 7 及之前版本在对正则表达式调用 typeof 操作符时会返回 "function" ,而其他浏览器在这种情况下会返回"object"
  • 0307、对未经声明的变量调用 delete 不会导致错误,但这样做没什么实际意义,而且在严格模式下确实会导致错误
  • 0308、对未初始化和未声明的变量执行 typeof 操作符都返回了 undefined 值;这个结果有其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作
  • 0309、如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 而不是其他值。只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值。这样做不仅可以体现 null 作为空对象指针的惯例,而且也有助于进一步区分null和undefined
  • 0310、要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean()
  • 0311、八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析
  • 0312、十六进制字面值的前两位必须是 0x,后跟任何十六进制数字(0~9 及 A~F)。其中,字母 A~F可以大写,也可以小写
  • 0313、保存浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript会不失时机地将浮点数值转换为整数值
  • 0314、在默认情况下,ECMASctipt 会将那些小数点后面带有 6 个零以上的浮点数值转换为以 e 表示法表示的数值(例如,0.0000003 会被转换成 3e-7)
  • 0315、浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1 加 0.2的结果不是 0.3,而是 0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值
  • 0316、要想确定一个数值是不是有穷的(换句话说,是不是位于最小和最大的数值之间),可以使用 isFinite() 函数。这个函数在参数位于最小与最大数值之间时会返回 true
  • 0317、NaN 与任何值都不相等,包括 NaN本身18、 isNaN() 在接收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串 "10" 或 Boolean 值。而任何不能被转换为数值的值都会导致这个函数返回true
  • 0318、尽管有点儿不可思议,但 isNaN() 确实也适用于对象。在基于对象调用 isNaN()函数时,会首先调用对象的 valueOf() 方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用 toString() 方法,再测试返回值。而这个过程也是 ECMAScript中内置函数和操作符的一般执行流程
  • 0319、由于 Number() 函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt()函数。parseInt() 函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号, parseInt()就会返回 NaN ;也就是说,用 parseInt() 转换空字符串会返回 NaN ( Number() 对空字符返回 0)。如果第一个字符是数字字符, parseInt() 会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。例如, "1234blue" 会被转换为 1234,因为 "blue" 会被完全忽略。类似地, "22.5"会被转换为 22,因为小数点并不是有效的数字字符。
  • 0320、除了第一个小数点有效之外, parseFloat() 与 parseInt() 的第二个区别在于它始终都会忽略前导的零, parseFloat() 只解析十进制值
  • 0321、ECMAScript 中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量
  • 0322、Object 的每个实例都具有下列属性和方法:constructor :保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是 Object(); hasOwnProperty(propertyName) :用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名( propertyName )必须以字符串形式指定(例如: o.hasOwnProperty("name") );isPrototypeOf(object) :用于检查传入的对象是否是传入对象的原型;propertyIsEnumerable(propertyName) :用于检查给定的属性是否能够使用 for-in 语句(本章后面将会讨论)来枚举。与 hasOwnProperty() 方法一样,作为参数的属性名必须以字符串形式指定;toLocaleString() :返回对象的字符串表示,该字符串与执行环境的地区对应;toString() :返回对象的字符串表示;valueOf() :返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值相同
  • 0323、在应用于对象时,相应的操作符通常都会调用对象的 valueOf()和(或) toString() 方法,以便取得可以操作的值
  • 0324、使用 while 循环做不到的,使用 for 循环同样也做不到。也就是说, for 循环只是把与循环有关的代码集中在了一个位置
  • 0325、Safari 3 以前版本的 for-in 语句中存在一个 bug,该 bug 会导致某些属性被返回两次。
  • 0326、break 和 continue 语句用于在循环中精确地控制代码的执行。其中, break 语句会立即退出循环,强制继续执行循环后面的语句。而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行
  • 0327、with 语句的作用是将代码的作用域设置到一个特定的对象中。严格模式下不允许使用 with 语句,否则将视为语法错误。由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句
with(location){
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}
  • 0328、通过为每个 case 后面都添加一个 break 语句,就可以避免同时执行多个 case代码的情况。假如确实需要混合几种情形,不要忘了在代码中添加注释,说明你是有意省略了 break 关键字。虽然 ECMAScript 中的 switch 语句借鉴自其他语言,但这个语句也有自己的特色。首先,可以在switch 语句中使用任何数据类型(在很多其他语言中只能使用数值),无论是字符串,还是对象都没有问题。其次,每个 case 的值不一定是常量,可以是变量,甚至是表达式
switch (i) {
    case 25:
    /*  合并两种情形 */
    case 35:
        alert("25 or 35");
        break;
    case 45:
        alert("45");
    break;
    default:
        alert("Other");
}
  • 0329、switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串 "10" 不等于数值 10)
  • 0330、函数会在执行完 return 语句之后停止并立即退出。因此,位于 return 语句之后的任何代码都永远不会执行
  • 0331、 return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下
  • 0332、严格模式对函数有一些限制:不能把函数命名为 eval 或 arguments ;不能把参数命名为 eval 或 arguments ; 不能出现两个命名参数同名的情况。如果发生以上情况,就会导致语法错误,代码无法执行
  • 0333、函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。 arguments 对象只是与数组类似(它并不是 Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是 arguments[0] ,第二个元素是 argumetns[1] ,以此类推),使用 length 属性来确定传递进来多少个参数
  • 0334、没有传递值的命名参数将自动被赋予 undefined 值
  • 0335、严格模式对如何使用 arguments 对象做出了一些限制。把 arguments[1] 设置为 10 , num2 的值仍然还是 undefined 。其次,重写arguments 的值会导致语法错误(代码将不会执行)
  • 0336、ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数
  • 0337、通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载
  • 0338、 ECMAScript 中的基本数据类型包括 Undefined 、 Null 、 Boolean 、 Number 和 String
  • 0339、与其他语言不同,ECMScript 没有为整数和浮点数值分别定义不同的数据类型, Number 类型可用于表示所有数值
  • 0340、ECMAScript 中也有一种复杂的数据类型,即 Object 类型,该类型是这门语言中所有对象的基础类型
  • 0341、未指定返回值的函数返回的是一个特殊的 undefined 值
  • 0342、 可以向 ECMAScript 函数传递任意数量的参数,并且可以通过 arguments 对象来访问这些参数
  • 0343、当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象

第四章、变量、作用域和内存问题

  • 0401、 typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具
  • 0402、通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此,ECMAScript提供了 instanceof 操作符
  • 0403、函数参数也被当作变量来对待,因此其访问规则与执行环境中的其他变量相同
  • 0404、在catch 语句中捕获的错误对象会被添加到执行环境的变量对象,而不是 catch 语句的变量对象中。换句话说,即使是在 catch 块的外部也可以访问到错误对象
  • 0405、变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链。JavaScript 引擎在优化标识符查询方面做得不错,因此这个差别在将来恐怕就可以忽略不计了
  • 0406、一旦数据不再有用,最好通过将其值设置为 null 来释放其引用——这个做法叫做解除引用(dereferencing)
  • 0407、基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中
  • 0408、 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本
  • 0409、引用类型的值是对象,保存在堆内存中
  • 0410、包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针
  • 0411、从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象
  • 0412、确定一个值是哪种基本类型可以使用 typeof 操作符,而确定一个值是哪种引用类型可以使用instanceof 操作符
  • 0413、 “标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存
  • 0414、解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用
  • 0415、除非必须使用变量来访问属性,否则我们建议使用点表示法

第五章、引用类型

  • 0501、与对象一样,在使用数组字面量表示法时,也不会调用 Array 构造函数(Firefox 3及更早版本除外)
  • 0502、数组最多可以包含 4 294 967 295 个项,这几乎已经能够满足任何编程需求了。如果想添加的项数超过这个上限值,就会发生异常。而创建一个初始大小与这个上限值接近的数组,则可能会导致运行时间超长的脚本错误
  • 0503、所有对象都具有 toLocaleString() 、 toString() 和 valueOf() 方法。其中,调用数组的 toString() 方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用 valueOf() 返回的还是数组。实际上,为了创建这个字符串会调用数组每一项的 toString() 方法
  • 0504、 toLocaleString() 方法经常也会返回与 toString() 和 valueOf() 方法相同的值,但也不总是如此
  • 0505、如果数组中的某一项的值是 null 或者 undefined ,那么该值在 join() 、toLocaleString() 、 toString() 和 valueOf() 方法返回的结果中以空字符串表示
  • 0506、push() 方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项
  • 0507、栈数据结构的访问规则是 LIFO(后进先出),而队列数据结构的访问规则是 FIFO(First-In-First-Out,先进先出)
  • 0508、结合使用 shift() 和 push() 方法,可以像使用队列一样使用数组
  • 0509、同时使用 unshift() 和 pop() 方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项
  • 0510、alert([0, 1, 5, 10, 15].sort());//0,1,10,15,5。可见,即使例子中值的顺序没有问题,但 sort() 方法也会根据测试字符串的结果改变原来的顺序。因为数值 5 虽然小于 10,但在进行字符串比较时, "10" 则位于 "5" 的前面,于是数组的顺序就被修改了。不用说,这种排序方式在很多情况下都不是最佳方案。比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数
  • 0511、由于比较函数通过返回一个小于零、等于零或大于零的值来影响排序结果,因此减法操作就可以适当地处理所有这些情况
  • 0512、 concat() 方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 concat() 方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给 concat() 方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾
  • 0513、slice() 方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下, slice() 方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。注意, slice() 方法不会影响原始数组
  • 0514、如果 slice() 方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如,在一个包含5项的数组上调用 slice(-2,-1) 与调用 slice(3,4) 得到的结果相同。如果结束位置小于起始位置,则返回空数组
  • 0515、splice() 方法可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数;可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项;可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等
  • 0516、splice() 方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)
  • 0517、 indexOf() 和 lastIndexOf()。这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, indexOf() 方法从数组的开头(位置0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。支持它们的浏览器包括 IE9+、Firefox 2+、Safari3+、Opera 9.5+和 Chrome
  • 0518、ECMAScript 5 为数组定义了5个迭代方法,这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。every() :对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true;filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组;forEach():对数组中的每一项运行给定函数。这个方法没有返回值;map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组;some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true。以上方法都不会修改数组中的包含的值
  • 0519、这些数组方法通过执行不同的操作,可以大大方便处理数组的任务。支持这些迭代方法的浏览器有IE9+、Firefox2+、Safari 3+、Opera 9.5+和 Chrome
  • 0520、 reduce() 和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。传给reduce()和reduceRight()的函数接收4个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。使用reduce()方法可以执行求数组中所有值之和的操作
  • 0521、使用 reduce() 还是 reduceRight() ,主要取决于要从哪头开始遍历数组。除此之外,它们完全相同
  • 0522、在调用 Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间
  • 0523、 Date.parse() 方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。ECMA-262没有定义Date.parse() 应该支持哪种日期格式,因此这个方法的行为因实现而异,而且通常是因地区而异
  • 0524、如果传入 Date.parse()方法的字符串不能表示日期,那么它会返回NaN。实际上,如果直接将表示日期的字符串传递给 Date 构造函数,也会在后台调用 Date.parse() 。换句话说,下面的代码与前面的例子是等价的:var someDate = new Date("May 25, 2004");
  • 0525、Date.UTC() 方法同样也返回表示日期的毫秒数,但它与Date.parse()在构建值时使用不同的信息。Date.UTC()的参数分别是年份、基于 0 的月份(一月是0,二月是1,以此类推)、月中的哪一天(1到31)、小时数(0到23)、分钟、秒以及毫秒数。在这些参数中,只有前两个参数(年和月)是必需的。如果没有提供月中的天数,则假设天数为1;如果省略其他参数,则统统假设为 0。
  • 0526、ECMAScript 5 添加了 Data.now() 方法,返回表示调用这个方法时的日期和时间的毫秒数
  • 0527、支持 Data.now() 方法的浏览器包括IE9+、Firefox3+、Safari3+、Opera10.5和Chrome。在不支持它的浏览器中,使用+操作符把 Data 对象转换成字符串,也可以达到同样的目的
  • 0528、正则表达式。字面量声明和构造函数声明的区别,字面量始终会共享一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例
  • 0529、RegExp 对象的主要方法是 exec() ,该方法是专门为捕获组而设计的。 exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null 。返回的数组虽然是 Array 的实例,但包含两个额外的属性: index 和 input 。其中, index 表示匹配项在字符串中的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)
  • 0530、正则表达式的第二个方法是 test() ,它接受一个字符串参数。在模式与该参数匹配的情况下返回true ;否则,返回 false 。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便
  • 0531、正则表达式的 valueOf() 方法返回正则表达式本身。
  • 0532、即使 test() 方法只返回一个布尔值,但RegExp 构造函数的属性$1和$2也会被匹配相应捕获组的字符串自动填充
  • 0533、每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定
  • 0534、Function构造函数可以接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。但是,我们不推荐读者使用这种方法定义函数,因为这种语法会导致解析两次代码(第一次是解析常规 ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能
  • 0535、函数声明式与函数表达式的区别。解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行
  • 0536、因为 ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回
  • 0537、arguments是一个类数组对象,包含着传入函数中的所有参数。虽然 arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数
  • 0538、this引用的是函数据以执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)
  • 0539、函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的 sayColor() 函数与 o.sayColor() 指向的仍然是同一个函数
  • 0540、ECMAScript5也规范化了另一个函数对象的属性: caller 。除了Opera的早期版本不支持,其他浏览器都支持这个 ECMAScript3并没有定义的属性。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。为了实现更松散的耦合,也可以通过 arguments.callee.caller来访问相同的信息
  • 0541、IE、Firefox、Chrome和Safari的所有版本以及 Opera 9.6 都支持 caller 属性
  • 0542、当函数在严格模式下运行时,访问arguments.callee 会导致错误。ECMAScript5还定义了arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是undefined。定义这个属性是为了分清 arguments.caller和函数的caller属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了
  • 0543、ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性: length和prototype 。其中,length属性表示函数希望接收的命名参数的个数
  • 0544、在ECMAScript5中,prototype属性是不可枚举的,因此使用 for-in 无法发现
  • 0545、每个函数都包含两个非继承而来的方法:apply() 和 call() 。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先, apply() 方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array 的实例,也可以是arguments 对象
  • 0546、在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window 。除非明确把函数添加到某个对象或者调用 apply() 或 call() ,否则 this 值将是undefined
  • 0547、call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是 this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来
  • 0548、事实上,传递参数并非 apply() 和 call() 真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域
  • 0549、使用 call()(或apply())来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系
  • 0550、ECMAScript 5 还定义了一个方法: bind() 。这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind() 函数的值。支持 bind() 方法的浏览器有 IE9+、Firefox 4+、Safari 5.1+、Opera 12+和 Chrome
  • 0551、为了便于操作基本类型值,ECMAScript还提供了 3 个特殊的引用类型:Boolean、Number和String。这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据
  • 0552、引用类型与基本包装类型的主要区别就是对象的生存期。使用 new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法
  • 0553、Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。var obj = new Object("some text"); alert(obj instanceof String); //true
  • 0554、使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的
  • 0555、 toFixed()方法会按照指定的小数位返回数值的字符串表示。如果数值本身包含的小数位比指定的还多,那么接近指定的最大小数位的值就会舍入。可以利用round配合toFixed加爵该方法的在ie浏览器上的兼容问题
  • 0556、对于一个数值来说,toPrecision()方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看哪种格式最合适。这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。实际上,toPrecision()会根据要处理的数值决定到底是调用 toFixed() 还是调用 toExponential()
  • 0557、在使用typeof操作符测试基本类型数值时,始终会返回 "number" ,而在测试 Number 对象时,则会返回 "object" 。类似地,Number对象是Number类型的实例,而基本类型的数值则不是
  • 0558、两个用于访问字符串中特定字符的方法是:charAt() 和 charCodeAt()。这两个方法都接收一个参数,即基于 0 的字符位置。其中,charAt()方法以单字符字符串的形式返回给定位置的那个字符(ECMAScript中没有字符类型)
  • 0559、ECMAScript5还定义了另一个访问个别字符的方法。在支持此方法的浏览器中,可以使用方括号加数字索引来访问字符串中的特定字符。使用方括号表示法访问个别字符的语法得到了 IE8 及 Firefox、Safari、Chrome 和 Opera 所有版本的支持。如果是在IE7及更早版本中使用这种语法,会返回undefined值(尽管根本不是特殊的undefined 值)
  • 0560、ECMAScript还提供了三个基于子字符串创建新字符串的方法: slice() 、 substr() 和 substring() 。这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说, slice() 和substring() 的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr() 的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。与concat() 方法一样, slice() 、 substr() 和 substring() 也不会修改字符串本身的值——它们只是返回一个基本类型的字符串值,对原始字符串没有任何影响。在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。其中, slice() 方法会将传入的负值与字符串的长度相加, substr() 方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为 0。最后, substring() 方法会把所有负值参数都转换为 0
  • 0561、IE 的 JavaScript 实现在处理向 substr() 方法传递负值的情况时存在问题,它会返回原始的字符串。IE9 修复了这个问题
  • 0562、有两个可以从字符串中查找子字符串的方法: indexOf() 和 lastIndexOf() 。这两个方法都是从一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回 -1 )。这两个方法的区别在于: indexOf() 方法从字符串的开头向后搜索子字符串,而 lastIndexOf() 方法是从字符串的末尾向前搜索子字符串。这两个方法都可以接收可选的第二个参数,表示从字符串中的哪个位置开始搜索
  • 0563、ECMAScript 5 为所有字符串定义了 trim() 方法。这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。由于 trim() 返回的是字符串的副本,所以原始字符串中的前置及后缀空格会保持不变。支持这个方法的浏览器有 IE9+、Firefox 3.5+、Safari 5+、Opera 10.5+和 Chrome。此外,Firefox 3.5+、Safari 5+和 Chrome 8+还支持非标准的 trimLeft() 和 trimRight() 方法,分别用于删除字符串开头和末尾的空格
  • 0564、接下来我们要介绍的是一组与大小写转换有关的方法。ECMAScript中涉及字符串大小写转换的方法有 4 个:toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase() 。其中, toLowerCase() 和 toUpperCase() 是两个经典的方法,借鉴自 java.lang.String 中的同名方法。而 toLocaleLowerCase() 和 toLocaleUpperCase() 方法则是针对特定地区的实现。一般来说,在不知道自己的代码将在哪种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些
  • 0565、String 类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是 match() ,在字符串上调用这个方法,本质上与调用 RegExp 的 exec() 方法相同。 match() 方法只接受一个参数,要么是一个正则表达式,要么是一个 RegExp 对象
  • 0566、另一个用于查找模式的方法是 search() 。这个方法的唯一参数与 match() 方法的参数相同:由字符串或 RegExp 对象指定的一个正则表达式。 search() 方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回 -1 。而且, search() 方法始终是从字符串开头向后查找模式
  • 0567、ECMAScript 提供了 replace() 方法。这个方法接受两个参数:第一个参数可以是一个 RegExp 对象或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的办法就是提供一个正则表达式,而且要指定全局( g )标志
  • 0568、replace() 方法的第二个参数也可以是一个函数。在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递 3 个参数:模式的匹配项、模式匹配项在字符串中的位置和原始字符串。在正则表达式中定义了多个捕获组的情况下,传递给函数的参数依次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串。这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数作为 replace() 方法的第二个参数可以实现更加精细的替换操作
  • 0569、最后一个与模式匹配有关的方法是 split() ,这个方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个 RegExp 对象(这个方法不会将字符串看成正则表达式)。 split() 方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。对 split() 中正则表达式的支持因浏览器而异。尽管对于简单的模式没有什么差别,但对于未发现匹配项以及带有捕获组的模式,匹配的行为就不大相同了
  • 0570、与操作字符串有关的最后一个方法是 localeCompare() ,这个方法比较两个字符串,并返回下列值中的一个: 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是 -1 ,具体的值要视实现而定); 如果字符串等于字符串参数,则返回 0 ;如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是 1 ,具体的值同样要视实现而定)。localeCompare() 方法比较与众不同的地方,就是实现所支持的地区(国家和语言)决定了这个方法的行为
  • 0571、另外, String 构造函数本身还有一个静态方法: fromCharCode() 。这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法 charCodeAt()执行的是相反的操作
  • 0572、 encodeURI() 主要用于整个 URI(例如,http://www.wrox.com/illegal value.htm),而 encode-URIComponent() 主要用于对 URI 中的某一段(例如前面 URI 中的 illegal value.htm )进行编码。它们的主要区别在于, encodeURI() 不会对本身属于 URI 的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而 encodeURIComponent() 则会对它发现的任何非标准字符进行编码
  • 0573、一 般 来 说 , 我 们 使 用 encodeURIComponent() 方 法 的 时 候 要 比 使 用encodeURI() 更多,因为在实践中更常见的是对查询字符串参数而不是对基础 URI进行编码。
  • 0574、与 encodeURI() 和 encodeURIComponent() 方法对应的两个方法分别是 decodeURI() 和decodeURIComponent()
  • 0575、现在,我们介绍最后一个——大概也是整个 ECMAScript语言中最强大的一个方法: eval() 。 eval()方法就像是一个完整的 ECMAScript 解析器,它只接受一个参数,即要执行的 ECMAScript (或 JavaScript)字符串
  • 0576、能够解释代码字符串的能力非常强大,但也非常危险。因此在使用 eval() 时必须极为谨慎,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)
  • 0577、其中, min() 和 max() 方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数
  • 0578、要找到数组中的最大或最小值,可以像下面这样使用 apply() 方法。Math.max.apply(Math,[1,2,4,65,8,4)//65
  • 0579、下面来介绍将小数值舍入为整数的几个方法: Math.ceil() 、 Math.floor() 和 Math.round()
  • 0580、Math.random() 方法返回大于等于 0 小于 1 的一个随机数
  • 0581、 引用类型与传统面向对象程序设计中的类相似,但实现不同
  • 0582、Object 是一个基础类型,其他所有类型都从 Object 继承了基本的行为
  • 0583、Array 类型是一组值的有序列表,同时还提供了操作和转换这些值的功能
  • 0584、Date 类型提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能
  • 0585、RegExp 类型是 ECMAScript 支持正则表达式的一个接口,提供了最基本的和一些高级的正则表达式功能
  • 0586、函数实际上是 Function 类型的实例,因此函数也是对象;而这一点正是 JavaScript 最有特色的地方。由于函数是对象,所以函数也拥有方法,可以用来增强其行为
  • 0587、因为有了基本包装类型,所以 JavaScript 中的基本类型值可以被当作对象来访问。三种基本包装类型分别是: Boolean 、 Number 和 String 。以下是它们共同的特征
  • 0588、在所有代码执行之前,作用域中就已经存在两个内置对象: Global 和 Math 。在大多数 ECMAScript实现中都不能直接访问 Global 对象;不过,Web 浏览器实现了承担该角色的 window 对象。全局变量和函数都是 Global 对象的属性。 Math 对象提供了很多属性和方法,用于辅助完成复杂的数学计算任务

第六章、面向对象的程序设计

  • 0601、ECMAScript中有两种属性:数据属性和访问器属性。数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。对于像前面例子中那样直接在对象上定义的属性,它们的[[Configurable]] 、[[Enumerable]]和[[Writable]]特性都被设置为 true ,而[[Value]]特性被设置为指定的值。要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是:configurable、enumerable 、 writable和value。设置其中的一或多个值,可以修改对应的特性值。可以多次调用Object.defineProperty() 方法修改同一个属性,但在把configurable特性设置为 false 之后就会有限制了。在调用Object.defineProperty() 方法时,如果不指定,configurable、enumerable 和writable特性的默认值都是false。多数情况下,可能都没有必要利用Object.defineProperty()方法提供的这些高级功能
  • 0602、访问器属性不包含数据值;它们包含一对儿getter 和 setter函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性:[[Configurable]]、[[Enumerable]]、[[Get]]、[[Set]]
  • 0603、在不支持Object.defineProperty()方法的浏览器中不能修改[[Configurable]]和[[Enumerable]]
  • 0604、支持 Object.defineProperties()方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome
  • 0605、在 JavaScript 中,可以针对任何对象——包括 DOM 和 BOM 对象,使用 Object.getOwnProperty-Descriptor() 方法。支持这个方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 12+和 Chrome
  • 0606、工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)
  • 0607、构造器模式。没有显式地创建对象;直接将属性和方法赋给了 this 对象;没有 return 语句。以这种方式调用构造函数实际上会经历以下 4个步骤:创建一个新对象; 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);执行构造函数中的代码(为这个新对象添加属性);返回新对象。以这种方式定义的构造函数是定义在 Global 对象(在浏览器中是 window 对象)中的。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。在前面的例子中, person1 和 person2 都有一个名为 sayName() 的方法,但那两个方法不是同一个 Function 的实例
  • 0608、 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中
  • 0609、无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针
  • 0610、使用 Object.getPrototypeOf()可以方便地取得一个对象的原型,而这在利用原型实现继承(本章稍后会讨论)的情况下是非常重要的。支持这个方法的浏览器有 IE9+、Firefox 3.5+、Safari 5+、Opera 12+和 Chrome
  • 0611、使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性
  • 0612、使用 hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从 Object 继承来的)只在给定属性存在于对象实例中时,才会返回 true
  • 0613、ECMAScript 5 的 Object.getOwnPropertyDescriptor() 方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用 Object.getOwnProperty-Descriptor() 方法
  • 0614、有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。在单独使用时, in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中
  • 0615、由于 in 操作符只要通过对象能够访问到属性就返回 true , hasOwnProperty() 只在属性存在于实例中时才返回 true ,因此只要 in 操作符返回 true 而 hasOwnProperty() 返回 false ,就可以确定属性是原型中的属性
  • 0616、要取得对象上所有可枚举的实例属性,可以使用 ECMAScript 5 的 Object.keys() 方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
  • 0617、如果你想要得到所有实例属性,无论它是否可枚举,都可以使用 Object.getOwnPropertyNames()方法。 Object.keys() 和 Object.getOwnProperty-Names() 方法都可以用来替代 for-in 循环。支持这两个方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera12+和 Chrome
  • 0618、实例与原型之间的连接只不过是一个指针,而非一个副本,因此就可以在原型中找到新的 sayHi 属性并返回保存在那里的函数
  • 0619、原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由其共享的本性所导致的
  • 0620、混合模式。创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。这种构造函数与原型混成的模式,是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式
  • 0621、使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系
  • 0622、关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式
  • 0623、继承是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于函数没有签名,在 ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的
  • 0624、构造函数、原型和实例的关系。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针
  • 0625、谨慎地定义方法。子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后
  • 0626、通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链
  • 0627、在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术(有时候也叫做伪造对象或经典继承)。这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用 apply() 和 call() 方法也可以在(将来)新创建的对象上执行构造函数
  • 0628、相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数
  • 0629、如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的
  • 0630、ECMAScript 5 通过新增 Object.create() 方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create() 与 object() 方法的行为相同。Object.create() 方法的第二个参数与 Object.defineProperties() 方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性
  • 0631、寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
  • 0632、 工厂模式,使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式后来被构造函数模式所取代
  • 0633、 构造函数模式,可以创建自定义引用类型,可以像创建内置对象实例一样使用 new 操作符。不过,构造函数模式也有缺点,即它的每个成员都无法得到复用,包括函数。由于函数可以不局限于任何对象(即与对象具有松散耦合的特点),因此没有理由不在多个对象间共享函数
  • 0634、原型模式,使用构造函数的 prototype 属性来指定那些应该共享的属性和方法。组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法
  • 0635、JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。 寄生组合式继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式
  • 0636、当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境

第七章 函数表达式

  • 0701、由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,我们建议读者只在绝对必要时再考虑使用闭包。虽然像 V8 等优化后的 JavaScript 引擎会尝试回收被闭包占用的内存,但请大家还是要慎重使用闭包
  • 0702、在闭包中使用 this 对象也可能会导致一些问题。我们知道, this 对象是在运行时基于函数的执行环境绑定的:在全局函数中, this 等于 window ,而当函数被作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向window。但有时候由于编写闭包的方式不同,这一点可能不会那么明显
  • 0703、如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁
  • 0704、闭包会引用包含函数的整个活动对象,而其中包含着 element 。即使闭包不直接引用 element ,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把 element 变量设置为 null 。这样就能够解除对 DOM 对象的引用,顺利地减少其引用数,确保正常回收其占用的内存
  • 0705、函数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。要将函数声明转换成函数表达式,只要像下面这样给它加上一对圆括号即可
  • 0706、一般来说,我们都应该尽量少向全局作用域中添加变量和函数。在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员既可以使用自己的变量,又不必担心搞乱全局作用域
  • 0707、严格来讲,JavaScript 中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
  • 0708、构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题
  • 0709、初始化未经声明的变量,总是会创建一个全局变量
  • 0710、多查找作用域链中的一个层次,就会在一定程度上影响查找速度。而这正是使用闭包和私有变量的一个显明的不足之处
  • 0711、 函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫做匿名函数
  • 0712、在无法确定如何引用函数的情况下,递归函数就会变得比较复杂
  • 0713、 递归函数应该始终使用 arguments.callee 来递归地调用自身,不要使用函数名——函数名可能会发生变化
  • 0714、当在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量:在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域;通常,函数的作用域及其所有变量都会在函数执行结束后被销毁;但是,当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止
  • 0715、使用闭包可以在 JavaScript中模仿块级作用域(JavaScript本身没有块级作用域的概念)
  • 0716、JavaScript 中的函数表达式和闭包都是极其有用的特性,利用它们可以实现很多功能。不过,因为创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存

第八章 BOM

  • 0801、抛开全局变量会成为 window 对象的属性不谈,定义全局变量与在 window 对象上直接定义属性还是有一点差别:全局变量不能通过 delete 操作符删除,而直接在 window 对象上的定义的属性可以
  • 0802、刚才使用 var 语句添加的 window 属性有一个名为 [[Configurable]] 的特性,这个特性的值被设置为 false ,因此这样定义的属性不可以通过 delete 操作符删除。IE8及更早版本在遇到使用 delete删除 window 属性的语句时,不管该属性最初是如何创建的,都会抛出错误,以示警告。IE9 及更高版本不会抛出错误
  • 0803、尝试访问未声明的变量会抛出错误,但是通过查询 window 对象,可以知道某个可能未声明的变量是否存在202、窗口位置。用来确定和修改 window 对象位置的属性和方法有很多。IE、Safari、Opera 和 Chrome 都提供了screenLeft 和 screenTop 属性,分别用于表示窗口相对于屏幕左边和上边的位置。Firefox 则在screenX 和 screenY 属性中提供相同的窗口位置信息,Safari 和 Chrome 也同时支持这两个属性。Opera虽然也支持 screenX 和 screenY 属性,但与 screenLeft 和 screenTop 属性并不对应,因此建议大家不要在 Opera 中使用它们
  • 0804、最终结果,就是无法在跨浏览器的条件下取得窗口左边和上边的精确坐标值。然而,使用 moveTo()和 moveBy() 方法倒是有可能将窗口精确地移动到一个新位置。这两个方法都接收两个参数,其中moveTo() 接收的是新位置的 x 和 y 坐标值,而 moveBy() 接收的是在水平和垂直方向上移动的像素数。
  • 0805、需要注意的是,这两个方法可能会被浏览器禁用;而且,在 Opera 和 IE 7(及更高版本)中默认就是禁用的。另外,这两个方法都不适用于框架,只能对最外层的 window 对象使用
  • 0806、跨浏览器确定一个窗口的大小不是一件简单的事。IE9+、Firefox、Safari、Opera 和 Chrome 均为此提供了 4个属性: innerWidth、 innerHeight 、 outerWidth 和 outerHeight 。在 IE9+、Safari和 Firefox中, outerWidth 和 outerHeight 返回浏览器窗口本身的尺寸(无论是从最外层的 window 对象还是从某个框架访问)。在Opera中,这两个属性的值表示页面视图容器① 的大小。而innerWidth 和 innerHeight则表示该容器中页面视图区的大小(减去边框宽度)。在 Chrome 中, outerWidth 、 outerHeight 与innerWidth 、 innerHeight 返回相同的值,即视口(viewport)大小而非浏览器窗口大小
  • 0807、在 IE、Firefox、Safari、Opera 和 Chrome 中, document.documentElement.clientWidth 和document.documentElement.clientHeight 中保存了页面视口的信息。在 IE6 中,这些属性必须在标准模式下才有效;如果是混杂模式,就必须通过 document.body.clientWidth 和 document.body.clientHeight 取得相同信息。而对于混杂模式下的 Chrome,则无论通过 document.documentEle-ment 还是 document.body 中的 clientWidth 和 clientHeight 属性,都可以取得视口的大小
  • 0808、对于移动设备, window.innerWidth 和 window.innerHeight 保存着可见视口,也就是屏幕上可见页面区域的大小。移动 IE 浏览器不支持这些属性,但通过 document.documentElement.client-Width 和 document.documentElement.clientHeihgt 提供了相同的信息。随着页面的缩放,这些值也会相应变化
  • 0809、有关移动设备视口的话题比较复杂,有很多非常规的情形,也有各种各样的建议。移动开发咨询师 Peter-Paul Koch 记述了他对这个问题的研究:http://t.cn/zOZs0Tz。如果你在做移动 Web 开发,推荐你读一读这篇文章
  • 0810、 window.open() 方法既可以导航到一个特定的 URL,也可以打开一个新的浏览器窗口。这个方法可以接收 4 个参数:要加载的 URL、窗口目标、一个特性字符串以及一个表示新页面是否取代浏览器历史记录中当前加载页面的布尔值。通常只须传递第一个参数,最后一个参数只在不打开新窗口的情况下使用。后面这行代码会打开一个新的可以调整大小的窗口,窗口初始大小为 400×400 像素,并且距屏幕上沿和左边各 10 像素。window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");
  • 0811、wroxWin.close()这个方法仅适用于通过 window.open() 打开的弹出窗口。对于浏览器的主窗口,如果没有得到用户的允许是不能关闭它的。不过,弹出窗口倒是可以调用 top.close() 在不经用户允许的情况下关闭自己。弹出窗口关闭之后,窗口的引用仍然还在,但除了像下面这样检测其 closed 属性之外,已经没有其他用处了
  • 0812、超时调用需要使用 window 对象的 setTimeout() 方法,它接受两个参数:要执行的代码和以毫秒表示的时间(即在执行代码前需要等待多少毫秒)。由于传递字符串可能导致性能损失,因此不建议以字符串作为第一个参数
  • 0813、JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个 JavaScript 任务队列。这些任务会按照将它们添加到队列的顺序执行。 setTimeout() 的第二个参数告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行
  • 0814、超时调用的代码都是在全局作用域中执行的,因此函数中 this 的值在非严格模式下指向 window 对象,在严格模式下是 undefined
  • 0815、一般认为,使用超时调用来模拟间歇调用的是一种最佳模式。在开发环境下,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动。而像前面示例中那样使用超时调用,则完全可以避免这一点。所以,最好不要使用间歇调用
  • 0816、通过 JavaScript 打开的对话框,即“查找”和“打印”。这两个对话框都是异步显示的,能够将控制权立即交还给脚本。这两个对话框与用户通过浏览器菜单的“查找”和“打印”命令打开的对话框相同。而在 JavaScript 中则可以像下面这样通过 window 对象的 find() 和 print() 方法打开它们
  • 0817、URL获取参数并封装成对象。这个函数的第一步是先去掉查询字符串开头的问号。当然,前提是 location.search 中必须要包含一或多个字符。然后,所有参数将被保存在 args 对象中,该对象以字面量形式创建。接下来,根据和号(&)来分割查询字符串,并返回 name=value 格式的字符串数组。下面的 for 循环会迭代这个数组,然后再根据等于号分割每一项,从而返回第一项为参数名,第二项为参数值的数组。再使用 decodeURIComponent() 分别解码 name 和 value (因为查询字符串应该是被编码过的)。最后,将 name 作为 args 对象的属性,将 value 作为相应属性的值
  • 0818、每次修改 location 的属性( hash 除外),页面都会以新 URL 重新加载
  • 0819、在 IE8、Firefox 1、Safari 2+、Opera 9+和 Chrome 中,修改 hash 的值会在浏览器的历史记录中生成一条新记录。在 IE 的早期版本中, hash 属性不会在用户单击“后退”和“前进”按钮时被更新,而只会在用户单击包含 hash 的 URL 时才会被更新
  • 0820、replace() 方法。这个方法只接受一个参数,即要导航到的 URL;结果虽然会导致浏览器位置改变,但不会在历史记录中生成新记录。在调用 replace() 方法之后,用户不能回到前一个页面
  • 0821、与位置有关的最后一个方法是 reload() ,作用是重新加载当前显示的页面。如果调用 reload()时不传递任何参数,页面就会以最有效的方式重新加载。也就是说,如果页面自上次请求以来并没有改变过,页面就会从浏览器缓存中重新加载。如果要强制从服务器重新加载,则需要像下面这样为该方法传递参数 true。位于 reload() 调用之后的代码可能会也可能不会执行,这要取决于网络延迟或系统资源等因素。为此,最好将 reload() 放在代码的最后一行
  • 0822、检测浏览器中是否安装了特定的插件是一种最常见的检测例程。对于非 IE 浏览器,可以使用plugins 数组来达到这个目的。
  • 0823、检测 IE 中的插件比较麻烦,因为 IE 不支持 Netscape 式的插件。在 IE 中检测插件的唯一方式就是使用专有的 ActiveXObject 类型,并尝试创建一个特定插件的实例。IE 是以 COM对象的方式实现插件的,而 COM对象使用唯一标识符来标识。因此,要想检查特定的插件,就必须知道其 COM 标识符。例如,Flash 的标识符是 ShockwaveFlash.ShockwaveFlash
  • 0824、plugins 集合有一个名叫 refresh() 的方法,用于刷新 plugins 以反映最新安装的插件。这个方法接收一个参数:表示是否应该重新加载页面的一个布尔值。如果将这个值设置为 true ,则会重新加载包含插件的所有页面;否则,只更新 plugins集合,不重新加载页面
  • 0825、history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。因为 history 是 window对象的属性,因此每个浏览器窗口、每个标签页乃至每个框架,都有自己的 history 对象与特定的window 对象关联
  • 0826、使用 go() 方法可以在用户的历史记录中任意跳转,可以向后也可以向前。这个方法接受一个参数,表示向后或向前跳转的页面数的一个整数值。负数表示向后跳转(类似于单击浏览器的“后退”按钮),正数表示向前跳转(类似于单击浏览器的“前进”按钮)。也可以给 go() 方法传递一个字符串参数,此时浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进,具体要看哪个位置最近。如果历史记录中不包含该字符串,那么这个方法什么也不做
  • 0827、另外,还可以使用两个简写方法 back() 和 forward() 来代替 go() 。顾名思义,这两个方法可以模仿浏览器的“后退”和“前进”按钮
  • 0828、当页面的 URL 改变时,就会生成一条历史记录。在 IE8 及更高版本、Opera、Firefox、Safari 3 及更高版本以及 Chrome 中,这里所说的改变包括 URL 中 hash 的变化(因此,设置 location.hash 会在这些浏览器中生成一条新的历史记录)
  • 0829、浏览器对象模型(BOM)以 window 对象为依托,表示浏览器窗口以及页面可见区域。同时, window对象还是 ECMAScript 中的 Global 对象,因而所有全局变量和函数都是它的属性,且所有原生的构造函数及其他函数也都存在于它的命名空间下

第九章 客户端检测

  • 0901、检测 Web 客户端的手段很多,而且各有利弊。但最重要的还是要知道,不到万不得已,就不要使用客户端检测。只要能找到更通用的方法,就应该优先采用更通用的方法。一言以蔽之,先设计最通用的方案,然后再使用特定于浏览器的技术增强该方案
  • 0902、在实际开发中,应该将能力检测作为确定下一步解决方案的依据,而不是用它来判断用户使用的是什么浏览器
  • 0903、与能力检测类似,怪癖检测(quirks detection)的目标是识别浏览器的特殊行为。但与能力检测确认浏览器支持什么能力不同,怪癖检测是想要知道浏览器存在什么缺陷(“怪癖”也就是 bug)
  • 0904、能力检测:在编写代码之前先检测特定浏览器的能力
  • 0905、 怪癖检测:怪癖实际上是浏览器实现中存在的 bug
  • 0906、用户代理检测:通过检测用户代理字符串来识别浏览器
  • 0907、在决定使用哪种客户端检测方法时,一般应优先考虑使用能力检测。怪癖检测是确定应该如何处理代码的第二选择。而用户代理检测则是客户端检测的最后一种方案,因为这种方法对用户代理字符串具有很强的依赖性


科瑞兹曼
72 声望7 粉丝