JavaScript高级程序设计
提示:以下是本篇文章正文内容,下面案例可供参考
一、javascript简介
1.ECMA
ECMA兼容
2.DOM
功能
- 提供访问和操作网页内容的方法和接口
什么是文档对象模型
文档对象模型(DOM)是针对 XML 但经过扩展用于 HTML 的应用程序编程接口。DOM 把整个页面映射为一个多层节点结构。HTML或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。
DOM级别
- 一共四个级别
DOM0级
- 实际是不存在的
- DOM0 级指的是 Internet Explorer 4.0 和 Netscape Navigator 4.0最
初支持的 DHTML
DOM1级
- 两个模块组成 DOM核心和DOM HTML
- DOM核心规定如何映射文档结构
- DOM HTML 在核心基础上扩展,添加针对HTML的对象和方法
DOM2级
- 扩展,增添了鼠标和用户界面事件、范围、遍历等,且支持CSS
- DOM视图 定义跟踪不同文档
- DOM事件 定义事件和事件处理接口
- DOM样式 定义了基于CSS为元素应用样式的接口
- DOM遍历和范围 定义了遍历和操作文档树的接口
DOM3级
- 扩展,支持XML1.0 如 XML INFOSET、XPath、XBase
浏览器对DOM级别支持
3.BOM
功能
- 提供与浏览器交互的方法和接口
- window对象是核心
扩展方法和接口
弹出新浏览器窗口的功能
如window对象的close、open、alert、prompt、confirm
移动、缩放和关闭浏览器窗口的功能;
提供浏览器详细信息的 navigator 对象;
提供浏览器所加载页面的详细信息的 location 对象;
常用属性host、hostname、href 如 windows.location.href
常用方法reload重载当前文档、 replace 替换当前文档
提供用户显示器分辨率详细信息的 screen 对象;
对 cookies 的支持;
像 XMLHttpRequest 和 IE的 ActiveXObject 这样的自定义对象
图示
二、html使用javascript
1.script标签
script标签属性
- async 表示立即下载脚本,只对引用外部的脚本有效
- charset 指定字符集 废弃
- defer 表示完全被解析和显示后才执行
- lauguage
- src
- type
加载顺序
JavaScript 代码将被从上至下依次解释
如果引用外部的,且标签内有js代码,则不会执行内部的代码
标签位置
- 放在head内,意味着必须等全部js代码都被下载解析执行完才能呈现页面,会导致延迟
- 放在Body结尾标签后会缩短
load和Domcontentload
- domcontentload文档加载解析完毕之后立即执行,不管等待图片和子框架
- load是页面所有资源完毕,在domcontentload之后
延迟脚本
- 立即下载延迟执行,多个延迟脚本,一般会先后顺序执行
异步脚本
不保证先后顺序,单会在onload之前执行,domcontentload前后不一定
嵌入代码和外部文件
noscript
- 没有开启脚本的客户端可书写这个标签用于提示
三、基本概念
语法
- 区分大小写
- 标识符,采用驼峰式
- 严格模式,use strict
关键字和保留字
- 关键字
- 保留字
变量
- 松散类型,可保存任何数据类型,但注意有变量提升,还有undefined
数据类型
五种简单数据类型和一种复杂数据类型
- Undefined 、 Null 、 Boolean 、 Number和 String
- Object
undefined
typeof
- 后面跟变量或者数值字面量
- typeof null =object
- typeof 正则=function
- typeof 未定义变量=undefined
null
null==undefined 返回true 保存的是对象
Number
- NaN返回数值却未返回数值 isNaN
- NaN==NaN false 跟任何值比都是false
操作符
语句
for in语句
- 枚举对象的属性,枚举之前最好确认不是null或者undefined
label语句
with语句
- 是将代码的作用域设置到一个特定的对象
- 严格模式不能使用
函数
命名参数和arguments
- 定义函数,但调用时候参数可以随便传,arguments中调用
- arguments类数组,可重写命名参数值 ,值同步,但是内存空间不是一块的
没有重载
- 同名函数后者会覆盖前者,但是可通过检查参数个数等方式去模拟重载
四、变量、作用域、内存问题
基本类型和引用类型的值
- 基本类型null undefined string number boolean 按值访问
- 引用类型object 操作引用 引用访问
动态的属性
- 保存方式不同,基本类型不能动态添加属性和方法
- 能给引用类型值动态地添加属性
复制变量值
- 基本类型拷贝值,变量完全独立,变量值也完全独立
- 引用类型,变量完全独立,但是变量值指向堆空间同一个引用
传递参数
- 都是值传递,基本类型复制给形参的局部变量
- 引用类型把地址赋值给局部变量
检测类型
执行环境和作用域
- 执行环境定义了变量或函数有权访问的其他数据,决定,了它们各自的行为。每个执行环境都有一个与之关联的变量对象.
- 环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它
- 全局执行环境是最外围的一个环境,执行环境中的所有代码执行完
毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁 - 每个函数都有自己的环境,执行流到该函数,推入环境栈,执行流结束,弹出交给之前的.
- 当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止
延长作用域链
- 执行环境的类型总共只有两种——全局和局部(函数),但还是有其他办法来延长作用域链
- 是当执行流进入下列任何一个语句时,作用域链就会
得到加长:
try-catch 语句的 catch 块;
with 语句 - 在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移
除
没有块级作用域
声明变量
- 使用 var 声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在 with 语句中,最接近的环境是函数环境。如果初始化变量时没有使用 var 声明,该变量会自动被添加到全局环境
查询标识符
- 变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链
垃圾收集
- 具有自动垃圾收集机制
- 执行环境执行环境会负责管理代码执行过程中使用的内存
- 找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作
标记清除
引用计数
性能问题
管理内存
五、引用类型
Object类型
创建对象实例的两种方式
- new Object() Obejct是构造函数
- 对象字面量{} 不调用构造函数
变量访问对象属性
var a=“name”
- person.name
- person[name]
- person[“a”]
- 特别是属性名字有空格时候 只能用变量不能用.访问
Array
创建数组
- new Array( new可省略
- 对象字面量
- 尽量不要隔着空位置插入数组,因为空位置引起来undefined
检测数组 isArray
- instanceof Array 单个全局环境可以,多个的话有不同框架引起问题
- Array.isArray() 不管多少全局环境都可以
转换方法 join
- 使用不同分隔符构建字符串
- toString( valueof( tolocaleString(
- 注意以上三个都是对象的方法,对象中若覆盖了返回结果可能不一样
栈方法 push pop
- 栈 后进先出 栈顶
- 推入 push 放置数组末端
- 推出 pop
队列方法 shift
- 队列 先进先出
- shift()模拟从数组前端取得数据
重排序 reverse sort
- reverse 翻转数组顺序
- sort 排序,可接受函数进行规则制定
操作方法 concat slice
- concat 创建当前数组的副本,将接受的参数添加到数组末端,返回值是数组
- slice 基于当前数组创建一项或多项创建一个新数组,不会影响原数组
slice(起始位置} slice(起始位置,结束为止} slice(起始位置,删除的项数,插入的字符串},返回值是数组 - 可实现插入 替换 删除功能
位置方法 indexOf lastindexOf
- 参数 查找项,可缺省的起始位置
- 返回值是索引位置
迭代方法
- 接受两个参数 函数,函数的作用域对象–this
- 传入的函数接受三个参数 数组项值、该项的索引,数组本身
- every 每一项返回true则返回true
- filter 返回true的组成项的数组
- forEach 没有返回值
- map 返回每次函数调用的结果组成的数组
- some 任意一项返回true 返回true
归并方法
- 接受两个参数 调用函数和可缺省的初始值
- 迭代数组所有项,最后返回最终值
- 传入函数接受四个参数 前一个值,当前值,当前索引,数组对象
- reduce从前往后
- reduceRight从后往前
Date
创建Date实例
- new Date( 当前日期,调用date构造函数
- Date.parse接受日期 返回毫秒数
- Date.UTC
- Date.now
继承的方法
- tolocaleString toString valueof
- 前两者返回日期,valueof返回毫秒数
日期格式化
日期时间组件方法
- getTime
- getDate返回几号
- getFullYear 返回2007
- getDay 星期几
- getHours
- getMinutes
- getSeconds
正则RegExp类型(略)
Function类型
没有重载(深入理解)
- 可以想象成指针,实际上是覆盖了引用第一个函数的变量
函数声明和函数表达式
- 解释器率先读取函数声明,并在执行任何代码前可用,函数表达式则执行到该位置可用,
- 函数声明变量提升(考题)
作为值的函数
- 函数声明, 函数名本来就是变量,赋值给另外一个变量
- 即像传递参数一样把函数传递给另外的函数,而且可以将一个函数作为另外一个函数的返回结果
- 要访问函数的指针而不执行函数的话,需要去掉函数名后的括号
函数内部属性
- arguments类数组对象
- 对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数
- this 具体看是指向windows还是对象的
- 函数对象的属性: caller 调用当前函数的函数的引用
- arguments.calllee.caller
函数属性和方法
- 每个函数都包含length和prototype
- length表示希望接受参数的个数 如sayName.length
- prototype是保存所有实例方法的所在,不可枚举,所以for in发现不了
- call()和apply()作用相同,扩充参数和扩充函数的作用域
- blind 创建一个函数的实例 ,this会绑定到传给blind函数的值
基本包装类型
Boolean类型
- new Boolean(true
- 重写valueOf和toString
- typeof结果是Object
- falseObjct instanceof Boolean 是true
- falseObjct instanceof boolean是false
Number类型 -略
- new Number()
String类型-略
单体内置对象-略
Global对象
Math对象
六、面向对象的程序设计
理解对象
数据类
- 包含数据属性和访问器属性
- 数据属性包含数据值的位置,这个位置可以读写值,有四个可以描述其行为的特性
- 访问器属性不包含数据值,包含一对getter和setter函数,可缺省,有四个特性
数据属性
Congifurable
- 默认为true
- 能否通过delete删除属性而重新定义
- 能否修改属性的特性
- 能否修改属性为访问器属性
Enumerable
- 默认为true
- 能否通过for in循环返回属性
Writable
- 默认值为true
- 能否修改属性的值
Value
- 默认为undefined
- 包含这个属性的数据值,读写从这个位置读写
修改属性默认特性
- Object.defineProperty
- 参数 属性所在对象 属性名字 数据属性名称
- 注意 修改configurable属性修改以后,就有限制了,再次修改会抛出异常
访问器属性
Configurable
Enumrable
Get
- 读取调用的函数 默认值undefined
Set
- 写入调取的函数 默认值为undefined
访问器属性不能直接定义
- 通过Object.defineProperty来实现定义
- _year是前面下划线是常用的记号,表示只能通过对象方法来访问
定义多个属性
- Object.defineProperties
- 接受两个对象参数,第一个是要添加和修改属性的对象,第二个对象属性要和第一个一一对应
读取属性的特性
- Object.getOwnPropertyDescriptor
- 接受两个参数,对象、属性名称,返回的是一个对象
创建对象
- 虽然可以用Obejct构造函数和对象字面量创建对象,但是使用一个接口创建很多对象,会有大量的重复代码
工厂模式
- 解决创建多个相似对象的问题,但是不知道对象的类型
构造函数模式
- 自定义属性和方法,构造函数首字母要大写
- 构造函数和普通函数唯一区别是用new关键字创建对象
- 过程:创建一个对象,将构造函数作用域赋值给新对象,执行构造函数代码,返回新对象
- 缺点是每个方法在新对象上要重新创建一遍,不是一个Function实例
原型模式
- 创建的每个函数都有一个prototype属性,是一个指针,指向一个对象
- 这个对象可以由特定类型的所有实例共享属性和方法,即prototype就是通过构造函数创建的那个对象的原型对象
理解原型
- 构造函数prototype属性指向原型对象,原型对象会默认取得constructor属性,包含一个指向prototype所在函数的指针
- 原型对象只会取得constructor属性,其他方法则由object继承而来
- 构造函数和实例中都有prototype属性,指向原型对象
- 实例中创建与原型中同名属性会屏蔽原型属性,delete以后恢复
isPrototypeOf()
- 返回boolean,Person.prototype.isPrototypeOf(person)
- 判断该实例是否是该原型创建
Object.getPrototypeOf()
- 返回实例原型,参数是实例对象
hasOwnProperty()
- 实例调用,检测属性存在原型还是实例中,来自实例返回true,来自原型是false
原型与in操作符
- 单独使用in操作符,“name” in person 实例或者原型存在返回true
- for in 返回可枚举的属性,实例和原型的都会返回
- Object.keys()返回对象实例的可枚举属性
- Object.getOwnPropertyNames 返回实例对象中所有属性 可枚举不可枚举都返回
- 重设构造函数以后,即原型的constructor值为Person引用,需要重新改写特性为不可枚举
原型的动态性
- 原型查找值是一次搜索
- 修改之后会同步修改,但是存在已有对象不要改原型中的构造函数 会报错,切断了实例与旧的原型之间的联系
原生对象的原型-略
原型对象存在的问题
- 初始值一样
- 包含引用类型,如数组,一增全增
组合使用构造和原型模式
- 构造函数定义实例属性
- 原型定义方法和共享属性
动态原型模式
- 构造函数中写,只初次调用时候会执行,只对一个方法进行判断就可以
寄生构造函数模式
- 除了用new关键字创建实例,其余与工厂模式一模一样
- 返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同
- 不能依赖 instanceof 操作符来确定对象类型
稳妥构造函数模式
- 使用范围 禁用this和new
- 稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象
- instanceof 操作符对这种对象也没有意义
继承
- ECMA没有签名,只能实现继承,无法实现接口,依赖原型链实现继承
原型链
- 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法
- 即实例的原型对象是另外一个构造函数的实例,不是 SubType 的原型的 constructor 属性被重写了,而是 SubType 的原型指向了另一个对象——SuperType 的原型,而这个原型对象的 constructor 属性指向的是 SuperType
- SubType.prototype = new SuperType(); //继承了 SuperType
- 搜索实例,搜索 SubType.prototype 搜索 SuperType.prototype ,最后一步才会找到该方法。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来
别忘记默认的原型
- 所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针,指向 Object.prototype 。这也正是所有自定义类型都会继承 toString() 、valueOf() 等默认方法的根本原因
- SubType 继承了 SuperType ,而 SuperType 继承了 Object
确定原型和实例的关系
谨慎定义方法
- 一定要在替换原型语句后面
- 通过原型链实现继承时,不能使用对象字面量创建原型方法
原型链问题
- 共享实例
- 创建子类型实例时候,不能向超类传递参数,会影响所有所有的对象实例
借用构造函数
- 解决引用类型共享实例的问题
- 在子类中SuperType.call(this 通过call或apply调用
- 借用构造函数 ,原型链的constructor没改变,因为不是new调用的
- 每个实例就都会具有自己的 colors 属性的副本
- supertype和subtype 实际没有继承关系
传递参数
- 可以在子类型构造函数中向超类型构造函数传递参数
- 了确保SuperType 构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性,即调用call写在第一条
借用构造函数的问题
- 方法都在构造函数中定义,因此函数复用就无从谈起了
组合继承
- 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
原型式继承
- 借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
- 本质上执行了一次浅复制
- 在 object() 函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例
Object.create
- 用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象
- 注意原型模式共享引用类型属性
寄生继承
- 创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象
- 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式
寄生组合继承
- 组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
- 即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背
后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已 - 它只调用了一次 SuperType 构造函数,并且因此避免了在 SubType.
prototype 上面创建不必要的、多余的属性
七、函数表达式
递归
- arguments.callee 使用 arguments.callee 总比使用函数名更保险,一个指向正在执行的函数的指针
- 严格模式下,不能通过脚本访问 arguments.callee
闭包
- 闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
- 即外部函数执行结束,变量还被内部匿名函数占用
- 闭包和匿名函数的区别,闭包持有外部即将销毁的属性,导致不能销毁
闭包与变量
- 作用域链导致即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量
关于this对象
- 匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window
- 个函数在被调用时都会自动取得两个特殊变量: this 和 arguments 。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量
内存泄漏
-如果闭包的作用域链中保存着一个
HTML 元素,那么就意味着该元素将无法被销毁
- 闭包包含引用导致变量无法释放
模仿块级作用域
- 没有块级作用域,函数中for循环 i实际是定义在函数中
- 匿名函数可以用来模仿块级作用域并避免这个问题。用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。
(function(){
//这里是块级作用域
})(); - 函数表达式可以直接加括号表示立即执行函数表达式
私有变量
定义
- 函数中定义的变量外部不能访问,叫私有变量
- 这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访
问这些变量。而利用这一点,就可以创建用于访问私有变量的公有方法 - 把有权访问私有变量和私有函数的公有方法称为特权方法
在构造函数中定义特权方法
- 构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问
静态私有化
- 就在于私有变量和函数是由实例共享的。由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法,作为一个闭包,总是保存着对包含作用域的引用
模块模式
-
为单例创建私有变量和特权方法。指的就是只有一个实例的对象
-
JavaScript 是以对象字面量的方式来创建单例对象
-
创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式
增强的模块模式
- 返回对象之前加入对其增强的代码。这种增强的模块模式适合那
些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况
八、bom
window对象
- BOM 的核心对象是 window ,它表示浏览器的一个实例。在浏览器中, window 对象有双重角色,它既是通过 JavaScript 访问浏览器窗口的一个接口,又是 ECMAScript 规定的 Global 对象。这意味着在网页中定义的任何一个对象、变量和函数,都以 window 作为其 Global 对象,因此有权访问parseInt() 等方法。
全局作用域
- 在全局作用域中声明的变量、函数都会变成 window 对象的属性和方法
- 全局变量不能通过 delete 操作符删除,而直接在 window 对象上的定义的属性可以
窗口关系及框架
- 页面中包含框架,则每个框架都拥有自己的 window 对象,并且保存在 frames 集合中
- 每个 window 对象都有一个 name 属性,其中包含框架的名称window.frames[0] 或者 window.frames[“topFrame”]
top
- top 而非 window 来引用这些框架(例如,通过 top.frames[0] )
- top 对象始终指向最高(最外)层的框架,也就是浏览器窗口。使用它可以确保在一个框架中正确地访问另一个框架。因为对于在一个框架中编写的任何代码来说,其中的 window 对象指向的都是那个框架的特定实例,而非最高层的框架
parent
- 另一个 window 对象是 parent 。顾名思义, parent (父)对象始终指向当前框架的直接上层框架。在某些情况下, parent 有可能等于 top ;但在没有框架的情况下, parent 一定等于top
self
- self ,它始终指向 window ;实际上, self 和 window 对象可以互
换使用。引入 self 对象的目的只是为了与 top 和 parent 对象对应起来,因此它不格外包含其他值
窗口位置
- screenLeft screenTop screenX screenY
- 使用 moveTo()和 moveBy() 方法倒是有可能将窗口精确地移动到一个新位置
窗口大小
- innerWidth 、 innerHeight 、 outerWidth 和 outerHeight