红宝书读书笔记

ECMAScript 与 javaScript 的区别:

 

第三章 基本概念

字面量:给变量赋值时,等号右边都可以认为是字面量。 比如: '8' 字符串字面量 ;8 数字字面量 ;

对于尚未声明过的变量,只能执行一项操作,即使用 typeof 操作符检测其数据类型。

Undefined:变量声明,但是未赋值初始化此时的值就是undefined。

Null:类型表示一个空对象指针。null被认为是一个空对象的引用。

保存浮点数需要的内存空间整数两倍,因此ECMAScript会不失时机的将浮点数值转换为整数,如果小数点后面没有跟任何数字,那么这个数值就可以作为整数值来保存。

同样的如果浮点数本身表示的就是一个整数,那么该值也会被转换成一个整数。

对于极大或者极小的数可以用e来表示(科学计数法),一个数值,中间是大写或小写的e,后面是10的幂中的指数。

浮点数值的最高精度是17位小数,但是进行算数计算其精度远远不如整数。

永远不要测试某个特定的浮点数值。(这是基于使用IEEE754数值的浮点计算的通病,ECMAScript并非独此一家,其它使用相同数据格式的语言也存在这个问题。)

Number.MIN_VALUE 5e-324

Number.MAX_VALUE 1.7976931348623157e+308

Infinity:超过最小或最大值,即为负无穷或正无穷。

isFinity():函数用来测试是否在范围之内,范围内返回true。

NaN:即非数值,用于表示一个本来要返回数值的操作数未返回数值的情况。

任何数值除以非数值都会返回NaN,因此不影响其他代码的执行。

NaN,两个特性,任何涉及NaN操作,都将返回NaNNaN与任何值不相等,包括NaN本身

isNaN() 在接受一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,比如字符串数字布尔值等。任何不能被转换为数值的值都会导致这个函数返回true。

三个函数可以把非数值,转换为数值

Number():可以用于任何数据类型    null返回0

parseInt():字符串 转数值   解析数字,第一个字符是不是数字直接返回NaN,遇到非数字直接忽略,只返回数字

parseFloat():字符串 转数值   也是从头开始解析到尾,遇见的第一个点有效,第二个点就是无效了,因此它后面的字符串将被忽略。始终忽略前导0,整数返回整数。

字符字面量,也叫转义序列,用于表示非打印字符。

\n 换行  \t 制表  \b 退格  \r 回车  \f 进纸  \\ 斜杠  \' 单引  \" 双引

\xnn  以十六进制代码nn表示一个字符

\unnn 以十六进制代码nnn表示一个unicode字符

任何字符串的长度都可以通过访问其length属性取得。

字符串特点:ECMAScript中的字符串是不可变的,一旦创建,值即是永久,不可改变。

要改变某个变量保存的字符串,首先要销毁原来的字符串,然后在用另一个包含新值的字符串填充该变量。

例如: var lang = "Java"

                  lang = lang + "Script"

把一个值转换为一个字符串:

l例如:var age = 11;

       ageAsString = age.toString();

toString(): 可以输入一个参数,来决定转换输出的进制 , null 和 undefined 值没有toString()方法,所以在不知道包不包含null和undefined时,可以使用转型函数String()

把某个值转换为字符串,可以使用加号操作符,把它与一个字符串("")加在一起。

Object:ECMAScript中的对象就是一组数据和功能的结合。可以通过执行 new 操作符后跟要创建对象类型的名称来创建。

Object是所有对象的基础,所以它的每个实例都具有下列属性和方法:

  constructor:保存着用于创建当前对象的函数。构造函数 (constructor) 就是 Object ()

  hasOwnProperty(proprertyName):用于检查给定的属性在当前对象中是否存在,作为参数的属性名字,必须以字符串的形式,加引号。

  isPrototypeOf(object):用于检查传入的对象时否是当前对象的原型。

  propertyIsEnumerable(propertyName):用于检查传入对象是否是当前对象的原型。

  toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。

       toString():返回对象的字符串表示。

       valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值相同。    

布尔操作符:

逻辑非!,逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。

同时使用两个逻辑非!!操作符,与对此数值使用Boolean()函数相同,将一个值转换为与其对应的布尔值。

两个和号&&,逻辑与操作属于短路操作,如果第一个操作数为false,即不会对第二个操作数求值。

两个竖线符号||,逻辑或操作,如果第一个操作数为true,同样短路逻辑不会继续操作后面的。

ps:不能在在逻辑与或操作中使用未定义的值。

求模,操作符由一个(%)表示。 //  var result= 26 % 5 ;  // 值为1

字符串加什么都是字符串,()小括号可以提高运算的优先级。    // var ha = " hahahahah " + (6 + 7)

ECMAScript相等由两个等号操作符表示(==) ,不相等(!=),比较之前转换操作数。 // = 为赋值

全等由三个等于号表示(===),,不全等(!==),比较之前不转换操作数。

==================================完成量1/15======================================

逗号操作符多用于声明多个变量;逗号操作符还可以用来赋值,并总会返回表达式中的最后一项:var num = (5,2,6,0)  // num 的值为 0

do-while:语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次,因此需要虚幻提中代码至少被执行一次的情形,是十分适合do-while的。

while:语句是前测试循环语句,先判断再执行,循环体内的代码可能永远不会被执行。

for 循环:如果将初始化表达式,控制表达式和循环后表达式全部省略,就会创建一个无限循环。               

       for (;;){ // 无限循环

              doSomething();    

       }

       而只给出控制表达式实际上就是把 for 循环转换成了 while 循环。

       var count = 10;

       var i = 0;

       for(;i < count;){

           alert(i);

           i++

       }

for-in:语句是一种精准的迭代语句,可以用来枚举对象的属性。如果表示要迭代的对象的变量值为 null 或 undefined,for-in 语句会抛出错误。(为了保证兼容性:ES5已更改为不抛出错误,只是不执行循环体)

     forvar propName in window){

          document.write(propName);

     }

label 语句:定义的标签可以再将来由 break 或 continue 语句引用,一般都要与 for 循环配合使用。场景:循环嵌套。

break语句:会立即退出循环,强制继续执行循环后面的语句。continue 语句,也是立刻退出循环,但是退出后会从循环的顶部继续执行。

switch 语句: 中的每一个情形(case)的含义是,如果表达式等于这个值(value),则执行后面的语句(statement)。而 break 关键字会导致代码执行流跳出 switch 语句。如果省略 break 关键字,就会导致执行完当前 case 后,继续执行下一个 case 。最后的 default 关键字则用于在表达式不匹配前面任何一种形式的时候,执行激动代码(因此,也相当于 else 语句)。当有一些故意省略的 break 造成混合时,记得写注释。

        switch (expression){

                case value:statement

                   break;

             case value:statement

                   breakcase value:statement

                   breakdefault:statement

         }

可以在 switch 语句中使用任何数据类型。其次,每个 case 的值不一定是常量,可以是变量,甚至是表达式。

switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换。          

函数在执行完 return 语句之后停止并立即退出。推荐的做法是要么让函数始终返回一个值,要么永远都不要返回值。严格模式下,函数名和参数名不能是 eval 或 arguments,不能出现同名参数。

ECMAScript 函数不介意参数的个数数据类型,这是因为在ECMAScript中的参数在内部是用一个数组来表示的。函数接受到的始终都是这个数组,而不关心数组中包含哪些参数。

实际上在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。

arguments 对象只是与数组类似,因为可以使用方括号语法访问它的每一个属性,使用 length 属性来确定传递进来多少个参数。

arguments 的值永远与对应命名参数的值保持同步,比如 第一个参数 和 arguments[0],内存空间相对独立,但是值保持相同。(严格模式下不允许如此修改)

关于参数,没有传递值的参数将自动被赋予 undefined 值。

没有函数的签名,真正的重载是不可能做到的。

如果在ECMAScript中,将一个函数名定义了两次,则后面的生效,名字只属于后定义的函数。

小结:

①没有为整数和浮点数分别定义不同的数据类型,Number 类型可用于表示所有数值。

②Object 是这门语言所有对象的基础类型。

③严格模式为这门语言中容易出错的地方施加了限制。

④无需指定函数的返回值,因为任何ECMAScript函数都可以在任何时候返回任何值。

⑤实际上,未指定返回值的函数返回的是一个特殊的 undefined 值。

⑥ECMAScript 中也没有函数签名的概念,因为其函数参数是以一个包含零或多个值的数组的形式传递的。

⑦可以向函数传递任意数量的参数,并可以通过 arguments 对象来访问这些参数。

⑧由于不存在函数签名的特性,所以ECMAScript 函数不能重载。 (是能实现类似重载的功能的) 

 

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

JavaScript 变量松散类型的本质,决定了它只在特定的时间用于保存特定值的一个名字而已。由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变。这可能是一个既有趣又强大,同时又容易出问题的特性。

基本类型指的是简单的数据段,而引用类型指那些可能由多个值构成的对象

五大基本类型是按值访问的,因为可以操作保存在变量中的实际的值。

引用类型的值是保存在内存中的对象js不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,在操作对象时实际上是操作对象的引用,而不是实际的对象。为此,

引用类型的值是按引用访问的。对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。

var num1 = 5;

var num2 = num1;

当使用 num1 的值来初始化 num2 时,num2 中也保存了值 5 ,但是 num2 中的 5 与 num1 中的 5 是完全独立的,该值只是 num1 中 5 的一个副本。两个变量任意操作而且不会相互影响。

引用类型与此相反,复制后,原对象与副本是一个指针,指向存储在堆中的同一个对象。因为引用相同的对象,所以互相影响。

传递参数:ECMAScript 中所有函数的参数都是 按值传递的。访问变量有按值按引两种方式。

检查类型:

typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具。

如果变量的值是一个 对象 null ,则 typeof 操作符会返回 "object" 。

虽然在检测基本数据类型时 typeof 是非常得力的助手,但在检测引用类型的值时,这个操作符作用不大。为此ECAMScript提供 instanceof 操作符

person instanceof Object;   // 变量 person 是Object 么?

colors instanceif Array;       // 变量 colors 是 Array 么?

pattern instanceof RegExp;  // 变量 pattern 是 RegExp么?

如果使用 instanceof 检测基本类型的值时会返回 false,因为基本类型不是对象。

执行环境和作用域:

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有函数和变量都保存在这个对象中。代码无法

访问,但解析器处理数据的时候会在后台使用它。

全局执行环境(ECS)是最外围的一个执行环境,根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是 window 对象,

因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。

某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的变量和函数定义也随之销毁(全局执行环境直到应用程序退出-列如关闭网页或浏览器-时才会被销毁)。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行后,栈将其环境弹出,把控制权返回给之前的执行环境。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终

都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这

个对象在全局环境中是不存在的)。全局执行环境的变量对象始终都是作用域链中的最后一个对象。

局部环境找不到东西,可以到它的父执行环境中去找。

函数的参数也被当做变量来对待,因此其访问规则与执行环境中的其他变量相同。

延长作用域链:  try-catch 语句的 catch 块; 和  with 语句; 这两个语句都会在作用域链的前端添加一个变量对象。对 with 语句来说,会将指定的对象添加到作用域链中。对 catch

语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。

提示:with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,如果可能,最好避免使用它。

for 语句创建的变量 i 即使在 for 循环执行结束后,也依然会存在于外部的执行环境中。

JS中,变量要先声明再初始化。

4.3 垃圾清除

(1) 标记清除:js中最常用的垃圾清除方式就是标记清除(mark-and-sweep)。当变量进入环境中(比如:在环境中声明一个变量)时,就将这个变量标记为 "进入环境" ,而当变量

离开环境时,则将其标记为 "离开环境"。

     可以使用任何方式来标记变量。比如,通过翻转某个特殊的位来记录一个变量何时进入环境 或者 使用以一个变量列表,来记录跟踪变量的变化。

     垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视  

为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

     2008年为止,主流浏览器基本都是标记清除式的垃圾收集策略,只不过垃圾收集的时间间隔不同。

(2) 引用计数:即跟踪记录每个值被引用的次数。引用标记+1,包含对这个值引用的变量取得了另外一个值-1,可能会出现循环引用问题,为了避免这类问题,最好在不使用他们的时

候手动断开原生 JavaScript 对象与 DOM 元素之间的连接。

myObject.element = null;

element.someObject = null;

将变量设置为 null 意味着切断变量与它此前引用值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存空间。 

垃圾收集器的临界值是动态修正,在浏览器中可触发垃圾收集,但是不建议这样做。

window.CollectGarbage();  // IE 立即执行垃圾收集

window.opera.collect();       // opera 启动垃圾收集例程

管理内存:

对于具备垃圾收集机制的语言编写程序,开发人员一般不必操心内存管理问题。但是,JavaScript 在进行内存管理及垃圾收集时面临的问题还是有些不同,其中最主要的就是,分配给

Web 浏览器的可用内存数量通常要比分配给桌面应用系统的少。这样做的目的是出于安全的考虑,防止JS网页耗尽全部内存导致系统崩溃。

因此,确保占用最少的内存可以让页面获得更好的性能。优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦不再有用,最好通过将其值设置为 null 来释放其引

用——这个方法叫做解除引用(dereferencing)。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

小结:

1.JavaScript 变量可以保存两种类型的值:基本类型和引用类型值。基本类型和引用类型有以下特点:

①基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;

②从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;

③引用类型的值是对象,保存在堆内存中;

④包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;

⑤从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;

⑥确定一个值是哪种基本类型可以使用 typeof 操作符,确定一个值是哪种引用类型可以使用 instanceof 操作符。

2.所有变量(包括基本类型和引用类型)都存在一个执行环境(也称作用域)当中,这个执行环境决定了变量的生命周期,以及那一部分代码可以访问其中的变量。以下是关于执行环境的几点总结:

①执行环境有全局执行环境(也称为全局环境)和 执行环境 之分;

②每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;

③函数的局部环境不仅有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境;

④全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;

⑤变量的执行环境有助于确定应该何时释放内存。

3.JS是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题。JS的垃圾收集例程总结如下:

①离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除。

②"标记清除" 是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存。

③另一种垃圾收集算法是"引用计数",这种算法的思想是跟踪记录所有值被引用的次数。JS引擎目前都不再使用这种算法;但在IE中访问非原生 JavaScript 对象(如DOM元素)时,这种算法仍然可能会导致问题。

④当代码中存在循环引用现象时,"引用计数"算法就会导致问题。

⑤解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象,全局对象属性以及循环引用变量的引用。

 

第五章 引用类型

引用类型的值是一个引用类型的实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织到一起。尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备

传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为他们描述的是一类对象所具有的属性和方法。

新对象是使用 new 操作符后跟一个构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

var person = new Object();

创建Object引用类型的一个新实例,然后把实例保存在变量 person 中,使用的构造函数是Object,它只为新对象定义了默认的属性和方法。ECMAScript 提供了很多原生引用类型(例如:Object),以便开发人员实现日常的计算任务。

5.1 Object 类型

创建 Object 实例的方式有两种:

第一种是 new 操作符后跟 Object 构造函数

     var person = new Object();

     person.name = "Nicholas";

     person.age = 29; 

第二种是使用对象字面量表示法:       

     var person = {             

        name:"Nicholas",    

        age:29   

     } 

          // 属性名也可以加引号,这里的属性名是自动转换为字符串。

          // 对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象过程

          // 通过对象字面量定义对象的时候,实际上不会调用 Object 构造函数。(firefox3之后就不调用了)

在这个例子中,左边的花括号,左边的花括号表示对象字面量的开始,因为它出现在了表达式的上下文(expression context)中。ECMAScript中的表达式上下文指的是该上下文期

待的一个值(表达式)。赋值操作符表示后面是一个值,所以左花括号在这里表示一个表达式的开始。同样的花括号,如果出现在一个语句上下文(statement context)中,例如跟

在if语句条件的后面,则表示一个语句块的开始。

开发人员更青睐对象字面量语法,因为代码量更少,而且又封装数据的感觉。对象字面量,也是向函数传递大量可选参数的首选方法。

一般来讲命名参数虽然容易处理,但在有多个可选参数的情况下就显得不够灵活。最好的做法是对那些必需值使用命名参数,而使用对象字面量来封装多个可选参数。

一般来说访问对象属性时使用的都是点表示法,这也是很多面向对象语言中通用的语法。不过在js中也可以使用方括号表示法来访问对象的属性。在使用方括号语法时,应该将要访问

的属性以字符串的形式放在方括号中。   

例如: person.name;            //"Nicholas"

            person[ " name " ] ;   //"Nicholas"

从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量来访问属性。

例如:var propertyName = "name";

           alert(person[ propertyName ]);    //"Nicholas"

如果属性名中包含导致语法错误的字符,或者属性名使用的是关键字或保留字,也可以使用方括号表示法。

           person[ " first name " ]  = "Nicholas";   

由于 "first name" 中包含一个空格,所以不能使用点表示法来访问它。因为,属性名是可以包含非字母,非数字的,这时候就可以使用方括号表示法来访问。

通常,除非必须使用变量来访问属性,否则我们建议使用点表示法

5.2 Array 类型

ECMAScript 数组与其它语言中的数组都是 数据的有序列表。不同的是,ECMAScript 数组的每一项可以保存任何类型的数据。而且ECMAScript数组大小是可以动态调整的,即可随着数据增加自动增长以容纳新增数据。  

创建数组方式有两种:

第一种是使用 Array 构造函数

     var colors = new Array();

     //如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量,该值会自动变成 length 属性的值。

     var colors = new Array(20);    // length值为20的数组

     //也可以直接传入数组包含项    

     //使用 Array 构造函数时也可以省略 new 操作符。

第二种基本方式是使用数组字面量表示法

     //数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开。

     //与对象一样,在使用数组字面量表示法时,也不会调用 Array 构造函数。

  //因为数组的索引始终是 length-1 ,因此下一个新项的位置就是 length 。即长度值等于最后一项的索引加1。

     // var colors = [ "red","blue","green" ]; 创建一个包含3个字符串的数组

     // colors[colors.length] = "black";                 (在位置3)添加一种颜色

5.2.1 检测数组   PS:可以参考JS数组API

自从ECMAScript 3 做出规定之后,就出现了确定某个对象是不是数组的经典问题。

对于一个网页,一个全局作用域而言,instanceof 操作符就可以满足。

但是如果网页包含两个或者多个框架,那就存在多个不同的全局执行环境。从而存在多个不同版本的 Array 构造函数。ECMAScript 5 为此新增 Array.isArray()。这个方法的目的最

终是确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。

栈方法:数组可以表现的像栈一样,后者可以限制插入和数据结构。栈是一种LIFO(last in first out)的数据结构。最新添加的项最早被移除。ECMAScript 提供 push()和 pop()方法。

push():栈顶推入,

pop():栈顶移除。在调用 pop 时,它会返回数组的最后一项。

队列方法:栈数据结构的访问规则是 LIFO(Last in First Out,后进先出),而队列数据结构的访问规则是 FIFO(Fist-In-Fist-out,先进先出)。队列从列表前端,末端添加。

shift():移除数组的第一个项,并返回该项。

unshift():在数组前端添加任意项,并返回新数组长度。

reverse():反转数组顺序。

sort():该方法会调用每个数组的 toString()转型方法,其比较的也是字符串。默认情况下,sort()方法按升序排列数组项。

concat():该方法会先创建一个当前数组的副本然后将接收的参数放在副本的末尾,最后返回新构建的数组。无参数,只是复制副本并返回副本。如果传递的参数是一个或多个数

组,则将每一项添加到结果数组中。不影响原来的数组。

slice():基于当前数组的一个或者多个项创建一个新数组,可接受两个参数,起始 和 结束 的位置 ,一个 参数就是 起始 至 最后。参数为负,直接用数组长度加上参数确定相应位置。

splice():数组最强大方法。主要用途是向数组中部插入项。

                     有3种用法:

                      删除:可以删除任意数量的项,只需要指定两个参数。即要删除 第一项的位置,要删除的项数。

                      插入:可以向指定位置插入任意数量的项,只需指定三个参数。起始位置,0 (要删除的项数),要插入的项。 

                      替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定三个参数。起始位置,要删除的项数,要插入任意数量的项。 

                      // splice()方法始终会返回一个数组,包含的是从原数组删除的项。

                      // var colors = ["red","blue","green"];

                      // var removed = colors.splice( 1,1,"yellow" );  删除一项,插入一项

                      // alert (colors);        // red,yellow,green

                      // alert (removed);    // blue

ES5为数组实例添加了两个位置方法:indexOf()和 lastIndexOf()。都接收两个参数:要查找的项 和 (可选)表示查找起点位置的索引。indexOf()从前往后查找,

lastIndexOf()从后往前查找。这俩个方法都返回要查找的项在数组中的位置,没找到返回 -1 。要查找的项必须严格相等,第一个参数与数组每一个项作比较时,使用的就是全等操

作。

5个数组迭代方法:所谓的迭代,就是对数组的每一项都进行相应的操作。

every():给数组每一项运行给定函数,每一项都返回 true,则返回true。

filter():给数组每一项运行给定函数,返回 true 项,组成的数组。

forEach():给数组每一项运行给定函数,无返回值。

map():给数组每一项运行给定函数,返回每次调用的结果组成的数组。

some():给数组每一项运行给定函数,任意一项返回true,则返回true。

以上方法,都不会修改数组中的值。   // PS:详解

这些数组中,最相似的是 every()和 some(),它们都用于查询数组中的项是否满足某个条件

归并方法:reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。

reduce():从数组第一项开始,逐个遍历到最后。

reduceRight():从数组最后一项开始,向前遍历到第一项。

这两个方法都接收两个参数:一个在每一个项上调用的函数 和 (可选的)作为归并基础的初始值。

传给 reduce()和 reduceRight()的函数接收4个参数:前一个值,当前值,项的索引 和 数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生

在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。

使用reduce()还是 reduceRight(),主要取决于要从哪头开始遍历数组。除此之外,它们完全相同。

Date 类型

要创建一个日期对象,使用 new 操作符 和 Date 构造函数即可:

var now = new Date;

当调用 Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前的日期和时间。如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。为了简化这一

计算过程,ECMAScript提供了两个方法:Date.parse()和 Date.UTC()。

Date.parse():该方法接收一个表示日期的字符串参数,然后根据这个字符串返回相应日期毫秒数。 如果不能显示,返回NaN。实际上直接将表示日期的字符串传递给Date构造函

数,也会在后台调用Date.parse()。

var someDate = new Date(Date.parse("May 25,2004"));

var someDate = new Date("May 25,2004");      // 二者是等价的

Date.UTC():该方法的参数是年份、基于0的月份(一月0,二月1,以此类推)

Date.now():调用这个方法时的日期和毫秒数。

使用 + 操作符获得时间戳:  var start = + new Date();

Date 类型也重写了 toLocalString(),toString()和 valueof()方法;

toLocalString() 返回浏览器设置的;

toString()返回带有时区的日期时间;

valueof()不返回字符串,直接返回日期毫秒表示。

==================================完成量2/15======================================

RegExp

g:表示全局(global)模式,即模式将应用于所有字符串,而非发现第一个立刻停止。

i:表示不区分大小写模式,即在匹配时忽略模式与字符串的大小写。

m:表示多行模式,即达到文本末尾的时候还会继续查找下一行是否存在与模式匹配的项。

Function

没有重载,将函数名想像为指针,重写的会覆盖之前的。

函数声明会提前,函数表达式执行到所在代码行才会真正的被解释执行。

如想同时使用 var sum = functionsum(){ }; 放在变量中即可但是Safari中会报错。

因为ECMAScript中,函数的名字就是变量,所以函数也可以在作为值来使用。

要访问函数指针而不执行函数的话,必须去掉函数名后面那对括号。

5.5.4 函数内部属性

在函数内部,有两个特殊的对象:arguments 和 this

arguments:是一个类数组对象,包含着传入函数中的所有参数。

                     虽其主要用途是保存函数参数但这个对象还有一个名叫callee属性,该属性是一个指针,指向拥有这个arguments对象的函数。

                     比如运用递归算法,函数的执行与函数名紧紧地耦合在一起,为了消除这种耦合,可以在函数内部使用 arguments.callee 来替换函数在自己内部的调用。

this:引用的是函数执行环境的对象,网页全局作用域中调用函数时,this对象引用的就是window .

函数的名字只是一个包含指针的变量而已,即使是在不同的环境中执行,指向的仍是同一个函数。

caller:ES5也规范化了另一个函数对象的属性,那就是caller。这个属性保存着调用当前函数的函数的引用,如果在全局中调用当前函数,它的值为null。  // 函数名.caller;

caller不能赋值,caller和callee严格模式下会报错。

5.5.5 函数属性和方法

前面提到过函数也是对象,所以也有方法和属性。每个函数都包含两个属性:length 和 prototype 。

length属性:表示函数希望接收的命名参数的个数。

prototype:属性是极为重要的,toString()和valueOf()等都保存在prototype下,在创建自定义引用类型以及实现继承时,protype也是很重要的(具体看第六章)。

每个函数都包含两个非继承而来的方法:

apply(函数作用域,[ 数组 ])和 call(this,num1,num2...) 二者都是设置函数体内 this 对象的值。     //    PS:可以参考10小时掌握前端面试题中的解释。

传递参数并非二者最大的作用,它们真正的强大之处是能够扩充函数赖以运行的作用域。而且,使用二者扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

ES5还定义了一个方法:

bind(),这个方法会创建一个函数的实例,其this值会被绑定到传给 bind()函数的值。

每个函数继承的toLocaleString()和 toString(),valueOf()只返回函数代码。返回代码的格式因浏览器而异。

5.6 基本包装类型

为了便于基本类型值的操作,ECMAScript还提供了3个特殊的引用类型:Boolean、Number 和 String。与其它引用类型类似,但是也具有自己的独特性。每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型对象,从而能够让我们调用一些方法来操作这些数据。

引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型实例,在执行流离开当前作用域之前一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。

var s1 = " some text ";

s1.color = " red ";

alert(s1.color);   // undefined

5.6.1 Boolean类型

5.6.2 Number类型

var numberObject = new Number(10);    // 如此创建 Boolean 和 Number 对象

toFixed( ):传入几保留几位小数。tofixed( ) 方法可以表示带哟与0到20个小数位的数值。这只是标准,有些浏览器范围更大。

toExponential( ):alert(num.toExponential);  // " 1.0e+1 "

toPrecision( ):方法可能返回固定大小的(fixed)格式,也可能返回指数(exponential)格式,具体规则是看哪种格式最合适。该方法接受一个参数,即表示数值所有数字的位数。

5.6.3 String类型

String 类型是字符串的对象包装类型,可以这样使用String构造函数来创建:

var stringObject = new String( " hello world " );

String 对象的方法也可以在所有基本的字符串值中访问到。其中,继承的 valueOf( )、toLocaleString( ) 和 toString( ) 方法,都返回对象所表示的基本字符串值。

1.字符方法:

charAt( ) 和 charCodeAt( )   都接收一个参数,即字符串位置下标,返回的即是给定位置的那个字符串。

2.字符串操作法:

concat( );   // 拼接字符串,实际中直接利用加号操作符就可以了

重点:

ECMAScript还提供了三个基于子字符串创建新字符串的方法:

slice( ):

substr( ):

substring( ):

三个方法都返回一个操作字符串的子字符串,而且都接受一或两个参数。第一个指定字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。

而 substr( ) 的第二个参数指则是指定的返回字符串个数。如果没有川第二个参数,则将字符串的长度作为结束位置。与concat( )一样,对原始字符串没有任何影响。

如果参数是负值的情况下,slice( ) 和 substr( ) 相同,即负值+长度,但是 substring( ) 则是返回全部字符串。

3.字符串位置方法:

有两个可以从字符串查找子字符串的方法:

indexof( ):

lastIndexof( ):

这两个方法都是从一个字符串中搜索给定的子字符串,然后返回子字符串的位置(如果没找到,返回-1)。

4.trim( )方法:

ECMAScript5 为所有字符串定义了 trim( ) 方法。这个方法会创建一个字符串副本,删除前后所有空格,然后返回结果。

5.字符串大小写转换:

toLowerCase( ) 和 toUpperCase( ) 

特殊地区( 如土耳其语 ):toLocaleLowerCase( ) 和 toLocaleUpperCase( )

6.字符串模式匹配方法:

match( ):参数正则或字符串引向的正则,功能同 RegExp 的 exec( ),全局匹配时,数组里的数据是匹配到的所有符合正则表达式的字符串。

                  非全局匹配时,数据里的数据则是第一个匹配的值以及正则表达式的子分组匹配到的值,再附加上三个属性:

      groups:一个捕获组数组或者 undefined(如果没有定义命名捕获组)。

      index:匹配结果的开始位置。

      input:进行匹配的原字符串。

search( ):参数为正则表达式或者字符串引向的正则表达式对象,该方法返回第一个匹配项的索引,没有返回-1

replace( ):接受两个参数,第一个正则或者字符串,第二个可以是一个字符串或者函数。

split( ):可以基于指定的分隔符将一个字符串分割为多个子字符串,并将结果放入一个数组。该方法可以接受第二个参数,用于指定数组的大小,以便返回值不超过既定大小。

5.7.1 Global 对象

1.URI编码方法:

2.eval( )方法:此方法中创建的函数或变量不会被提升,它们只在 eval( ) 执行的时候创建。

5.7.2 Math 对象

min( ) 和 max( ) 大小判断

Math.mix( ) 和 Math.max( ),直接把需要判断的当作参数传进去。

舍入方法:

Math.ceil( ):向上舍

Math.floor( ):向下舍

Math.round( ):四舍五入

random( ) 方法:大于等于0小于1的随机数。

小结:

对象在JS中被称为引用类型的值,而且一些内置的引用类型可以用来创建特定的对象,现总结如下:

①引用类型与传统面向对象程序设计的类相似,但实现不同。

②Object 是一个基础类型,其它所有类型都从Object继承了基本的行为。

③Array类型 是一组值的有序列表,同时还提供了操作和转换这些值的功能。

④Date类型 提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能。

⑤RexExp类型 是EScript 支持正则表达式的一个接口,提供了最基本的和一些高级的正则表达式功能。

函数实际上是 Function 类型的实例,因此函数也是对象;而这一点也是JS最具有特色的地方。因为函数是对象,所以函数也有方法,用来增强其行为。

⑦因为有了基本包装类型,所以JS中的基本类型值可以被当做对象来访问。三种及基本包装类型:Boolean、Number、String。以下是它们的共同特征:

    每个包装类型都映射到同名的基本类型;

    在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作。

    操作基本类型值的语句一经执行完毕,就会立即销毁创建的包装对象。

在所有代码执行之前,作用域中就已经有了两个内置对象:Global 和 Math。大多数ECMAScript实现中都不能直接访问Global对象的属性。Math对象提供了很多属性和方法,用以辅助完成复杂的数学计算任务。          

 

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

面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象,前面提到过,ECMAScript中没有类的概念,因此它的对象也基于类的语言中的对象有所不同。

每个对象都是基于一个引用类型创建的。

创建对象最简单的方式:

var person = new Object( );   // 创建一个Object实例

person.name = '' Nicholas " ; // 再为它添加属性和方法

person.age = 29;

对象字面量创建方式:

var person = {

      name:" Nicholas ",

      age: 29,

}

6.1.1 属性类型

ECMAScript中有两种属性:数据属性 和 访问器属性

①数据属性:数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性:

Configurable:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为true;

Enumerable:表示能否通过 for-in 循环返回属性。默认为true;

Writable:表示能否修改属性的值。默认为true;

Value:包含这个属性的数据值。读取属性值时从这个位置读。写入时,保存在这里。默认值为undefined;

要修改属性默认的特性,必须使用ES5的Objecct.defineProperty( )方法。

这个方法有三个参数:属性所在的对象、属性的名字、描述符对象。

其中描述符(descriptor)对象的属性必须是:configurable、enumerate、writable、value。设置其中的一个或者多个可以修改对应的特性值。

②访问器属性:访问器属性不包含数据值;它们包含一对getter setter 属性(不过这两个函数都不是必须的)。

在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值。

在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下四个特性:

Configurable:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为true;

Enumerable:表示能否通过 for-in 循环返回属性。默认为true;

Get:在读取属性时调用的函数。默认值 undefined ;

Set:在写入属性时调用的函数。默认值 undefined ;

6.1.2 定义多个属性

由于为对象定义多个属性的可能性很大,ECMAScript5 又定义了一个 Object.defineProperties( ) 方法。此方法可以通过描述符一次定义多个属性。

这个方法接收两个对象参数:第一个对象就是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。

6.1.3 读取属性的特性

ES5方法:Object.getOwnPropertyDescriptor( ) 方法,可以取得给定属性的描述符。两个参数:属性所在对象,要读取其描述符的属性名称。

返回值是一个对象,数据属性和访问器属性,属性特性一样的。

扩展:tips:跳转

  • 设置描述符使用defineProperty() 
  • 获取单个属性的描述符 —— Object.getOwnPropertyDescriptor()
  • 获取所有属性里面的数据描述符 —— Object.getOwnPropertyDescriptors()

设置描述符使用defineProperty()

  • 第一个参数目标对象
  • 第二个参数目标属性(可以是Symbol
  • 第三个参数是对描述符进行配置,true是开启,false是关闭
  • 返回传递的目标对象

获取单个属性的描述符 —— Object.getOwnPropertyDescriptor()

获取对象属性的描述符

  • 第一个参数目标对象
  • 第二个参数目标属性
  • 返回值是对象及其所有描述符,找不到属性返回undefined
// {value: "58/40", writable: false, enumerable: false, configurable: true}

获取所有属性里面的数据描述符 —— Object.getOwnPropertyDescriptors()

只接受一个参数,目标对象。

6.2 创建对象

虽然 Object 构造函数 对象字面量 都可以用来创建单个对象。但是有个明显的缺点就是使用同一个接口创建很多对象,会产生大量的重复代码。所以人们开始使用工厂模式的一种变体。 

6.2.1 工厂模式

这种模式抽象了创建对象的过程。考虑到ES中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节。

6.2.2 构造函数模式

①将构造函数当作函数

构造函数与其它的函数唯一的区别,就在于调用它们的方式不同。构造函数也是函数,不存在定义构造函数的特殊语法。任何函数只要通过 new 操作符来调用,那它就可以作为构造函数。而任何函数只要不通过 new 操作符来调用,那它就不回和普通函数有什么两样。

⑤构造函数的问题

每个方法都要在每个实例上重新创建一遍。

==================================完成量3/15======================================

6.2.3 原型模式(好久没读书了你这个家伙O(∩_∩)O哈哈~)

我们创建的每一个函数都有一个 prototype (原型)属性,这个属性是一个指针 ,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

prototype 就是通过调用构造函数而创建那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

要理解原型模型的工作原理,必须先理解ECMAScript中原型对象的性质。

①理解原型对象

无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性这个属性指向函数的原型对象。

默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数) 属性,这个属性时一个指向 prototype 属性所在的函数指针。

创建自定义的构造函数之后,其原型对象默认只会取得 constructor 属性,至于其他方法,都是从 Object 继承而来的。 

当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。虽脚本中没有明确的标准访问方式,但是浏览器有_proto_,要明确的是这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。

Person.prototype.isPrototypeof(person1);  //返回true;

tips:用原型对象的 isPrototypeof() 方法测试 person1内部是否有方法指向 Person.prototype 的指针,有返回true。

ES5增加了一个新方法,叫 Object.getPrototype(),在所有支持的实现中,这个方法返回 [[Prototype]]的值。

Object.getPrototypeof(person1)== Person.prototype;  //true

Object.getPrototypeof(person1)== Person.prototype.name; 

第一行代码只是确定,Object.getPrototypeOf() 返回德对象实际就是这个对象的原型。

第二行代码取得了原型对象中name属性的值。

Object.getPrototypeof();可以方便地取得了一个对象的原型,而这在利用原型实现集成的情况下非常重要。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始,如果找到,则返回该属性的值。没找到,则继续搜索指针指向的原型对象。找到返回该值。

当对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改该属性。即使设置为null,也只是会在实例中设置这个属性,而不会恢复其指向原型链的连接。不过,delete操作可以完全删除实例属性,从而能让我们重新访问原型中的属性。

hasOwnProperty(),该方法可以判断访问的是原型属性还是实例属性。  //实例返回false,原型返回true;

②原型与in操作符

有两种方式使用in操作符,一种单独使用,一种在 for-in 循环中使用。

单独使用:in 操作符会通过对象能够访问给定属性时返回 true ,无论该属性存在于实例中还是原型中;

name in person //true 属性in对象,同时使用 hasOwnProperty() 方法和 in 操作符,就可以判断属性到底存在于对象还是原型对象中。都为true就是实例中。

function hasPrototypeProperty(object,name){

  return !object.hasOwnProperty(name)  &&  (name in object);

}

要取得对象上所有可枚举的属性。ES5提供 Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

Object.keys()和 Object.getOwnPropertyNames()方法都可以用来代替 for-in 循环。

③更简单的原型语法 

前面例子中每次添加都需要,敲一遍 Person.protype,为了减少不必要的输入,也从视觉上更好的封装原型功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。

function (){ };

Person.prototype = {

  name:"Nana",

       age:20

}

每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。这里本质上已经完全重写了默认的 prototype 对象,所以 constructor 也变成了新的。

④原型的动态性

由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即是先创建实例后修改原型对象也是一样的。

如果重写整个原型对象,情况就不同了。我们知道,调用构造函数时会为实例添加一个指向最初原型的 [[protype]] 指针,而把原型修改为另外一个对象,就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。

 ⑤原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object,Array,String,等等)都在其构造函数的原型上定义了方法。例如 Array.prototype 中可以找到 sort() 方法,而在 String.prototype 中可以找到 substring()方法。

通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新的方法。可以像修改自定义原型对象的原型一样修改原生对象的原型,因此可以随时添加方法。

不推荐产品化的程序中修改原生对象的原型。可能会造成冲突,意外的重写原生方法等。

⑥原型对象的问题

缺点:首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然会在某种程度上带来一些不便,但这还不是原型对象的最大问题,原型模式的最大问题是由其共享的本性所导致的。

原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,毕竟,通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,就有很大的问题。因为实例一般都是要有属于自己的全部属性,而不是所有实例共享一个。

6.2.4 组合使用构造函数模式和原型模式

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。结果,每个实例都会有自己的一份实例属性副本,但是同时又共享者对方方法的引用,最大限度的节省了内存。另外,这种,混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

function Person(name,age,job){
   this.name = name ;
   this.age = age ;
   this.job = job ;
   this.friends = ["Jim","Murph"] 
}

Person.prototype = {
   constructor : Person ,
   sayName : function(){
       alert(this.name);       
   }
}

var person1 = new Person("Nicholas",29,"Drvier");
var person2 = new Person("Greg",27,"Doctor");

person1.friends.push("Van")
person1.friends === person2.friends //false

//这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和 方法sayName()则是在原型中定义的。所以二者不相等,因为引用了不同的数组。

//这种构造函数与原型混成的模式,是目前在ECMAScript中使用最广泛的,认同度最高的一种创建自定义类型的方法。

6.25 动态原型模式

可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

对原型所做的修改会立即在所有的实例中反映。

使用动态原型的时候,不能使用对象字面量重写原型。如果在已经创建了实例的情况下,重写原型,那么会切断现有实例与新原型之间的联系。

6.26 寄生构造函数模式

该模式的思想是,创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。

构造函数在不返回值的情况下,默认返回新对象实例。而通过在构造函数末尾添加一个return语句,可以重写调用构造函数时返回的值。

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,这个时候就可以使用这个模式。

function SpecialArray(){
   //创建数组
   var values = new Array();
   //添加值
   values.push.apply(values,arguments);
   //添加方法
   values.toPipedString = function(){
       returnthis.join("|");  
};
 //返回数组
 returnvalues;
}
var colors = new SpecoalArray("red","blue","green");
alert(colors.toPipedString());   //"red|blue|green"

关于寄生构造函数模式的说明:首先,返回的对象与构造函数或者构造函数的原型属性之间没有关系。也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此不能依赖 instanceof 操作符来确定对象类型。因为上述问题,建议在可以使用其他模式的情况下,不要使用这种模式。

6.2.7 稳妥构造函数模式

道格拉斯·克罗克福德发明了JavaScript中的稳妥对象(durableobjects)这个概念。所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建的对象实例方法不引用this;二是不使用new操作符调用构造函数。

function person( name,age,job ){
   // 创建要返回的对象
   var o = new Object();
   // 可以在这里定义私有变量和函数
   // 添加方法
   o.sayName = function(){
     alert(name);
     // 返回对象
     return o
  }
 }
//在这种模式创建的对象中,除了使用sayName()方法外,没有别的方法可以访问其数据成员。

6.3 继承 

继承是OO语言中最为人津津乐道的概念。("OO"就是面向对象, 面向对象(Object Oriented,OO)。

许多OO语言都支持两种继承方式:接口继承实现继承

接口继承:只继承方法签名。

实现继承:继承实际的方法。

由于函数没有签名,在ECMScript中只支持实现继承,无法实现接口继承。而且其实现继承主要是靠原型链来实现的

6.3.1 原型链

ECMScript中描述了原型链的概念,并将原型链作为实现继承的主要方式。

其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

简单回顾构造函数原型实例之间的关系:

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都抱一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个对象的实例,此时的原型对象将包含一个指向另一个原型的指针相应的,另一个原型中也包含着一个指向另一个构造函数的指针。假如,另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。

AZUKI七~不得不承认丢人的事是,原型链(第三版 162-163)这里看的头痛。。。。

当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。找不到属性或者方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。

别忘记默认的原型,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的,所有函数的默认原型都是Object的实例,因此默认原型都会包含一内部指针,指向 Object.prototype .。这也正是所有自定义类型都会继承 toString(),valueOf()等默认方法的根本原因。

确定原型和实例的关系:

有两种方式,第一种是使用 insatnceof 操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true 。

                      第二种是使用 isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。

谨慎地定义方法:不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后。ps:在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链。   

原型链的问题:

原型链虽然很强大,可以用它来实现继承,但它也存在一些问题。其中最主要的问题来自包含引用类型值的原型。引用类型值的原型属性会被所有实例所共享;而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。第二个问题:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数,实践中很少会单独使用原型链。

6.3.2 借用构造函数

在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数的技术,即在子类型构造函数的内部调用超类型构造函数(函数只不过是在特定环境中执行代码的对象,apply和call方法也可以在将来新创建的对象上执行构造函数)。

传递参数:相对于原型链而言,借用构造函数有一个很大优势,即可以在子类型构造函数中向超类型构造函数传递参数。

借用构造函数问题:如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题,方法都在构造函数中定义,因此无法复用。而且,在超类型中的原型定义的方法, 对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。

6.3.6 组合继承

有时候也叫作伪经典继承,指的是将原型链和借用构造函数的技术组合到一起 ,从而发挥二者之长的一种继承模式,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。既能复用,又能保证都有自己的属性。是js中最常用的继承模式。

6.3.4 原型式继承

6.3.5 寄生式继承

6.3.5 寄生组合式继承                                                        

6.4小结

ECMAScript支持面向对象编程(OO),但不使用类或者接口。代码可以在执行过程中创建和增强,因此具有动态性而非严格定义的实体。在没有类的情况下,可以采用下列模式创建对象。

工厂模式,使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式后来被构造函数模式所代替。

构造函数模式,可以创建自定义引用类型,可以像创建内置对象实例一样使用new操作符,缺点,即它的每个成员都无法复用,包括函数。由于函数可以不局限于任何对象(即与对象具有松散耦合的特点),因此没有理由不在多个对象间共享函数。

原型模式,使用构造函数的prototype属性,来指定那些应该共享的属性和方法。组合使用构造函数和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。

JavaScript主要通过原型链实现继承。原型链的构建是通过将一个实例赋值给另一个构造函数的原型实现的。这样,子类型就能访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部借用超类型构造函数,这样就可以做到每个实例都有自己的属性,同时还能保证只使用构造函数来定义类型。使用最多的继承模式是作何继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。

此外,还存在下列可供选择的继承模式:

原型式继承:可以在不必须预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以进一步改造。

寄生式继承:与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型结构函数而导致的低效率问题,可以将这个模式与组合继承一起使用。

寄生组合式继承:集寄生式继承和组合式继承优点于一身,是实现基于类型继承的最有效方式。

 

第七章 函数表达式

函数表达式是JavaScript中的一个既强大又容易令人困惑的特性。第5章曾经介绍过,定义函数的方式有两种:一种是函数声明,另一种就是函数表达式。
首先是 function 关键字,然后是函数的名字,这就是指定函数名的方式。

关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之前先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面。

第二种创建函数的方式是使用函数表达式。函数表达式有几种不同的表达方式:①变量赋值(创建函数并赋值给变量),function后面没有name标识符的话为匿名函数(拉姆达函数),name属性为空字符串。

7.1 递归

递归函数是在一个函数通过名字调用自身的情况下构成的。

arguments.callee 是一个指向正在执行的函数的指针,因此可以用他来实现对函数的递归调用。(简单说就是在内部调用别的时候不用函数名用arguments。callee代替,效果相同,却可以避免被重新定义的问题。),但是在严格模式下,不能通过脚本访问 arguments.callee , 访问会导致报错。var skt = (function f(){ return f() }),这样重新命名可以解决一切问题。

7.2 闭包

不少人混淆闭包匿名函数,闭包:指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式:在一个函数内部创建另一个函数。

当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,...直至作为作用域链终点的全局执行环境。

后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在。而创建的局部环境的变量对象,只会在执行过程中存在。

无论什么时候在函数中范根一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象。)

在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。

把匿名函数设置为null解除引用,释放内存,虽然V8等优化后js引擎会尝试回收被闭包占用的内存,为避免内存被占用过多,慎重使用闭包。

7.2.1 闭包与变量

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。

7.2.2 关于this对象

在闭包中使用this对象可能导致问题。我们知道,this对象是在运行时基于函数的执行环境绑定的:在全局函数中this等于window,而当函数被作为某个对象的调用方法,this等于那个对象。不过匿名函数的执行环境具有全局性,因此其this通常指向window。但有时候因为编写闭包的方式不同,这方面可能不是很明显。

var name = "我是柏仔的AZUKI七"

var object = {
   
     name : "真的!!!",
   
      getNameFunc : function(){
           
         // var that = this; 提前把this对象赋值给一个名叫that的变量,而在定义闭包后还可以访问that    

          return function(){

              return this.name;      // that.name 所以这里就会返回 "真的!!!"
 
          }   
 
       }

}

alert(object.getNameFun()());        //"我是柏仔的AZUKI七" 非严格模式下
// 因为匿名函数这里this指向本身最后指向全局,函数调用时自动获取的this和arguments,只搜索到其活动对象。

tips:在通过 call() 或 apply() 改变函数执行环境的情况下,this就会指向其他对象。

7.2.3 内存泄漏

IE9之前的版本 JS 对象 和 COM 对象使用不同的垃圾收集例程,因此闭包在这些IE版本中会导致一些特殊问题。

比如,如果闭包作用域链中保存着一个HTML元素,那么就以为这该元素无法销毁。

7.3 模仿块级作用域

JS没有块级作用域的概念,这意味着在块语句中定义的变量,实际上是包含在函数中而非语句中创建的。

JS从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见,后续声明中的变量初始化除外。匿名函数可以用来模仿块级作用域并避免这个问题。

(function (){
        // 这里是块级作用域
})()

以上代码定义了并立即调用了一个匿名函数。将函声明包含在一对圆括号中,表示它实际上是一个函数表达式。而后面的括号,表示是这个函数的调用。(注:函数声明不可直接加圆括号调用,表达式可以)。

无论什么时候,只要需要一些临时变量,就可以使用私有作用域。在匿名函数中定义的任何变量,都会在执行结束时被销毁。

这种技术经常在全局作用域中用在函数外部,从而限制向全局作用域中添加过多的变量和函数。多人开发时,可以创建私有作用域避免冲突。

7.4 私有变量

7.4.1 静态私有变量

作为一个闭包,总是保存着对包含作用域的应用; 

多查找作用域链中的一个层次,就会在一定程度上影响查找速度。而这正是使用闭包和私有变量的一个明显的不足之处。

7.4.2 模块模式 

7.4.2 增强的模块模式

7.5 小结

在js编程中函数表达式是一种非常有用的技术。使用函数表达式可以无需对函数命名,从而实现动态编程。匿名函数,也称为拉姆达函数,是一种使用js函数的强大方式。

①函数表达式不同于函数声明。函数声明要求有个名字,但函数表达式不需要。没有名字的函数表达式也叫作匿名函数。

②在无法确定如何引用函数的情况下,递归函数就会变得比较复杂。

③递归函数应该始终使用 arguments.callee 来递归地调用自身,不要使用匿名函数,因为函数的名字可能发生变化。

当在函数内部定义了其它函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理如下:

①在后台执行环境中,闭包的作用域链包含它自己的作用域,包含函数的作用域和全局作用域。

②通常,函数的作用域及其包含的所有边变量,都会在函数执行结束后被销毁。

③但是,当函数返回闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。

使用闭包可以在js中模仿块级作用域(js本身没有块级作用域的概念),要点如下:

①创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。

②结果就是函数内部的所有变量都会被立即销毁,除非将某些变量赋值给了包含作用域(即外部作用域)中的变量。

闭包还可以用于在对象中创建私有变量,相关概念如下:

① 即是js中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含在作用域中定义的变量。

②有权访问私有变量的公有方法叫做特权方法。

③可以使用构造函数模式,原型模式来实现自定义类型的特权方法,也可以使用模块模式,增强的模块模式来实现单例的特权方法。

JS中的函数表达式闭包都是极其有用的特性,利用他们可以实现很多功能。不过因为创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存。

 

第八章 BOM

BOM德核心是——理解window对象

控制窗口,框架和弹出窗口

利用localtion对象中的页面信息

使用navigator对象了解浏览器

ES是JS的核心,但如果要在Web中使用JS,那么BOM(浏览器对象模型)才是真正的核心。BOM提供很多对象,用于访问浏览器功能,这些功能与任何网页内容无关。多年来,缺少事实上的规范导致BOM既有意思又有问题,因为浏览器提供商会按照自己的想法随意去扩展它。于是,浏览器之间共有的对象就成了事实上的标准。W3C为了把浏览器中JS最基本的部分标准化,已经将BOM的主要方面纳入了HTML5的规范中。

8.1 window对象

BOM核心对象是window,它表示浏览器的一个实例。在浏览器中,window对象有双重角色,它既是通过JS访问浏览器窗口的一个接口,有事ES规定的Global对象。这意味着网页中定义的任何一个对象,变量和函数,都以window作为其Global对象,因此有权访问parseInt()等方法。

8.1.1 全局作用域

定义全局变量与window对象上直接定义属性还是有一点差别的:全局变量不能通过delete操作符删除,在window对象上定义的属性可以。

访问未声明的变量会抛出错误,但是通过查询window对象,来判断某个可能未声明的变量是否存在(undefined)。

8.1.2 窗口关系及框架

如果页面中包含框架,则每个框架都拥有自己的window对象,并且保存在freams集合中,可以通过数值索引(从零开始,从左至右,从上到下)或者框架名称来访问相应的window对象。每个window对象,都有一个name属性,其中包含框架名称。

注意:除非最高层窗口是通过 window.open()打开的,否则其 window 对象的 name 属性不会包含任何值。

与框架有关的最后一个对象是 self  ,它始终指向window;实际上 self 和 window 对象可以互换使用。引入 self 对象的目的只是为了与 top 和 parent 对象对应起来,因此它不格外包含其他值。

所有这些对象都是window对象的属性,可以通过 window.parent , window.top 等形式访问。同时这也意味着,可以将不同层次的window对象连缀起来,例如:window.parent.parent.frames[0] 。

在使用框架的情况下,浏览器中会存在多个 Global 对象。在每个框架中定义的全局变量会自动成为框架中 window 对象的属性。由于每个 window 对象都包含原生类型的构造函数,因此每个框架都有一套自己的构造函数,这些构造函数一一对应,但并不相等。例如,top.Object 并不等于 top.frames[0].Object 。这个问题会对跨框架传递的对象使用 instanceof 操作符。

8.1.3 窗口位置 

使用下列代码可以跨浏览器取得窗口左边和上边的位置:           

var leftPos = ( typeof window.screenLeft = = "number") ? window.screenLeft : window.screenX;

var topPos = ( typeof window.screenTop = = "number") ? window.screenTop : window.screenY;

这个例子运用二元操作符首先确定 screenLeft 和 screenTop 属性是否存在,如果是在(IE、Safari、Opera、Chrome中),则取得这两个属性的值。如果不存在(Firefox中),则取得 screenX 和 screenY 的值。

因为不统一的问题,无法在跨浏览器的条件下取得窗口左边和上边的精确坐标值。然而 movwTo() 和 moveBy()方法倒是有可能将窗口精确地移动到一个新位置。两个方法都接收两个参数,moveTo 接收的是新位置的x和y坐标值,而moveBy 接收的是在水平和垂直方向上移动的像素数。(注意:这两个方法可能会被浏览器禁用,且不适用于框架,只对最外层的window对象使用。)

//将窗口移动到屏幕左下角
window.moveTo(0,0);
//将窗口向下移动100像素
window.moveBy(0,100);
//将窗口移动到(200,300)
window.moveTo(200,300);
//将窗口向左移动50像素
window.moveBy(-50,0);

8.1.4 窗口大小

跨浏览器确定窗口大小并不容易。

IE9、Firefox、Safari、Chrome均为此提供了四个属性:outerWidth、outerHeight、outerWidth、outerHeight。

除了Safari表示页面视图容器(单个标签页对应的浏览器窗口)大小,outerWidth、outerHeight返回浏览器窗口本身的尺寸。
outerWidth、outerHeight 则表示页面视图区的大小(减去边框宽度)。Chrome中二者返回相同的值,即视口(viewport)大小而非浏览器窗口大小。

浏览器更早一些的版本,没有得到当前浏览器窗口的属性,但是可以根据DOM方法获得:document.documentElement.clientWidth 和 clicentHeight ;

虽然最终无法确定浏览器窗口的大小,但是可以取得页面视口的大小。   

另外,resizeTo()和 resizeBy()方法,可以调整浏览器窗口的大小,两个方法都接收两个参数,resizeTo( 新宽度和新高度),resizeBy(新窗口与原窗口宽度差和高度差)。

8.1.5 导航和打开窗口

使用 window.open()方法既可以导航到一个特定URL,也可以打开一个新的浏览器窗口;、

这个方法可以接收四个参数:window.open(要加载的URL、窗口目标、一个特性字符串、表示新页面是否取代浏览器历史记录中当前加载页面的布尔值);通常只须传第一个参数,最后一个蚕食只在不打开新窗口的情况下使用。

==================================完成量4/15======================================

 调用close()方法还可以关闭新打开的窗口。

wroxWin.close(); 但是这个方法只适用于通过 wndow.open()打开的弹窗窗口。对于主窗如果没得到用户的允许是不能关闭的。弹窗到是可以通过 top.close()在不经用户同意下自己关闭,但是引用还在。

新创建的 window 对象有一个 opener 属性,其中保存着打开它的原始窗口对象。将 opener 属性设置为 null ,即表示在单独的进程中运行新标签。

因为安全限制,各大浏览器平台对弹窗加以限制,所以只能通过 点击鼠标和敲击键盘 事件进行触发弹窗,否则页面加载中调用 window.open() 的语句根本不会执行。

弹出窗口屏蔽:大多数浏览器都有内置弹出窗口屏蔽程序,没有的可以下载 Yahoo!Toolbar 等带有内置屏蔽程序的工具。

如果弹窗被屏蔽,考虑两种可能:①如果是浏览器内置屏蔽程序window.open()可能返回null ②如果被其它外置插件屏蔽,window.open() 会抛出错误,所以要套 try-catch

8.1.6 间歇调用和超时调用

js是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执行。前者是在指定的时间过后执行代码,而后者则是每隔指定的事件就执行一次代码。

超时调用需要使用 window 对象的 setTimeout() 方法,它接受两个参数:要执行的代码和以毫秒表示的时间。不建议第一个参数传递字符串,可能导致性能损失。

JS是一个单线程的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JS队列。这些任务会按照它们添加到队列的顺序执行。setTimeout的第二个参数就是告诉JS再过多长时间把当前任务添加到队列中。如果队列是空的,那么立即执行,如果不是,执行完前面的之后再执行。

clearTimeout() : 只要是在指定的时间尚未过去之前调用,就可以取消超时调用。设置立马取消,就什么也不会发生。超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window,严格模式下是undefined。

设置间歇调用的方法是 setInterval() ,它接受的参数与 setTimeout() 相同。

8.1.7 系统对话框  alert(),confirm(),prompt()

们的外观由浏览器设定,不是css,显示这些对话框的时候代码会停止执行,而关掉这些对话框后代码会恢复执行。

8.2 location对象

location 是最有用的BOM对象之一 ,它提供了与当前窗口中加载的文档有关信息,还提供了一些导航功能(它将URL解析为独立片段,让开发者可以通过不同属性获取)。

location 是一个很特别的对象,因为它既是 wondow 也是 document 对象的属性:window.location = document.location

 

8.2.1 查询字符串参数

function getQueryStringArgs(){
 
    //取得查询字符串并去掉开头的问号
    var qs = (location.search.length > 0 ? location.search.substring(1) : ""),
 
    //保存数据的对象
    args = {},
 
    //取得每一项
    items = qs.length ? qs.split("&") : [],
    item = null,
        name = null,
        value = null,
 
        //在 for 循环中使用
        i = 0,
        len = items.length;
 
    //逐个将每一项添加到 args 对象中
    for (i=0; i < len; i++){
        item = items[i].split("=");
        name = decodeURIComponent(item[0]);
        value = decodeURIComponent(item[1]);
 
        if (name.length) {
            args[name] = value;
        }
    }
 
    return args;
}

8.2.2 位置操作 

location对象可以通过很多方式来改变浏览器的位置。

首先是 :assign() 

location.assign("AZUKI7.top");

这样就可以打开一个新URL并在浏览器的历史记录中生成一条记录。如果以 location.href() 和 window.location() 设置为一个URl值,也会以该值调用 assign() 方法。效果完全一样。

window.location("AZUKI7.top")  ==  location.href("AZUKI7.top")  == location.assign("AZUKI7.top");

通过location对象的其他属性值也可以改变当前加载界面:hash 、search 、hostname 、pathname 、 port 。

以上任何一种方式修改URL后都会在浏览器中留下记录,因此可以通过后退按钮导航到前一个页面。要禁用这种行为,可以使用 repalce() 方法。// location.replace("AZUKI7.top");

与位置最后有关的一个方法是 reload() ,如果不传参,页面会以最有效的方式重载。如果要强制从服务器从新加载,则需要传递参数为 true ;// location.reload(true);

8.3 navigator

navigator对象是所有支持 JavaScript 的浏览器所共有的。

8.3.1 检测插件

可以使用 plugins 数组来达到目的每一项都包含下列属性:

name:插件的名字。

description:插件的描述。

filename:插件的文件名。

length:插件所处理的 MIME 类型数量。

在IE中检测插件的唯一方式就是使用转悠的 ActiveXObject 类型,并尝试创建一个特定插件的实例。IE 是以 COM 对象的方式实现插件的,而COM对象使用唯一标识符来表示。因此要想检查特定插件,必须知道其COM标识符。

8.3.2 注册处理程序

8.4 screen 对象

只用来表明客户端能力,浏览器窗口外部的显示器信息,像素宽度高度等。每个浏览器screen对象包含着各不相同的属性。

8.5 history 对象

history 对象保存着用户上网的浏览记录,从窗口被打开的那一刻算起。处于安全考虑,开发人员无法得知用户浏览过的URL。不过,借助用户访问过的页面列表,可以在不知实际的情况下实现后退和前进。

使用 go() 方法可以在用户的历史记录中任意跳转。这个方法接受一个参数,表示向前向后的整数值,负数向后跳。也可以是一个字符串参数,会跳往匹配该字符串最近距离的浏览记录。

还有两个简写方法可以代替 go();向前一页 history.forward( ) ;向后一页 history.back();

除此外,history 还有 length 属性,保存着历史记录的数量。

8.6 小结

浏览器对象模型(BOM)以 window 对象为依托,表示浏览器窗口以及页面可见区域。同时,window 对象还是 ECMAScript 中的 Global对象,因而所有全局变量和函数都是它的属性,且所有原生的构造函数及其他函数也都存在于它的命名空间下。

在使用框架时,每个框架都有自己的 window 对象以及所有原生构造函数及其他函数的副本。每个框架都保存在 frames 集合中,可以通过位置或通过名称来访问。

有一些窗口指针,可以用来引用其他框架,包括父框架。

top 对象始终指向最外围的框架,也就是整个浏览器窗口。

parent 对象表示包含当前框架的框架,而 self 对象则回指 window。

使用 location 对象可以通过编程方式来访问浏览器的导航系统。设置相应的属性,可以逐段或整体性地修改浏览器的URL。

调用 replce()方法可以导航到一个新的 URL ,同时该 URL 会替换浏览器历史记录中当前显示的页面。

navigator 对象提供了与浏览器有关的信息。到底提供了那些信息,很大程度上取决于用户的浏览器;不过,也有一些公共的属性(如 userAgent)存在于所有浏览器中。

BOM中还有两个对象:screen 和 history ,但它们的功能有限。screen 独享中保存着与客户端显示器有关的信息,这些信息一般只用于站点分析。history 对象为访问浏览器的历史记录开了一个小缝隙,开发人员可以据此判断历史记录的数量,也可以在历史记录中向后或向前导航到任意页面。

 

第九章 客户端检测 

9.1 能力检测

两个重要概念:①先检测达成目的的最常用的特性,可以保证代码最优化,因为在多数情况下都可以避免测试多个条件。②必须测试实际要用到的特性。

function getElement ( id ) {
    if ( document.getElementById ){
         return document.getElementById ( id ) ;
    }
}  else if ( document.all ) {
         return document.all[id] ;
}  else {
         throw new Error (" No way to retrieve element! ");   
}    

//IE<5.5 不支持document.getElementById这个方法

作为开发人员,必须对自己要使用某个功能的风险作出理性的估计。能力检测,不是浏览器检测。 

//确定浏览器是否支持 Netscape 风格的插件
var hasNSPlugins = !!( navigator.plugins && navigator.plugins.length ) ;

//确定浏览器是否具有DOM1级规定的能力
var hasDOM! = !!( document.getElementById && document.creatElement && document.getElementByTagName ) ;

在实际开发中,应该将能力检测作为确定下一步解决方案的依据,而不是用它来判断用户使用是什么浏览器。

9.2 怪癖检测

怪癖检测的目的是识别浏览器的特殊行为。但与能力监测确认浏览器支持什么能力不同,怪癖检测是想要知道浏览器存在什么缺陷。

9.3 用户代理检测

用户代理字符串作为响应首部发送,且该字符串可通过 navigator.userAgent 属性访问

9.4 小结

能力监测:在编写代码之前先检测特定浏览器的能力。

怪癖检测:怪癖实际上是浏览器实现中存在的bug。

用户代理检测:通过检测用户代理字符串来识别浏览器。

       

第十章 DOM    

DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。DOM脱胎于Netscape及微软公司创始的DHTML(动态HTML),但现在它已经成为表现和操作页面标记的真正跨平台、语言中立的方式。

10.1 节点层次

HTML元素通过元素节点表示,属性通过属性节点表示,注释通过注释节点表示,一共有十二种节点类型,这些类型都继承自一个基础类型。

JS中所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法,除了IE其它浏览器都可以访问到这个类型。

每个节点都有nodeType属性,用于表明节点类型。

任何节点类型必居其一:

1 ELEMENT_NODE(1)  元素节点
2 ATTRIBUTE_NODE(2)  属性节点
3 TEXT_NODE(3)  文本节点
4 CDATA_SECTION_NODE(4)  CDATA区段
5 ENTITY_REFERENCE_NODE(5) 实体引用元素
6 ENTITY_NODE(6)  实体
7 PROCESSING_INSTRUCTION_NODE(7)  表示处理指令
8 COMMENT_NODE(8)  注释节点
9 DOCUMENT_NODE(9)  最外层的Root element,包括所有其他子节点
10 DOCUMENT_TYPE_NODE(10)  <!DOCTYPE>
11 DOCUMENT_FRAGMENT_NODE(11)  文档碎片节点
12 NOTATION_NODE(12)  DTD中声明的符号节点 

开发人员最常用的就是元素和文本节点,并不是所有节点都被Web浏览器支持。

每个节点都有一个 childNodes 属性,其中保存一个NodeList对象。NodeList是一种类数组对象,用于保存一组有序的节点。

对arguments对象使用 Array.prototype.slice() 可以将其转换为数组。也可以将NodeList对象转换为数组。

==================================完成量5/15======================================

parentNode属性,指向当前节点父属性。

previousSibling属性,返回选定节点的上一个同级节点,第一个节点返回null。

nextSibling属性,返回选定节点的下一个同级节点,最后一个节点返回null。

firstChild属性,返回父节点第一个子节点。

lastChild属性,返回父节点最后一个子节点。

appendChild()方法,用于向 childNodes 列表的末尾添加一个节点。

insertBefore()方法,把节点放到 childNodes 列表中某个特定的位置上。参数:要插入的节点,作为参照的节点。

replaceChild()方法,替换节点。参数:要插入的节点,要替换的节点。

removeChild()方法,移除节点。参数:要移除的节点。

cloneNode()方法,创建调用该方法节点副本。参数:true/false,true执行深复制,即节点及其整个子节点树。有两个方法是所有类型节点都有的,第一个就是cloneNode(),不会赋值属性比如点击事件,只会复制特性 、子节点。

normalize(),处理文档树中的文本节点。当在某节点调用这个方法,两种情况,空文本节点删除它,相邻文本节点合并为一个。

10.1.2 Document 类型

js通过 Document 类型表示文档。在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以作为全局对象来访问。Document节点具有以下特性:

  • nodeType 的值为9;
  • nodeName 的值为 "#document";
  • nodeValue 的值为 null;
  • parentNode 的值为 null;
  • ownerDocument 的值为 null;
  • 其子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction 或 Comment。

Document 类型可以表示 HTML 页面或者其他基于XML的文档。不过最常见的应用还是作为 HTMLDocument 实例的 document 对象。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。

文档的子节点:

虽然DOM标准规定 Document 节点的子节点可以是 DocumentType、Element、ProcessingInstruction 或 Comment ,但还有两个内置的访问子节点的快捷方式。第一个就是documentElement 属性,该属性始终指向 HTML 页面中的 <html> 元素。另一个就是通过 childNodes 列表访问文档元素,但是 documentElement 属性更加快捷直接。

document 对象还有一个 body 属性:var body = document.body; // 取得对<body>的引用

所有浏览器都支持 document.documentElement 和 document.body 属性。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                          

 

posted @ 2020-11-04 20:06  AZUKI七  阅读(388)  评论(0编辑  收藏  举报