JavaScript
Sunxiaoshen
JavaScript实现
JavaScript
ECMAScript DOM BOM
ECMAScript与浏览器
ECMAScript与 web浏览器没有依赖关系。实际上,这门语言本身并不包含输入和输出定义。 ECMA-262定义的只是这门语言的基础,而在此基础上可以构建更完善的脚本语言。常见的Web浏览器只是 ECMAScript实现的可能的宿主环境之一。宿主环境不仅提供基本的 ECMAScript实现,同时也会提供该语言的扩展,以便语言与环境之间对接交互。
ECMAScript
• 法语
• 型类
• 句语
• 字关键
• 保留字
• 操作符
• 象对
文档对象模型( DOM)
• DOM是针对 XML但经过扩展用于 HTML的应用程序编程接口
• DOM把整个页面映射为一个多层节点结构
• HTML或 XML页面的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据
DOM
• 通过 DOM创建的表示文档的树形图,开发人员获得了控制页面内容和结构的主动权。
• 借用 DOM提供的 API,开发人员可以轻松自如地删除、添加、替换或修改任何节点
DOM
• DOM并不只是针对 JavaScript的,很多别的语言也都实现了 DOM,不过在Web浏览器中,基于 ECMAScript实现的 DOM的确已经成为 JavaScript这门语言的一个重要组成部分
DOM级别
• DOM1
DOM核心( DOM Core)、 DOM HTML
DOM核心规定的是如何映射基于 XML的文档结构,以便简化对文档中任意部分的访问和操作
DOM HTML模块则在 DOM核心的基础上加以扩展,添加了针对 HTML的对象和方法
DOM2
• DOM2在原来 DOM的基础上又扩充了鼠标和用户界面事件、范围、遍历等细分模块,而且通过对象接口增加了对 CSS的支持, DOM1中的 DOM核心模块也经过扩展开始支持 XML命名空间
DOM2
DOM2引入了下列新模块
DOM视图—定义了跟踪不同文档视图的接口
DOM事件 ---定义了事件和事件处理的接口
DOM样式 ---定义了基于 CSS为元素应用样式的接口
DOM遍历和范围 ---定义了遍历和操作文档树的接口
DOM3
• DOM3 引入了以统一方式加载和保存文档的方法—在 DOM加载和保存模块中定义;新增了验证文档的方法—在 DOM验证模块中定义
• DOM3也对 DOM核心进行了扩展,开始支持 XML1.0规范,设计 XML Infoset、 Xpath和 XML Base
BOM
• IE3和 NetScape Navigator3都支持可以访问和操作浏览器窗口的浏览器对象模型。开发人员使用 BOM可以控制浏览器显示的页面意外的部分。
• 无标准可以遵循
针对浏览器的 JavaScript扩展
• 弹出新浏览器窗口的功能
• 移动、缩放和关闭浏览器窗口的功能
• 提供浏览器详细信息的 navigator对象• 提供浏览器所加载页面的详细信息的 locati
on对象• 提供用户显示器分辨率详细信息的 screen对象
• 对 cookie的支持• 像 XMLHttpRequst和 IE的 ActiveXObject这样的自定义对象
使用 JavaScript
• 所有 <script>元素会按照它们在页面中出现的先后顺序依次被解析。只有在解析完前面 <script>元素中的代码之后,才会开始解析后面 <script>元素中的代码
• 浏览器在呈现后面的页面内容之前,必须先解析完前面 <script>元素中的代码。为此,一般要把 <script>元素放在页面的末尾,放在页面内容之后和结束的 </body>标签之前
基本概念
• 标识符,就是指变量、函数、属性的名字,或者函数的参数
• 第一个字符必须是字母、下划线或一个美元符号
• 其它字符可以是字母、下划线、美元符号或数字
• 不能把关键字、保留字、 true、 false和 null用作标识符
数据类型
• Undefined• Null• Bollean• Number• String• Object-本质上是由一组无序的名值对组成
Undefined
• 对未声明的变量,只能执行一项操作,即使用 typeof操作符检测其数据类型
• // var age• alert(typeOf(age));
Null
• 如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null而不是其他值
• alert(null==undefined) true
• 只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存 null 值
Boolean
虽然 Boolean类型的字面量只有两个,但 ECMAScript中所有类型的值都有与这两个 Boolean 值等价的值。要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean()
Number
• 默认情况下, ECMAScript会把那些小数点后面带有 6个零以上的浮点数值转换为以 e表示法表示的数值(例如, 0.0000003会转换成 3e-7)
• 浮点数的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。例如 0.1加 0.2并不等于 0.3
Number
• Number.MIN_VALUE• Number.MAX_VALUE
• isFinite() 函数用来鉴定某个数值是否在最大和最小值之间
NaN
• NaN是一种特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况。 ECMAScript中,任何数值除以0会返回 NaN
• 1.任何涉及 NaN的操作都会返回 NaN• 2.NaN与任何值都不相等,包括 NaN本身
isNaN
• isNaN()在接收到一个值以后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值 ,而任何不能被转换为数值的值都会导致这个函数返回 true。
• 在基于对象调用 isNaN() 函数时,会首先调用对象的 valueOf()方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用 toString()方法,再测试返回值
数值转换
• 有 3个函数可以把非数值转换为数值:
• Number() parseInt() parseFloat()
Object类型
• constructor—保存着用于创建当前对象的函数
• hasOwnProperty(propertyName)—用于检查给定的属性在当前对象实例中是否存在,其中作为参数的属性名必须以字符串形式指定
• isPrototypeOf(object)--用于检查传入的对象是否是另一个对象的原型
• propertyIsEnumerable(propertyName)—用于检查给定的属性是否能够使用 for-in语句来枚举,作为参数的属性名必须同样以字符串形式指定
• toString()— 返回对象的字符串表示
• valueOf()--- 返回对象的字符串、数值或布尔值表示。通常与 toSting()方法的返回值相同
IE
• IE的 javascript实现在对象方面稍有不同。IE中只有开发人员定义的对象才继承自 Object。而且所有 BOM和 DOM对象也与这里介绍的不同,可能不会具有 Object的所有属性和方法
位操作符
• 按位非 (NOT) ~ 操作数的负值减一
• 按位与 (And) &• 按位或 (OR) |• 按位异或 (XOR) ^• 左移 <<• 右移 >>• 无符号右移 >>>
With语句
• With语句的作用是将代码的作用域设置到一个特定的对象中。
• with语句的语法如下 :• With(expression) statement
• switch在比较值时使用的是全等操作符,因此不会发生类型转换
函数
• return语句可以不带有任何返回值,在这种情况下,函数在停止执行后将返回 undefined 值。
• 推荐做法要么让函数始终都返回一个值,要么永远都不要返回值。否则会给调试代码带来不便
• ECMAScript 函数不能重载,但通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载
• 理解 ECMAScript 及其纷繁复杂的各种实现,是理解其在Web浏览器中的实现 ----JavaScript的关键
变量
• ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值
• 基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置
• 引用类型值指那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象
传递参数
• ECMAScript中所有函数的参数都是按值传递的。
• Function setName(obj)• {
obj.name=“Nicholas”;
}
var person=new Object();
setName(person);
alert(person.name); //Nicholas访问变量有按值和按引用两种方式,而参数只能按值传递
执行环境
• 执行环境定义了变量或函数有权访问的其它数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它
执行环境
• 全局执行环境是最外面的一个执行环境,根据 ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是 window对象,因此所有全局变量和函数都是作为 window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁
• 每个函数在被调用时都创建自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。 ECMAScript程序中的执行流正是由这个方便的机制控制着。
• 当代码在一个执行环境中执行时,会创建由变量对象构成的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即 arguments对象(这个对象在全局环境中是不存在的 )。作用域链中的下一个变量对象来自包含 (外部 )环境,而再下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象
• 内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性的、有次序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。
引用类型
• 引用类型的值 (对象 )是引用类型的一个实例。在 ECMAScript中,引用类型是一种数据结构用于将数据和功能组织在一起。
• 对象是某个特定引用类型的实例。新对象是使用 new操作符后跟一个构造函数来创建的,构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。
RegExp类型
• 实例属性:
• global 、 ignoreCase、 lastIndex、multiline、 source
• 实例方法:
• exec() test()• 构造函数属性:
• Input、 lastMath、 lastParen、 leftContext、multiline、 rightContext
Function类型
• 函数实际上是对象,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定
• 函数没有重载只有覆盖
函数声明与函数表达式
• 解析器会率先读取函数声明,并使其在执行任何代码之前可用 (可以访问 );至于函数表达式,则必须等到解析器执行到它所在的代码行才会真正被解释执行
alert(sum(10,10)); 正确
function sum(num1,num2){return num1+num2}
alert(sum(10,10)); 错误
var sum=function(num1,num2){return num1+num2}
apply()
• 每个函数都包含两个非继承而来的方法: apply()和 call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this对象的值。
• 首先 apply()方法接受两个参数,一个是在其中运行函数的作用域,另一个是参数数组。其中第二个参数可以是 Array的实例,也可以是 arguments对象
call()
• call()方法与 apply()方法的作用相同,它们的区别仅在于接受参数的方式不同。对于 call()方法而言,第一个参数是作用域没有变化,变化的只是其余的参数都是直接传递给函数的。
apply() call()用武之地
• 真正强大的地方是能够扩充函数赖以运行的作用域
window.color=“red”;
var o={color:”blue”};
function sayColor(){ alert(this.color)}
sayColor();
sayColor.call(this);
sayColor.call(window);
sayColor.call(o); //blue
使用 call()或 apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系
基本包装类型
• Boolean、 Number、 String• slice(3,5) substr(3,5) substring(3,5)• 下标 个数
下标
• 如果有负数, slice()方法会把传入的负值与字符串的长度相加, substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为 0,最后, substring()方法会把所有负值参数都转换为 0
• 因为有了基本包装类型,所以 JavaScript中的基本类型值可以被当做对象来访问
共同特征
• 1.每个包装类型都映射到同名的基本类型
• 2.在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作
• 3.操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象
内置对象
• ECMA-262对内置对象的定义是:“由 ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在 ECMAScript程序执行之前就已经存在了 .”意思就是说,开发人员不必显示地实例化内置对象;因为它们已经实例化了。只定义了两个内置对象:
• Global和 Math
Global
• 终极“兜底儿对象”
• 不属于任何其他对象的属性和方法,最终都是它的属性和方法
• 没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是 Global对象的属性。
• 如 isNaN()、 isFinite()、 parseInt()、 parseFloat()
• ECMAScript没有指出如何直接访问 Global对象,但Web浏览器都是将这个全局对象作为 window对象的一部分加以实现的
• 在原型模型中,为了实现类继承,必须首先将子类构造函数的 prototype设置为一个父类的对象实例。创建这个父类对象实例的目的就是为了构成原型链,一起到共享上层原型方法的作用。但创建这个实例对象时,上层构造函数也会给它设置对象成员,这些对象成员对于继承来说是没有意义的。虽然我们也没有给构造函数床底参数,但却是创建了若干没有用的成员,尽管其值时 undefined
var anObj=new aFunction()
• 第一步:建立一个新对象
• 第二步:将对象内置的原型对象设置为构造函数 prototype引用的那个原型对象
• 第三步:将该对象作为 this 参数调用构造函数,完成成员设置等初始化工作。
• 对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用
闭包
• 闭包,就是在构造函数体内定义另外的函数座位目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。
• 这使得只要目标对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目标对象的方法内却始终能引用到该变量的值,而且该值只能通过这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只对应新的值。和上次那次调用的是各自独立的。
闭包
• 闭包最常用的方式就是返回一个内联函数( 就是在函数内部声明的函数)
• JS中有作用域和执行环境的问题,在函数内部的变量在函数外部是无法访问的,在函数内部却可以得到全局变量。通过创建一个闭包用来在外部访问这个函数内的变量
• 闭包的用途除了读取函数内部变量,还有一个作用就是可以使这些变量一直保存在内存中
• 使用闭包要注意,由于变量被保存在内存中,所以会对内存造成下好,所以不能滥用闭包。解决方法是在退出函数之前将不使用的局部变量全部删除
面向对象程序设计
工厂模式
构造函数模式
原型模式
组合使用构造函数模式和原型模式
动态原型模式
寄生构造函数模式
稳妥构造函数模式
原型链
借用构造函数
组合继承
原型式继承
寄生式继承
寄生组合式继承
创建对象
继承
工厂模式
• 解决了创建多个相似对象的问题,却没解决对象识别的问题(即怎样知道一个对象的类型)
构造函数模式
构造函数
• 按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数应该以一个小写字母开头。
• 以这种方式调用构造函数实际上经历以下 4个步骤:
1.创建一个新对象
2. 将构造函数的作用域赋给新对象 ( 因此 this 就指向了这个新对象 )
3. 执行构造函数中的代码 (为这个新对象添加属性 )
4. 返回新对象
constructor
• 对象的 constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是 instanceof操作符更可靠一些。
• 创建自定义构造函数意味着将来可以将它的实例标识为一种特定的类型,而这正是构造函数模式胜过工厂模式的地方。
• person1和 person2之所以同时是 object的实例,是因为所有对象均继承自 Object。
• 以这种方式定义的构造函数是定义在 Global对象(在浏览器中是 window对象)中的。因此除非另有说明, instanceof操作符和 constructor 属性始终会假设是在全局作用域中查询构造函数。
构造函数当作函数
构造函数模式的缺点
每个方法都要在每个实例上重新创建一遍
弥补构造函数模式缺陷
• 新问题: sayName()是全局函数却只能被某个对象调用,如果对象需要定义很多方法,就要定义很多个全局函数
原型模式
• 我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。
• 如果按照字面意思来理解, prototype 就是通过调用构造函数而创建的那个对象的原型对象。使用原型的好处是可以让所有对象实例共享它所包含的属性和方法
原型模式
理解原型
• 无论什么时候,只要新建了一个函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,默认情况下,所有 prototype 属性都会自动获得一个 constructor 属性,这个属性包含一个指向 prototype 属性所在函数的指针。
• Persons.prototype.constructor 指向 Persons
理解原型
• 创建自定义构造函数后,原型属性默认只会取得 construcotr 属性;至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型属性。很多实现中这个内部属性的名字是 _proto_。
• 要明确真正重要的一点,就是这个连接存在于实例与构造函数的原型属性之间,而不是存在实例与 构造函数之间。
理解原型
• 虽然在某些实现中无法访问到内部的 _proto_ 属性,但在所有实现中都可以通过 isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果对象的 _proto_指向调用 isPrototypeOf()方法的对象 (Person.prototype),那么这个方法就返回 true
• alert(Person.prototype.isPrototypeOf(person1) //true
理解原型
• 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值,如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
理解原型
• 原型最初只包含 constructor 属性,而该属性也是共享的,因此可以通过对象实例访问
• 虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。
屏蔽原型中的属性
• 可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值
屏蔽原型中的属性
• 添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null,也只会在实例中设置这个属性,而不会删除其指向原型的链接。不过使用 delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性。
hasOwnProperty()
• 使用 hasOwnProperty()可以检测一个属性是存在于实例中还是存在于原型中。这个方法 (不要忘了它从 Object 继承而来)只在给定属性存在于对象实例中时,才会返回 true。
原型与 in操作符
• 单独使用时, in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中
只要 in操作符返回 true而 hasOwnProperty() 返回false,就可以确定属性是原型中的属性
判断是否属于原型
for in
• 使用 for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性(即设置了 [[DontEnum]]标记的属性 )的实例属性也会在 for-in循环中返回 , 因为根据规定,所有开发人员定义的属性都是可枚举的——只有 IE除外
IE不会弹出警告框
更简单的原型语法
原型动态性
• 由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来—即使是先创建了实例后修改原型也照样如此。
• 但是如果用前面更简单的原型语法那种方式重写了原型对象的话,就等于切断了构造函数与最初原型之间的联系。因此会报错
原生对象的原型
• 原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型 (Object、 Array、 String)都在其构造函数的原型上定义了方法。
• 通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。
原型模式缺点
• 省略为构造函数传递初始化参数环节,结果所有实例在默认情况下都将取得相同的属性值。
• 原型模式最大的问题是由其共享的本性所导致的(尤其针对包含引用类型值的属性而言)
包含引用类型值得属性时
组合构建函数模式和原型模式
动态原型模式
• 使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,就会切断现有实例与新原型之间的联系
针对组合使用构造函数模式和原型模式时,将构造函数和原型独立成两个函数容易导致困惑
寄生构造函数模式
• 这个模式可以在特殊的情况下用来为对象创建构造函数
除了使用 new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的
创建具有额外方法的特殊数组
寄生构造函数模式
• 返回的对象与构造函数或者与构造函数的原型属性之间没有关系,也就是说构造函数返回的对象与构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof操作符来确定对象类型。因此建议在可以使用其他模式的情况下,不要使用这种模式
稳妥构造函数模式
• 稳妥对象指的是没有公共属性,而且其方法也不引用 this对象。
• 稳妥对象最适合在一些安全的环境中 (这些环境会禁止使用 this和 new) ,或者在防止数据被其他应用程序 (如Mashup程序)改动时使用。
稳妥构造函数模式
• 稳妥对象没有公共属性,而且其方法也不引用 this的对象。
• 新创建对象的实例方法不引用 this• 不适用 new操作符调用构造函数
稳妥构造函数模式
• 稳妥构造函数模式与寄生构造函数模式类似,创建的对象与构造函数之间没有什么关系,因此 instanceof操作符对这种对象也没有意义
继承
• 许多 OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于函数没有签名,在 ECMAScript中无法实现接口继承。 ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现
原型链
• 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如让原型对象等于另一个类型的实例。结果会怎么样呢?
• 此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构造了实例与原型的链条。这就是所谓原型链的基本概念。
实现原型链基本模式
确定原型和实例之间的关系
• 第一种方法使用 instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true
• 第二种方法使用 isPrototypeOf()。只要原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
谨慎定义方法
给原型添加方法的代码一定要放在替换原型的语句之后
通过 SuperType实例调用 getSuperValue()时还调用原来的方法
通过原型链实现继承时,不能使用对象字面量创建原型方法
原型链问题
包含引用类型值的原型
创建子类型实例时,不能向超类型的构造函数中传递参数。实际上应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数
借用构造函数
• 解决原型中包含引用类型值所带来的问题
函数只不过是在特定环境中执行代码的对象,因此通过 apply()和 call()方法也可以在将来新创建的对象上执行构造函数
也叫伪造对象或经典继承
借用构造函数
实现参数传递
借用构造函数的问题
• 如果仅仅是借用构造函数,也将无法避免构造函数模式存在的问题—方法都在构造函数中定义,因此函数复用无从谈起。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
• 考虑到这些问题,借用构造函数的技术也是很少单独使用的
组合继承(伪经典继承)
• 将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。
• 背后思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
• 这样既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性
组合继承
原型式继承
• 在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承时完全可以胜任的。不过包含引用类型的属性始终都会共享相应的值,就像使用原型模式一样
寄生式继承
• 思路与寄生式构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象
寄生组合式继承
• 组合继承最大的问题就是无论什么情况下,都会调用两次超类构造函数:一次是在创建子类型原型的时候,另一个是在子类型构造函数内部。
寄生组合式继承
• 所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的思路是 :不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。
寄生组合式继承是引用类型最理想的继承范式
闭包
• 闭包是指有权访问另一个函数作用域中的变量的函数。
• 创建闭包的常见方式,就是在一个函数内部创建另一个函数。
不存在块级作用域
模仿块级作用域
• for循环外部插入一个私有作用域。在匿名函数中定义的任何变量,都会在执行结束时被销毁。因此,变量 i只能在循环中使用,使用后即被销毁。而在私有作用域中能够访问 count是因为这个匿名函数是一个闭包
模仿块级作用域
• 经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们应该尽量少向全局作用域中添加变量和函数。通过创建私有作用域每个开发人员既可以使用自己的变量,又不必担心搞乱全局作用域
私有变量
• 私有变量包括函数的参数、局部变量和在函数内部定义的其它函数。如果在函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。利用这一点就可以创建用于访问私有变量的公有方法。
• 特权方法:有权访问私有变量和私有函数的公有方法。
构造函数中定义特权方法
隐藏不该被直接修改的数据
缺点
• 在构造函数中定义特权方法的缺点就在于你必须使用构造函数模式来达到这个目的,而构造函数模式的缺点就是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题
静态私有变量 (创建特权方法 2)
模块模式(单例添加私有变量和特权方
法)
• 如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是Object的实例,因为最终要通过一个对象字面量来表示它
增强的模块模式
• 有人进一步改进了模块模式,在返回对象之前加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型的实现,同时是必须添加某些属性或方法对其加以增强的情况。
增强的模块模式
BOM
• BOM的核心对象是 window,它表示浏览器的一个实例。在浏览器中, window对象有双重角色,既是通过 JavaScript访问浏览器窗口的一个窗口,又是 ECMAScript规定的 Global对象。这意味着网页中定义的任何一个对象、变量和函数,都以 window作为其 Global对象,因此有权访问 parseInt()等方法
窗口关系及框架
• 如果页面包含框架,每个框架都有自己的 window对象,并且保存在 frames集合中
• top对象始终指向最高(最外)层的框架,也就是浏览器窗口
• parent对象始终指向当前框架的直接上层框架
• 某些情况下, parent有可能等于 top,但在没有框架的情况下 ,parent一定等于 top(此时它们都等于 window)
• 最好使用 top而非 window来引用框架
跨浏览器取得窗口左上边位置
间歇调用和超时调用
• 间歇调用:
• setInterval(); clearInterval();
• 超时调用:
• setTimeout(); clearTimeOut();
location对象
• location提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能
查询字符串参数
navigator
• navigator对象已成为识别客户端浏览器的事实标准
• IE中检测插件的唯一方式就是使用专有的 ActiveXObject类型,并尝试创建一个特定插件的实例。 IE是以 COM对象的方式实现插件的,而 COM对象使用唯一标识符来标识。因此,要想检查特定的插件,必须知道其 COM标识符
检测插件
检测所有浏览器中的 Flash
Screen对象
• JavaScript中有几个对象在编程中用处不大,而 screen对象就是其中之一。 screen对象基本上只用来表明客户端能力,其中包括浏览器窗口外部的显示器的信息,如像素宽度和高度等。每个浏览器中的 screen对象都包含着各不同的属性
history对象
• history对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。因为 history是 window对象的属性。因此每个浏览器窗口、每个标签页乃至每个框架都有自己的 history对象与特定的 window对象关联。
DOM
• DOM(文档对象模型 )是针对 HTML和 XML文档的一个 API。 DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。 DOM脱胎于 Netscape及微软创始的 DHTML,但现在它已经成为表现和操作页面标记的真正的跨平台、语言中立的方式。
• IE中所有 DOM对象都是以 COM对象的形式实现的。因此 IE中的 DOM与原生 JavaScript对象的行为或活动特点并不一致
节点层次
• DOM可以将任何 HTML或 XML文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构
• nodeName和 nodeValue 属性
• childNodes、 parentNode; firstChild、 lastChild; previousSibling、 nextSibling;(节点关系 )
• appendChild()、 insertBefore()、 replaceChild()、 removeChild()操作节点
Document类型
• JavaScript通过 Document类型表示文档。在浏览器中, document对象是 HTMLDocument( 继承自 Document类型 )的一个实例,表示整个 HTML页面。而且 document对象是 window对象的一个属性,因而可将其作为全局对象来访问。
• Document类型可以表示 HTML页面或者其他基于 XML的文档。不过,最常见的应用还是作为 HTMLDocument实例的 document对象。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。
文档子节点
• document.documentElement 属性 指向 HTML页面中的 <html>元素
• document.body 属性直接指向 <body>元素• document.title 属性指向 <title>元素• url 属性包含页面完整的 URL• domain 属性只包含页面域名
• referrer保存连接到当前页面的那个页面的URL
( url,domain,referrer)中只有 domain可设置,但不是随意设置
Node类型
• Node.ELEMENT_NODE(1);• Node.ATTRIBUTE_NODE(2);• Node.TEXT_NODE(3);• Node.CDATA_SECTION_NODE(4);• Node.ENTITY_REFRENCE_NODE(5);• Node.ENTITY_NODE(6);• Node.PROCESSING_INSTRUCTION_NODE(7)• Node.COMMENT_NODE(8);• Node.DOCMENT_NODE(9);• Node.DOCUMENT_TYP_NODE(10);• Node.DOCUMENT_FRAGEMENT_NODE(11);• Node.NOTATION_NODE(12);
DOM扩展 -滚动
• scrollIntoView(alignWithTop)• scrollIntoViewInNeeded(alignCenter)
- - - - - - - - - - - - - - -元素的窗口 - - - - - - - - - - - - - - - -
• scrollByLines(lineCount)• scrollByPages(pageCount)
- - - - - - - - - - - - - - -元素本身 - - - - - - - - - - - - - - - - -
DOM操作技术
• 动态脚本
DOM操作技术
• 动态样式
DOM操作技术
• 动态创建表格
DOM操作技术
• 使用 NodeList• 理解 NodeList 及其“近亲” NamedNode
Map和 HTMLCollection,是从整体上透彻理解 DOM的关键所在。这三个集合都是“动态的”;换句话说,每当文档结构发生变化时,他们都会得到更新。因此,他们始终都会保存着最新、最准确的信息。从本质上说,所有 NodeList对象都是在访问 DOM文档时实时运行的查询。
无限循环
解决无限循环
DOM2和 DOM3
• DOM1级主要定义的是 HTML和 XML文档的底层结构。 DOM2和 DOM3级则在这个结构的基础上引入了更多的交互能力,也支持了更高级的 XML 特性。为此, DOM2和 DOM3分为许多模块(模块之间有某种关联),分别描述了 DOM的某个非常具体的子集。
新增模块
• DOM2级核心:在 1级核心基础上构建,为节点添加了更多方法和属性
• DOM2级视图:为文档定义了基于样式信息的不同视图
• DOM2级事件:说明了如何使用事件与 DOM文档交互
• DOM2级样式:定义了如何以编程方式来访问和改变 CSS样式信息
• DOM2级遍历和范围:引入了遍历 DOM文档和选择其特定部分的新接口
• DOM2级 HTML :在 1级 HTML基础上构建,添加了更多属性、方法和新接口
判断是否支持某模块
事件
• JavaScript与 HTML之间的交互是通过事件完成的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器 (或处理程序 )来预订事件,以便事件发生时执行相应的代码,这种在传统软件工程中被称为观察员模式的模型,支持页面的行为 (JavaScript 代码)与页面的外观( HTML和 CSS 代码)之间的松散耦合
事件
• Firefox、 Opera、 Safari和 Chrome 全都已经实现了“ DOM2级事件”模块的核心部分, IE是唯一一个仍然使用其专有事件系统的主要浏览器
事件流
• 描述的是从页面中接收事件的顺序
• IE事件冒泡流
• Netscape事件捕获流
DOM2级事件
• DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和冒泡阶段
• 首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应
在 HTML中指定事件处理程序缺点
• 1.存在时差问题,用户可以在事件处理程序尚不具备执行条件时触发相应的事件
• 2.HTML与 JavaScript 代码紧密耦合。如果要更换事件处理程序,就要改动两个地方: HTML 代码和 JavaScript 代码
• 因此应使用 JavaScript 指定事件处理程序
DOM0级事件处理程序
• 通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
• 使用 DOM0级指定的事件处理程序被认为是元素的方法
DOM2级事件处理程序
• addEventListener() removeEventListener()• 参数:要处理事件名、作为事件处理程序的函数、布尔值 (true捕获 ,false冒泡 )
• 事件处理程序也是在其依附的元素的作用域中运行
DOM2可以添加多个事件处理程序
• 大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,不建议在事件捕获阶段注册事件处理程序
IE事件处理程序
• attachEvent() detachEvent()• 参数:事件处理程序名称与事件处理程序函数
• 事件处理程序在全局作用域中运行
颠倒的执行顺序
• Hello world后才 true
跨浏览器事件处理程序
使用 EventUtil
• 并没有考虑到所有的浏览器问题,如 IE中的作用域问题
DOM2级规定的 5中事件
• UI事件• 鼠标事件
• 键盘事件
• HTML事件,当浏览器窗口发生变化或发生特定的客户端 /服务器交互时触发
• 变动事件 , 当底层 DOM结构发生变化时触发
UI事件
• 主要与元素的焦点相关,而且仅在兼容 DOM的浏览器中得到了支持 .有 3个 UI事件:
• DOMActive• DOMFocusIn• DOMFocusOut
鼠标事件
• click• dblclick• mousedown• mouseout• mouseover• mouseup• mousemove
单体模式
单体在 js中有多种用于,它用来划分命名空间。可以减少网页中的全局变量的数量 (在网页中使用全局变量有风险 );可以在多人开发时避免代码的冲突 (使用合理的命名空间 )等等
中小型项目中,单体可以用作命名空间把自己的代码组织在一个全局变量名下;
大型项目中,单体可以用来把相关代码组织在一起以便日后好维护
工厂模式
• 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
• 通过使用工厂方法而不是 new关键字及具体类,可以把所有实例化的代码都集中在一个位置,有助于创建模块化的代码,这才是工厂模式的目的和优势
XHR 工厂(简单工厂模式)
XHR 工厂(抽象工厂模式)
桥梁模式
• 用来弱化它与使用它的类和对象之间的耦合,就是将抽象与其实现隔离开来,以便二者独立变化;这种模式对于 js中常见的时间驱动的编程有很大益处,桥梁模式最常见和实际的应用场景之一是时间监听器回调函数。
桥梁模式
• 还可以用来连接公开的 API 代码和私有的实现代码,还可以把多个类连接在一起。特权方法,也是桥梁模式的一种特例。
装饰者模式
• 为对象增加功能
• 动态地给一个对象添加一些额外的职责,就扩展功能而言,它比生成子类方式更为灵活。
• 装饰者模式与组合模式共同点:都与所包装的对象实现统一的接口并且会把任何方法调用传递给这些对象。组合模式用于把众多子对象组织为一个整体,而装饰者用于在不修改现有对象或派生子类前提下为其添加方法。
装饰者模式
• 装饰者的运作是透明的,这就是说你可以用它包装其他对象,然后继续按照之前使用那个对象的方法来使用。
装饰者模式
• 上例看出,一切都可以不用事先知道组件对象的接口,甚至可以动态的实现,在为现有对象增添特性这方面,装饰者模式有极大的灵活性。
• 如果需要为类增加特性或方法,而从该类派生子类的解决办法又不实际就该使用装饰者模式。
• 之所以不实际最常见原因是需要添加的特性或方法的数量要求使用大量子类
组合模式
• 将对象组合成树形结构以表示“部分 -整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
• 组合模式是一种专为创建Web上的动态用户界面而量身定制的模式。使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为。组合模式擅长于对大批对象进行操作。
组合模式好处
• 可以用同样的方法处理对象的结合与其中的特定子对象
• 可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历
组合模式适用范围
• 存在一批组织成某层次体系的对象 ( 具体结构可能在开发期间无法知道)
• 希望对这批对象或其中一部分对象实施一个操作
• 组合模式就是将一系列相似或相近的对象组合在一个大的对象,由这个大对象提供一些常用的接口来对这些小对象进行操作,代码可重用,对外操作简单。
组合模式举例
• 对 form内的元素,不考虑从页面设计情况下,一般就剩下 input了,对这些 input都有 name和 value 属性,因此可以将这些 input元素作为 form对象的成员组合起来, form对象提供个对外的接口,便可以实现一些简便的操作,比如设置某个 input的 value,添加 /删除某个 input等等
组合模式
门面模式
几乎所有 JavaScript库的核心原则
子系统中的一组接口提供一个一致的界面,门面模式定义了一个高层接口,这个接口使得这一子系统更加容易使用,简单的说这是一种组织性的模式,可以用来修改类和对象的接口,使其更便于使用
两大作用: 1简化类的接口 2 消除类与使用它的客户代码之间的耦合
基本的门面