JS高级程序设计自学

JS高级程序设计自学

  • 三、基本概念
    • 基本数据类型
      • Undefined类型
      • Null类型
      • Boolean类型
      • Number类型
      • String类型
    • 引用数据类型
      • Object类型
    • 操作符
      • 一元操作符
      • 位操作符
      • 布尔操作符
      • 乘性操作符
      • 加性操作符
      • 关系操作符
      • 相等操作符
      • 条件操作符(三元运算符)
      • 赋值操作符
      • 逗号操作符
    • 语句
      • if语句
      • do-while语句
      • while语句
      • for语句
      • for-in语句
      • label语句
      • break和continue语句
      • with语句
      • switch语句
    • 函数
      • 理解参数
      • 没有重载
  • 四、变量、作用域和内存问题
    • 基本类型和引用类型
      • 检测类型
    • 执行环境及作用域
    • 垃圾收集
      • 标记清除
      • 引用计数
      • 管理内存
  • 五、引用类型
    • Object类型
    • Array类型
      • 检测数组
      • 转换方法
      • 栈方法
      • 队列方法
      • 重排序方法
      • 操作方法
      • 位置方法
      • 迭代方法
      • 归并方法
    • Date类型
    • RegExp类型
    • Function类型
      • 没有重载
      • 函数声明与函数表达式
      • 作为值的函数
      • 函数内部属性
      • 函数属性和方法
    • 基本包装类型
      • Number类型的方法
      • String类型
    • 单体内置对象
      • Global对象
        • URI编码方法
        • eval()
        • Global对象的属性
        • window对象
        • Math对象
  • 六、面向对象的程序设计
    • 理解对象
      • 属性类型
        • 数据属性
        • 访问器属性
        • 定义多个属性
        • 读取属性的特性
    • 创建对象
      • 1.工厂模式
      • 2.构造函数模式(可以作为普通函数调用)
      • 3.原型模式
        • 遍历对象方法
      • 组合使用构造函数模式和原型模式
      • 动态原型模式
      • 寄生构造函数模式
      • 稳妥构造函数模式
      • 几种方式的特点
    • 继承
      • 原型链
        • 默认原型
        • 原型与实例的关系
        • 谨慎地定义方法
        • 原型链的问题
      • 借用构造函数
      • 组合继承
      • 原型式继承
      • 寄生式继承
      • 寄生组合式继承
  • 七、函数表达式
    • 递归
    • 闭包
      • 闭包和变量
      • 关于this对象
      • 内存泄漏
    • 模仿块级作用域
    • 私有变量
      • 静态私有变量
      • 模块模式
      • 增加的模块模式
  • 八、BOM
    • window对象
      • 全局作用域
      • 窗口关系及框架
      • 窗口位置
      • 窗口大小
      • 导航和打开窗口
    • location对象
    • navigator对象
    • screen 对象
    • history对象
  • 九、客户端检测

三、基本概念

基本数据类型

typeof 操作符 返回字符串 ‘undefined’ ‘boolean’ ‘string’ ‘number’ ‘object’ ‘function’

Undefined类型

  • undefined 声明变量但是未对其初始化。

Null类型

  • null 被认为是一个空的对象引用

Boolean类型

Number类型

数值转换 把非数值转换为数值

函数 作用
Number() 转换任何数据类型为数值
parseInt() 转换字符串为数值
parseFloat() 转换字符串为数值
  • Number()在转换字符串时比较复杂且不够合理,更常用的是parseInt();
  • parseInt(),第二个参数,转换时使用的基数(既多少进制)例如:parseInt(“0xAF”, 16); 指定基数会影响到转换的输出结果。

String类型

双引号和单引号无区别

  • .length属性 返回字符串长度

转换为字符串

方法 作用
toString() 1个参数(基数)
String() 可以将任何类型值转换为字符串
+号运算符 与一个字符串相加

引用数据类型

Object类型

一组数据和功能的集合

Object每个实例都具有下列属性和方法

  • constructor 保存着用于创建当前对象的函数;
  • hasOwnProperty(propertyName) 检查指定属性是否在当前对象实例中(不是实例的原型);
  • isPrototypeof(Object)用于检查传入的对象是否是传入对象的原型;
  • propertyIsEnumerable(propertyName): 用于价差指定的属性能否使用for-in语句来枚举;
  • toLocaleString():返回对象的字符串表示,与执行环境的地区对应;
  • toString():返回对象的字符串表示;
  • valueOf():返回对象的字符串、数值、或布尔值表示,通常与toString()返回值相同;

操作符

一元操作符

前置型(++a)和后置型(a++)

位操作符

布尔操作符

非(NOT)、与(AND)、或(OR)

  • 逻辑非
    由一个叹号(!)表示,可以应用于任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。
    同时使用两个逻辑非,就会模拟Boolean()转型函数的行为。得到这个值真正对应的布尔值。
  • 逻辑与
    由两个和号(&&)表示。
    如果第一个操作数是false,则不会再对第二个操作数求值。
  • 逻辑或
    由两个竖线符号(||)表示。
    如果第一个操作数求值结果为true,就不会对第二个操作数求值了。

乘性操作符

  • 乘法 *
  • 除法 /
  • 求模(余数) %

加性操作符

  • 加法 +
  • 减法 -

关系操作符

小于(<)、大于(>)、小于等于(<=)、大于等于(>=)

相等操作符

  • 相等操作符 (先转换操作数,强制类型转换,再比较它们的相等性)
    由两个等号(==)表示
  • 不相等操作符
    由叹号后跟等于号(!=)表示
  • 全等(===)和不全等( ! ==) 不转换类型 推荐使用

条件操作符(三元运算符)

var a = true ? 1 : 0 ;

赋值操作符

逗号操作符

var num1 = 1, num2 = 2, num3 = 3;

语句

if语句

do-while语句

对条件表达式求值之前,循环体内的代码至少会被执行一次。

while语句

在循环体内的代码被执行之前,会对出口条件求值。

for语句

for-in语句

是一个精准的迭代语句,用来枚举对象的属性。
使用前先确认该对象的值是不是null或者undefined。

label语句

break和continue语句

with语句

导致性能下降,不建议使用。

switch语句

每个case后面都添加一个break语句,可以避免同时执行多个case代码。
如果需要混合几种情形,不要忘了在代码中添加注释,说明有意省略break关键字。
在比较值得时候使用的是全等操作符,不会发生类型转换。

函数

可以封装任意多条语句,使用function关键字声明,后跟一组参数及函数体。
return语句可以不带任何返回值,这种情况,函数在停止执行后返回undefined。

理解参数

  • 参数在内部是用一个数组来表示。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。
  • 在函数体内可以通过arguments对象来访问这个参数数组。
  • arguments对象只是和数组类似(它并不是Array的实例),可以通过下标来访问,使用length属性来确定参数个数。
  • arguments对象可以与命名参数一起使用。

没有重载

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

基本类型和引用类型

基本数据类型 保存在栈内存中。
引用数据类型 值是对象,保存在堆内存中。

  • 复制引用类型的值时,这个值实际上是一个指针,指针指向存储在堆中的一个对象。两个变量实际上引用同一个对象。

检测类型

  • 检测基本数据类型:typeof
  • 检测引用数据类型,想知道某个对象是什么类型的:instanceof

执行环境及作用域

  • 执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
  • 全局执行环境是最外围的一个执行环境,window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。全局执行环境直到程序退出,关闭网页或浏览器才会被销毁。
  • 每个环境都可以向上搜索作用域链,不能向下搜索作用域链而进入另一个执行环境。
  • 不声明而直接初始化变量,是一个常见的错误做法,这样可能导致意外。

垃圾收集

js具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。

  • 局部变量只在函数执行的过程中存在。

标记清除

  • 当变量进入环境,就将这个变量标记为“进入环境”,当变量离开环境,将其标记为“离开环境”。

引用计数

  • 跟踪记录每个值被引用的次数。
  • 循环引用会导致无法删除并回收内存。需要手动断开连接,设置为null。

管理内存

  • 优化内存占用方式,执行中的代码只保存必要的数据。一旦数据不再有用,将其值设置为null释放引用,这种做法叫“解除引用”。

五、引用类型

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

Object类型

大多数引用类型都是Object类型的实例。

  • 创建Object实例的方式
  1. 使用new操作符后跟Object构造函数
var person = new Object();
person.age = 29;
  1. 使用对象字面量,是一种简写形式
var person = {
	age: 29
}
  • 推荐对象字面量语法,这种语法要求的代码量少,能够给人封装数据的感觉,也是向函数传递大量可选参数的首选方式。
  • 点表示法,方括号表示法 访问属性

Array类型

数组每一项可以保存任何类型的数据

  • 创建数组的基本方式
  1. 使用Array构造函数
var colors = new Array();
//可以省略new操作符
var colors = Array();
  1. 使用数组字面量表示法
var colors = ['red', 'blue'];
  • 通过方括号索引表示要访问的值。
  • 数组的length不是只读的,可以设置这个属性,从数组末尾移除项或添加新项。

检测数组

  • 确定某个对象是不是数组。
  • 使用instanceof操作符 value instanceof Array。
  • ES5新增了Array.isArray()方法,最终确定某个值是不是数组 Array.isArray(value)。兼容IE9+

转换方法

  1. toString()
  2. valueOf()
  3. toLocaleString()
  4. join()

栈方法

栈是一种LIFO(last-in-first-out 后进先出)的数据结构,最新添加的项最早被移除,栈中插入(推入)移除(弹出),只发生在一个位置,栈的顶部。

  • push()、pop()
  • push():可以接受任意数量的参数,逐个添加到数组的末尾,返回修改后数组的长度。
  • pop():从数组末尾移除最后一项,减少数组的length值,返回移除的项。

队列方法

队列数据结构规则是FIFO(first-in-first-out 先进先出)。

  • shift()、unshift()
  • shift():移除数组中的第一项并返回该项,将数组长度减1。
  • unshift():在数组前面添加任意个项并返回新数组的长度。

重排序方法

  • reverse()、sort()
  • reverse():反转数组项的顺序
  • sort():按照升序排列数组项,最小值在最前面,最大值在最后面。sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串。即便每一项都是数值,sort()比较的也是字符串。
  • sort()可以接受一个比较函数作为参数,以便指定哪个值位于哪个值的前面。比较函数接受两个参数:第一个位于前面,返回负数,两个参数相等,返回0,第二个位于前面,返回正数。

操作方法

  • concat() 基于当前数组 创建一个新数组
  • slice() 基于当前数组一个或多个项创建一个新数组。接受一个或两个参数,返回起始项和结束项之间的项,不包括结束项。不会影响原始数组。
  • splice()主要用途是向数组中部插入项,返回从原始数组删除的项,会改变原始数组
  1. 删除:可以删除任意数量的项,指定2个参数,删除第一项的位置和要删除的项数。例如splice(0, 2)会删除数组中的前两项。
  2. 插入:向指定位置插入任意数量的项,指定3个参数 (起始位置,要删除的项数, 插入的项),如果插入多个项,可以穿第4第5多个项,例如splice(2, 0, ‘red’);
  3. 替换:可以插入任意数量的项,同时删除任意数量的项。
方法 作用 是否改变原始数组
concat() 基于当前数组,创建一个新数组
slice() 基于当前数组一个或多个项创建一个新数组
splice() 向数组插入、删除、替换项

位置方法

  • indexOf()、lastIndexOf(),这两个方法都接受两个参数,要查找的项和起点位置的索引(可选)。indexOf()从数组的开头向后查找,lastIndexOf()从数组的末尾向前查找,没找到情况下返回-1,查找时使用全等操作符===
  • 兼容IE9+

迭代方法

  • 每个方法都接受两个参数 1. 每一项上运行的函数 2. 运行该函数的作用于对象(可选)
  • 第一个传入的函数,接受3个参数 1. 数组项的值 2. 该项的索引值 3. 数组本身
  • 不会影响原始数组
  • 兼容IE9+
序号 方法 作用
1 every() 如果每一项都返回true,则返回true
2 filter() 返回 该函数会返回true的项组成的数组
3 forEach() 无返回值
4 map() 返回每次函数调用的结果组成的数组
5 some() 如果任意一项返回true,则返回true

归并方法

归并数组的方法,都会迭代数组的所有项,然后构建一个最终返回的值。

  • reduce() 从数组第一项开始,逐个遍历到最后
  • reduceRight() 从数组最后一项开始,向前遍历到第一项。和reduce()只是顺序不同,其它完全相同。
  • 接受两个参数 1. 每一项调用的函数 2. 作为归并基础的初始值(可选)
  • 第一个函数接受4个参数 1. 前一个值、2. 当前值、 3. 项的索引、 4. 数组对象
  • 这个函数返回的任何值都会作为第一个参数自动传给下一项,第一次迭代发生在数组的第二项上。
  • 兼容IE9+

Date类型

  • 要创建一个日期对象,使用new操作符和Date构造函数即可。
  • 如果想根据特定的日期和时间创建日期对象,必须传入该日期的毫秒数。两个方法 1. Date.parse()和Date.UTC()。
  • Date.now()方法,表示调用这个方法时的日期和时间的毫秒数。
  • (new Date()).valueOf() 返回日期的毫秒数。

RegExp类型

正则表达式

  • 匹配模式支持下列3个标志
  1. g 表示全局(global)模式
  2. i 表示不区分大小写模式
  3. m 表示多行模式

Function类型

函数实际上是对象,每个函数都是Function类型的实例,与其他引用类型一样具有属性和方法。

  • 使用不带圆括号的函数名是访问函数指针,而非调用函数。

没有重载

  • 函数名只是指针,后面的同名函数覆盖了前面的函数。

函数声明与函数表达式

  • 解析器会率先读取函数声明,并使其在执行任何代码之前可用。解析器会通过一个名为函数声明提升的过程,读取并将行数声明添加到执行环境中。js引擎在第一遍会声明函数并将它们放到源代码树的顶部。
  • 函数表达式,必须等到解析器执行到它所在的代码行,才会真正被解释执行。

作为值的函数

函数可以作为值来使用。可以像传递参数一样把一个函数传递给另一个函数,还可以将一个函数作为另一个函数的结果返回。

函数内部属性

  • arguments 类数组对象,包含着传入函数中的所有参数。还有一个名叫callee的属性,是一个指针,指向拥有这个arguments对象的函数。arguments.callee 指向该函数
  • this 引用的是函数据以执行的环境对象。
  • this 引用的是对象,this指向的就是该对象。
  • 函数对象的属性caller,保存着调用当前函数的 函数的引用。
  • 定义阶乘函数一般都要用到递归算法。

函数属性和方法

函数是对象,因此函数也有属性和方法

  • 每个函数都包含两个属性 1. length 2. prototype
  • length:表示函数希望接收的参数的个数。
  • prototype: 保存所有实例方法。诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。prototype属性是不可枚举的,使用for-in无法发现。
  • 每个函数都包含两个非继承而来的方法 1. apply() 2. call(),这两个方法都是在特定的作用域中调用函数,等于设置函数体内this对象的值。
  • apply()接收两个参数 1. 运行函数的作用域 2. 参数数组(可以是Array实例,也可以是arguments对象)
  • call()与apply()作用相同,区别仅在于接收参数的方式不同,第二个参数必须逐个列举出来。
  • apply()和call()真正强大是能够扩充函数赖以运行的作用域。
  • 扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。
  • bind(),该方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。
属性
length
prototype
方法 传参
apply() 两个参数 1. 作用域 2. 参数(arguments对象或数组)
call() 两个参数 1. 作用域 2. 参数,需要展开
bind() 作用域

基本包装类型

特殊的引用类型 1. Boolean 2. Number 3. String

Number类型的方法

  1. toString()
  2. toFixed() 用于将数值格式化字符串的方法。
  3. toExponential() 返回以指数表示法表示的数值的字符串形式。接受一个参数,表示小数的位数。

String类型

  • charAt()和charCodeAt(),接受一个参数,基于0的字符位置
  • charAt 返回字符
  • charCodeAt 返回字符编码
  • 字符串操作方法
方法 作用
concat() 拼接字符串
slice() 基于字符串创建新字符串,两个参数 1:开始的位置 2. 结束的位置,不会修改原字符串
substr() 基于字符串创建新字符串,两个参数 1:开始的位置 2. 长度,不会修改原字符串
substring() 基于字符串创建新字符串,两个参数 1:开始的位置 2. 结束的位置,不会修改原字符串
  • 位置方法
  1. indexOf()
  2. lastIndexOf()
  • trim()方法 创建一个字符串的副本,删除前置及后缀的所有空个,然后返回结果。不影响原始字符串,兼容IE9+。
  • trimLeft() 删除字符串开头
  • trimRight() 删除字符串末尾
  • 字符串大小写转换方法
  1. toLowerCase()
  2. toLocalLowerCase()
  3. toUpperCase()
  4. toLocalUpperCase()
  • 字符串模式匹配方法
  1. match()
  2. search()
  3. replace
  4. split() 将一个字符串分割多个字符串,并将结果放在一个数组中
  5. localeCompare() 比较两个字符串
  6. fromCharCode() 接收一个或多个字符编码,将他们转换成一个字符串,与实例方法charCodeAt()执行的是相反的操作。

单体内置对象

不必显式地实例化内置对象,因为它们已经实例化了

Global对象

URI编码方法

  • 可以对URI 通用资源标识符进行编码,以便发送给浏览器。
  1. encodeURI() 不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井号
  2. encodeURIComponent() 会对它发现的任何非标准字符进行编码。一般使用这个方法比较多
  3. deCodeURI()
  4. deCodeURIComponent(),对字符进行解码

eval()

  • 只接受一个参数,既要执行的字符串。
  • 在eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在eval()执行的时候创建。
  • 使用时必须极为谨慎,特别是执行用户输入数据的情况下。防止代码注入。

Global对象的属性

window对象

在全局作用域中声明的所有变量和函数,都成了window对象的属性。

Math对象

  • Math对象的属性
  • min()和max()方法 获取一组数值中最小值和最大值。
  • 舍入方法 将小数值舍入为整数的几个方法
  1. Math.ceil()
  2. Math.floor()
  3. Math.round()
方法 作用 例子 结果
Math.ceil() 向上舍入 25.1 26
Math.floor() 向下舍入 25.9 25
Math.round() 四舍五入 25.5 26
  • Math.random()方法,返回大于等于0小于1的一个随机数
  • 其他方法 例如 Math.abs() 返回绝对值

六、面向对象的程序设计

通过类,可以创建任意多个具有相同属性和方法的对象。

理解对象

创建对象方式 1. 使用new 2. 使用对象字面量

属性类型

  • 只有内部才用的特性,描述了属性property的各种特征,定义这些特征是为了实现js引擎用的,因此js不能直接访问它们。
  • 为了表示特性是内部值,该规范把它们放在了两对方括号中,例如[[Enumerable]]

数据属性

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

序号 属性名 作用 默认
1 [[Configurable]] 能否通过delete删除属性从而重新定义属性,能否修改属性的特性 true
2 [[Enumerable]] 能否通过for-in循环遍历属性 true
3 [[Writable]] 能否修改属性的值 true
4 [[Value]] 包含这个属性的值 undefined
  • 要修改属性的默认特性,必须使用Object.defineProperty()方法。这个方法接受3个参数,
  1. 属性所在的对象
  2. 属性的名字
  3. 1个描述符对象
  • Object.defineProperty() 兼容IE9+(IE8部分实现)

访问器属性

访问器属性不包含数据值,包含getter、setter函数
在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值
在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据

  • 访问器属性有一下4个特性
名称 作用 默认值
[[Configurable]] 能否通过delete删除属性从而重新定义属性,能否修改属性的特性 true
[[Enumerable]] 能否通过for-in遍历该属性 true
[[Get]] 读取属性调取的函数 undefined
[[Set]] 写入属性时调用的函数 undefined
  • 要定义访问器属性,必须使用Object.defineProperty()。

定义多个属性

  • Object.defineProperties(),利用这个方法可以通过描述符一次定义多个属性。
  • 接受两个参数
  1. 要修改的对象
  2. 该参数是个对象,该参数的属性与第一个参数中要修改的属性一一对应。

读取属性的特性

  • Object.getOwnPropertyDescriptor(),可以取得给定属性的描述符
  • 接受两个参数
  1. 属性所在的对象
  2. 要读取描述符的属性名称

创建对象

1.工厂模式

2.构造函数模式(可以作为普通函数调用)

3.原型模式

  • 只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。
  • 默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
  • Person.prototype.constructor == Person
  • 通过这个构造函数,可以继续为原型对象添加其他属性和方法。
  • 创建自定义构造函数之后,其原型对象默认只会取的constructor属性,至于其他方法,都是从Object继承而来的。
  • 当调用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。该指针是一个属性__proto__或者[[Prototype]] 。这个连接存在于实例和构造函数的原型对象之间,而不是实例与构造函数之间。
  • 虽然实例都不包含属性和方法,但是却可以调用构造函数的属性,这是通过查找对象属性过程来实现的。
  • 虽然无法访问[[Prototype]],可以通过调用isPrototypeOf()确定对象之间是否存在这种关系
  • Person.prototype.isPrototypeOf(person1),返回true。
  • ES5新增一个方法 Object.getPrototypeOf(),返回[[Prototype]]的值。
  • Object.getPrototypeOf(person1),返回Person.prototype。兼容IE9+
  • 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是该属性。搜索首先从对象实例本身开始,如果实例本身具有该属性,就返回值,如果没有,继续搜索指针指向的原型对象,在原型对象中查找该属性。
  • 实例不能重写原型中的值。当为实例添加一个属性时,该属性会屏蔽原型对象中保存的同名属性。添加这个属性,会阻止我们访问原型对象中的那个属性,但不会修改那个属性。
  • hasOwnProperty(),检测一个属性是实例属性还是原型属性。
  • person1.hasOwnProperty(‘name’); 返回true

遍历对象方法

序号 方法 作用 兼容
1 in 遍历原型属性及实例属性(可枚举的) IE8+
2 Object.keys() 遍历自身可枚举属性 IE9+
3 Object.getOwnPropertyNames() 遍历实例属性,无论是否可枚举 IE9+
  • in 操作符使用方式
  1. 单独使用
    对象能够访问到给定属性时返回true,无论该属性存在于实例还是原型中。
    ’name’ in person1; 返回true
  2. for-in 循环
    返回的事所有能通过对象访问的,可枚举的(enumerated)属性,既包括实例中的属性,也包括原型中的属性。屏蔽了原型中不可枚举的属性的实例属性也会在for-in循环中返回。
  • 要取的对象上所有可枚举的实例属性,可以使用Object.keys()方法,这个方法接受一个对象作为参数,返回1个包含所有可枚举属性的数组。
  • 如果你要得到所有实例属性,无论是否可以枚举,可以使用Object.getOwnPropertyNames()。兼容IE9+
  • 原型的动态性,重写原型,相当于切断了构造函数与最初原型对象之间的联系。
  • 实例中的指针仅仅指向原型对象,而不是构造函数本身。
  • 原生对象的原型:原型模式不仅体现在创建自定义类型方面,所有原生引用类型,都采用这种模式创建的。都是在其构造函数的原型上定义了方法。Array.prototype中可以找到sort()方法。
  • 可以修改原生对象的原型,随时添加方法。(不建议这么做,容易导致意外错误)。
  • 原型对象的问题:
  1. 所有实例默认情况下取得相同的属性值,带来不便
  2. 对于包含引用类型的属性,问题很大,所有实例共享,导致改变一个实例的属性值,所有实例的属性值都跟着改变。

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

最常见的方式就是使用组合模式,使用最广泛、认同度最高的一种方法,定义引用类型的一种默认模式。
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性副本,同时又共享对方法的引用,最大限度节省了内存。还可以向构造函数传递参数。

动态原型模式

  • 不能使用对象字面量重写原型

寄生构造函数模式

  • 不建议使用这种

稳妥构造函数模式

几种方式的特点

序号 方式 特点 缺点
1 工厂模式 抽象了创建具体对象的过程 没有解决对象识别的问题(即怎样知道一个对象的类型)
2 构造函数模式 构造函数始终以一个大写字母开头,为了区别其他非构造函数 存在冗余代码过多问题
3 原型模式 存在所有实例共用同一个方法问题,导致没有单独的属性
4 组合构造函数与原型模式 暂时没有
5 动态原型模式
6 寄生构造函数模式
7 稳妥构造函数模式

继承

继承主要是依靠原型链来实现的

原型链

原型链作为实现继承的主要方法。
基本思想是利用原型让一个引用类型 继承 另一个引用类型的属性和方法。

  • 让原型对象等于另一个类型的实例。此时,该原型对象将包含一个指向另一个原型的指针,另一个原型中也包含着一个指向另一个构造函数的指针。如此层层递进,就构成了实例与原型的链条,这就是所谓原型链的基本概念。
  • 通过实现原型链,本质上扩展了原型搜索机制。
  • 以读取模式访问一个实例属性时,首先会在实例中搜索该数据,如果没有找到该属性,会继续搜索实例的原型。

默认原型

  • 所有函数的默认原型都是Object的实例,隐藏默认原型都包含一个内部指针,指向Object.prototype。

原型与实例的关系

  • instanceof
  • isPrototypeOf();

谨慎地定义方法

  • 给原型添加方法的代码,一定要放在替换原型的语句之后。
function SuperType(){
	this.property = true;
}
SuperType.prototype.getSuperValue = function(){
	return this.property;
}
function SubType(){
	this.subproperty = false;
}
//继承SuperType
SubType.prototype = new SuperType();
//添加xinfangf
SubType.prototype.getSubValue = function(){
	return this.subproperty;
}
//重写超类型中的方法
SubType.prototype.getSuperValue = function(){
	return false;
}
var instance = new SubType();
instance.getSuperValue(); // false
  • 通过原型链实现继承时,不能使用对象字面量创建原型方法,这样会重写原型链。

原型链的问题

  • 很少单独使用原型链
  1. 主要问题是来自于包含引用类型值的原型。
function SuperType(){
	this.colors = ["red", "blue", "green"];
}
function SubType(){
	
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
instance1.colors // ['red', 'blue', 'green', 'black']
var instance2 =new SubType();
instance2.colors // ['red', 'blue', 'green', 'black']
  • 所有的实例都会共享这个colors属性(引用类型)
  1. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数

  • 很少单独使用

基本思想:在子类型构造函数的内部 调用 超类型构造函数
函数是在特定环境中执行代码的对象,因此可以通过使用apply()和call()方法也可以在新创建的对象上执行构造函数

function SuperType(){
	this.colors = ['red', 'blue', 'green'];
}
function SubType(){
	//继承了SuperType
	SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
instance1.colors // ['red', 'blue', 'green', 'black'];
var instance2 = new SubType();
instance2.colors // ['red', 'blue', 'green'];
  • 实际上是在新创建的SubType实例的环境下调用了SuperType构造函数。
  • 会在新SubType对象上执SuperType()函数中定义的所有对象初始化代码。
  • SubType的每个实例都会具有自己的colors属性的副本了。

借用构造函数可以在子类型构造函数中向超类型构造函数传递参数

  • 存在问题:方法都在构造函数中定义,函数无法复用。

组合继承

伪经典继承,指的是将原型链和借用构造函数的技术组合到一块。
基本思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。
即通过在原型上定义方法 实现了函数复用,又能保证每个实例都有自己的属性。

function SuperType(name){
	this.name = name;
	this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function(){
	alert(this.name);
}

function SubType(name, age){
	//继承属性
	SuperType.call(this.name);
	this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
	alert(this.age);
}

var instance1 = new SubType('nicholas', 29);
instance1.colors.push('black');
instance1.colors // ['red', 'blue', 'green', 'black'];
instance1.sayAge() // 29
instance1.sayName() // 'nicholas'

var instance2 = new SubType('greg', 25);
instance2.colors // ['red', 'blue', 'green'];
instance2.sayAge() // 25
instance2.sayName() // ''greg

避免了原型链和借用构造函数的缺陷,融合了有点,成为js中最常用的继承模式。

原型式继承

  • Object.create() 规范化了原型式继承。接收两个参数
  1. 用作新对象原型的对象
  2. 为新对象定义额外属性的对象(可选)
  • 兼容IE9+
  • 就像使用原型模式一样,包含引用类型的属性,使用都会共享相应的值。

寄生式继承

是与原型式继承紧密相关的一种思路
思路:创建一个仅用于封装继承过程的函数,

  • 无法做到函数复用而降低效率

寄生组合式继承

七、函数表达式

定义函数两种方式

  1. 函数声明
function name(){

}
  • 函数声明的重要特征:函数声明提升,执行代码前会先读取函数声明。可以把函数声明放在调用它语句的后面。
  1. 函数表达式
var name = function(){

}
  • 这种形式好像是常规的变量赋值语句,创建一个函数,赋值给变量。这种情况下叫匿名函数,因为function关键字后面没有标识符。匿名函数的name属性时空字符串
  • 函数表达式不存在提升
  • 把函数当成值来使用,都可以使用匿名函数。

递归

递归函数:一个函数通过名字调用自身的情况

  • arguments.callee,指向正在执行的函数的指针,可以用它来实现对函数的递归调用。无论怎样调用都不会出问题。

闭包

闭包:闭包是一个函数,有权访问另一个函数作用域中的变量

  • 创建闭包的常见方式,就是在一个函数内部创建另一个函数
  • 内部函数的作用域链包含外部函数的作用域
  • 作用域链: 当某个函数被调用时,会创建一个执行环境及相应的作用域链。
  • 在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数活动对象处于第三位,直至作用域链终点的全局执行环节。
  • 当函数执行完毕,局部活动对象就会被销毁,内存中仅保存全局作用域。

闭包会携带包含它的函数的作用域,会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,慎重使用闭包。

闭包和变量

关于this对象

在闭包中使用this对象也会导致一些问题

  • this对象是在运行时基于函数的执行环境绑定的:
  1. 全局中:this等于window
  2. 函数作为某个对象的方法调用,this等于这个对象
  3. 匿名函数的执行环境具有全局性,this对象指向window

内存泄漏

如果闭包的作用域链中保存着一个HTML元素,那么该元素将无法被销毁。

模仿块级作用域

匿名函数可以模仿块级作用域并避免这个问题
块级作用域(私有作用域)语法如下:

(function(){
 //块级作用域
})();
//相当于以下代码
var fun = function(){

}
fun();
  • 尽量少向全局作用域中添加变量和函数
  • 在一个多人开发的大型应用程序中,过多的全局变量和函数容易导致命名冲突。
  • 创建私有作用域,开发人员可以使用自己的变量,不必担心搞乱全局作用域。

私有变量

js没有私有成员的概念,所有对象属性都是公有的。

静态私有变量

模块模式

模块模式是为单例创建私有变量和特权方法。

增加的模块模式

八、BOM

window对象,BOM的核心
控制窗口、框架和弹出窗口
利用location对象中的页面信息
使用navigator对象了解浏览器

window对象

BOM的核心对象是window,表示浏览器的一个实例。
window对象有着双重角色,1. js访问浏览器窗口的一个接口 2. ECMAScript规定的Global对象

全局作用域

由于window对象同时扮演着ECMAScript中Global对象的角色,因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法。

  • 全局变量不能通过delete删除,在window对象上定义的属性可以。
  • 很多全局js对象(如location和navigator)都是window对象的属性。

窗口关系及框架

  • top对象始终指向最外层框架,也就是浏览器窗口。
  • parent对象始终指向当前框架的直接上层框架。
  • 某些情况下,parent有可能等于top。
  • 没有框架的情况下,parent一定等于top(此时它们都等于window)。
  • self,始终指向window。self和window对象可以互换使用。
  • 所有这些对象都是window对象的属性,都可以通过window.parent、window.top等形式来访问。

窗口位置

用来确定和修改window对象位置的属性和方法有很多。

  • 使用moveTo()、moveBy()有可能将窗口精确地移动到一个新位置。

窗口大小

  • 视口(viewport)大小
  1. innerWidth
  2. innerHeight
  3. outerWidth
  4. outerHeight
  5. document.documentElement.clientWidth
  6. document.documentElement.clientHeight
  7. document.body.clientWidth
  8. document.body.clientHeight

导航和打开窗口

  • window.open(); 可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。接受4个参数。通常只需第一个参数。
  1. 要加载的URL
  2. 窗口目标
  3. 一个特性字符串
  4. 一个表示新页面是否取代浏览器历史记录中当前页面的布尔值。
  5. window.open()返回null,该弹出窗口被屏蔽了。
  6. 如果是浏览器扩展或其他程序阻止的弹出窗口,window.open()会抛出一个错误。

setTimeout()、clearTimeout()、setInterval()、clearInterval()

  • js是一个单线程序的解释器,一定时间内只能执行一段代码。为了控制要执行的代码,就有一个js任务队列。
  • setTimeout()第二个参数告诉js再过多长时间把当前任务添加到任务队列中。
  • 最好使用setTimeout(),不要用setInterval()。

系统对话框
样式与浏览器网页没有关系,外观由操作系统或浏览器设置决定,而不是css决定的。
对话框都是同步和模态,显示对话框时,代码会停止执行,关掉对话框后,代码恢复执行。

  1. alert();
  2. confirm();
  3. prompt();

还有两个可以通过js打开的对话框,1. 查找 2. 打印
这两个对话框都是异步显示,能给将控制权立即交还给脚本。

  1. window.print();
  2. window.find();

location对象

提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能。

  • 属性
  1. hash
  2. host
  3. hostname
  4. href
  5. pathname
  6. port
  7. protocol
  8. search
  • 位置操作
  • location.assign(‘url’);
  • window.location = ‘url’;
  • location.href = ‘url’;
  • location.replace(‘url’);
  • location.reload(); //重新加载(有可能从缓存中加载)
  • location.reload(true); //重新加载,从服务器重新加载
  • 每次修改location属性(hash除外),页面都会以新URL重新加载。

navigator对象

识别客户端浏览器的事实标准。

  • 检测浏览器插件
  • navigator.plugins

screen 对象

history对象

保存着用户上网的历史记录,从窗口被打开的那一刻算起。是window对象的属性。

  • history.go();
  • history.back();
  • history.forward();

九、客户端检测

你可能感兴趣的:(web前端,javascript)