三、基本概念
基本数据类型
typeof 操作符 返回字符串 ‘undefined’ ‘boolean’ ‘string’ ‘number’ ‘object’ ‘function’
Undefined类型
Null类型
Boolean类型
Number类型
数值转换 把非数值转换为数值
函数 |
作用 |
Number() |
转换任何数据类型为数值 |
parseInt() |
转换字符串为数值 |
parseFloat() |
转换字符串为数值 |
- Number()在转换字符串时比较复杂且不够合理,更常用的是parseInt();
- parseInt(),第二个参数,转换时使用的基数(既多少进制)例如:parseInt(“0xAF”, 16); 指定基数会影响到转换的输出结果。
String类型
双引号和单引号无区别
转换为字符串
方法 |
作用 |
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类型的实例。
- 使用new操作符后跟Object构造函数
var person = new Object();
person.age = 29;
- 使用对象字面量,是一种简写形式
var person = {
age: 29
}
- 推荐对象字面量语法,这种语法要求的代码量少,能够给人封装数据的感觉,也是向函数传递大量可选参数的首选方式。
- 点表示法,方括号表示法 访问属性
Array类型
数组每一项可以保存任何类型的数据
- 使用Array构造函数
var colors = new Array();
var colors = Array();
- 使用数组字面量表示法
var colors = ['red', 'blue'];
- 通过方括号索引表示要访问的值。
- 数组的length不是只读的,可以设置这个属性,从数组末尾移除项或添加新项。
检测数组
- 确定某个对象是不是数组。
- 使用instanceof操作符 value instanceof Array。
- ES5新增了Array.isArray()方法,最终确定某个值是不是数组 Array.isArray(value)。兼容IE9+
转换方法
- toString()
- valueOf()
- toLocaleString()
- 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()主要用途是向数组中部插入项,返回从原始数组删除的项,会改变原始数组
- 删除:可以删除任意数量的项,指定2个参数,删除第一项的位置和要删除的项数。例如splice(0, 2)会删除数组中的前两项。
- 插入:向指定位置插入任意数量的项,指定3个参数 (起始位置,要删除的项数, 插入的项),如果插入多个项,可以穿第4第5多个项,例如splice(2, 0, ‘red’);
- 替换:可以插入任意数量的项,同时删除任意数量的项。
方法 |
作用 |
是否改变原始数组 |
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类型
正则表达式
- g 表示全局(global)模式
- i 表示不区分大小写模式
- 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()函数的值。
方法 |
传参 |
apply() |
两个参数 1. 作用域 2. 参数(arguments对象或数组) |
call() |
两个参数 1. 作用域 2. 参数,需要展开 |
bind() |
作用域 |
基本包装类型
特殊的引用类型 1. Boolean 2. Number 3. String
Number类型的方法
- toString()
- toFixed() 用于将数值格式化字符串的方法。
- toExponential() 返回以指数表示法表示的数值的字符串形式。接受一个参数,表示小数的位数。
String类型
- charAt()和charCodeAt(),接受一个参数,基于0的字符位置
- charAt 返回字符
- charCodeAt 返回字符编码
- 字符串操作方法
方法 |
作用 |
concat() |
拼接字符串 |
slice() |
基于字符串创建新字符串,两个参数 1:开始的位置 2. 结束的位置,不会修改原字符串 |
substr() |
基于字符串创建新字符串,两个参数 1:开始的位置 2. 长度,不会修改原字符串 |
substring() |
基于字符串创建新字符串,两个参数 1:开始的位置 2. 结束的位置,不会修改原字符串 |
- indexOf()
- lastIndexOf()
- trim()方法 创建一个字符串的副本,删除前置及后缀的所有空个,然后返回结果。不影响原始字符串,兼容IE9+。
- trimLeft() 删除字符串开头
- trimRight() 删除字符串末尾
- 字符串大小写转换方法
- toLowerCase()
- toLocalLowerCase()
- toUpperCase()
- toLocalUpperCase()
- match()
- search()
- replace
- split() 将一个字符串分割多个字符串,并将结果放在一个数组中
- localeCompare() 比较两个字符串
- fromCharCode() 接收一个或多个字符编码,将他们转换成一个字符串,与实例方法charCodeAt()执行的是相反的操作。
单体内置对象
不必显式地实例化内置对象,因为它们已经实例化了
Global对象
URI编码方法
- 可以对URI 通用资源标识符进行编码,以便发送给浏览器。
- encodeURI() 不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井号
- encodeURIComponent() 会对它发现的任何非标准字符进行编码。一般使用这个方法比较多
- deCodeURI()
- deCodeURIComponent(),对字符进行解码
eval()
- 只接受一个参数,既要执行的字符串。
- 在eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在eval()执行的时候创建。
- 使用时必须极为谨慎,特别是执行用户输入数据的情况下。防止代码注入。
Global对象的属性
window对象
在全局作用域中声明的所有变量和函数,都成了window对象的属性。
Math对象
- Math对象的属性
- min()和max()方法 获取一组数值中最小值和最大值。
- 舍入方法 将小数值舍入为整数的几个方法
- Math.ceil()
- Math.floor()
- 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个描述符对象
- Object.defineProperty() 兼容IE9+(IE8部分实现)
访问器属性
访问器属性不包含数据值,包含getter、setter函数
在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值
在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据
名称 |
作用 |
默认值 |
[[Configurable]] |
能否通过delete删除属性从而重新定义属性,能否修改属性的特性 |
true |
[[Enumerable]] |
能否通过for-in遍历该属性 |
true |
[[Get]] |
读取属性调取的函数 |
undefined |
[[Set]] |
写入属性时调用的函数 |
undefined |
- 要定义访问器属性,必须使用Object.defineProperty()。
定义多个属性
- Object.defineProperties(),利用这个方法可以通过描述符一次定义多个属性。
- 接受两个参数
- 要修改的对象
- 该参数是个对象,该参数的属性与第一个参数中要修改的属性一一对应。
读取属性的特性
- Object.getOwnPropertyDescriptor(),可以取得给定属性的描述符
- 接受两个参数
- 属性所在的对象
- 要读取描述符的属性名称
创建对象
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+ |
- 单独使用
对象能够访问到给定属性时返回true,无论该属性存在于实例还是原型中。
’name’ in person1; 返回true
- for-in 循环
返回的事所有能通过对象访问的,可枚举的(enumerated)属性,既包括实例中的属性,也包括原型中的属性。屏蔽了原型中不可枚举的属性的实例属性也会在for-in循环中返回。
- 要取的对象上所有可枚举的实例属性,可以使用Object.keys()方法,这个方法接受一个对象作为参数,返回1个包含所有可枚举属性的数组。
- 如果你要得到所有实例属性,无论是否可以枚举,可以使用Object.getOwnPropertyNames()。兼容IE9+
- 原型的动态性,重写原型,相当于切断了构造函数与最初原型对象之间的联系。
- 实例中的指针仅仅指向原型对象,而不是构造函数本身。
- 原生对象的原型:原型模式不仅体现在创建自定义类型方面,所有原生引用类型,都采用这种模式创建的。都是在其构造函数的原型上定义了方法。Array.prototype中可以找到sort()方法。
- 可以修改原生对象的原型,随时添加方法。(不建议这么做,容易导致意外错误)。
- 原型对象的问题:
- 所有实例默认情况下取得相同的属性值,带来不便
- 对于包含引用类型的属性,问题很大,所有实例共享,导致改变一个实例的属性值,所有实例的属性值都跟着改变。
组合使用构造函数模式和原型模式
最常见的方式就是使用组合模式,使用最广泛、认同度最高的一种方法,定义引用类型的一种默认模式。
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性副本,同时又共享对方法的引用,最大限度节省了内存。还可以向构造函数传递参数。
动态原型模式
寄生构造函数模式
稳妥构造函数模式
几种方式的特点
序号 |
方式 |
特点 |
缺点 |
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;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
SubType.prototype.getSuperValue = function(){
return false;
}
var instance = new SubType();
instance.getSuperValue();
- 通过原型链实现继承时,不能使用对象字面量创建原型方法,这样会重写原型链。
原型链的问题
- 主要问题是来自于包含引用类型值的原型。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
instance1.colors
var instance2 =new SubType();
instance2.colors
- 所有的实例都会共享这个colors属性(引用类型)
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数
基本思想:在子类型构造函数的内部 调用 超类型构造函数
函数是在特定环境中执行代码的对象,因此可以通过使用apply()和call()方法也可以在新创建的对象上执行构造函数
function SuperType(){
this.colors = ['red', 'blue', 'green'];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
instance1.colors
var instance2 = new SubType();
instance2.colors
- 实际上是在新创建的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
instance1.sayAge()
instance1.sayName()
var instance2 = new SubType('greg', 25);
instance2.colors
instance2.sayAge()
instance2.sayName()
避免了原型链和借用构造函数的缺陷,融合了有点,成为js中最常用的继承模式。
原型式继承
- Object.create() 规范化了原型式继承。接收两个参数
- 用作新对象原型的对象
- 为新对象定义额外属性的对象(可选)
- 兼容IE9+
- 就像使用原型模式一样,包含引用类型的属性,使用都会共享相应的值。
寄生式继承
是与原型式继承紧密相关的一种思路
思路:创建一个仅用于封装继承过程的函数,
寄生组合式继承
七、函数表达式
定义函数两种方式
- 函数声明
function name(){
}
- 函数声明的重要特征:函数声明提升,执行代码前会先读取函数声明。可以把函数声明放在调用它语句的后面。
- 函数表达式
var name = function(){
}
- 这种形式好像是常规的变量赋值语句,创建一个函数,赋值给变量。这种情况下叫匿名函数,因为function关键字后面没有标识符。匿名函数的name属性时空字符串
- 函数表达式不存在提升
- 把函数当成值来使用,都可以使用匿名函数。
递归
递归函数:一个函数通过名字调用自身的情况
- arguments.callee,指向正在执行的函数的指针,可以用它来实现对函数的递归调用。无论怎样调用都不会出问题。
闭包
闭包:闭包是一个函数,有权访问另一个函数作用域中的变量
- 创建闭包的常见方式,就是在一个函数内部创建另一个函数
- 内部函数的作用域链包含外部函数的作用域
- 作用域链: 当某个函数被调用时,会创建一个执行环境及相应的作用域链。
- 在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数活动对象处于第三位,直至作用域链终点的全局执行环节。
- 当函数执行完毕,局部活动对象就会被销毁,内存中仅保存全局作用域。
闭包会携带包含它的函数的作用域,会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,慎重使用闭包。
闭包和变量
关于this对象
在闭包中使用this对象也会导致一些问题
- 全局中:this等于window
- 函数作为某个对象的方法调用,this等于这个对象
- 匿名函数的执行环境具有全局性,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()有可能将窗口精确地移动到一个新位置。
窗口大小
- innerWidth
- innerHeight
- outerWidth
- outerHeight
- document.documentElement.clientWidth
- document.documentElement.clientHeight
- document.body.clientWidth
- document.body.clientHeight
导航和打开窗口
- window.open(); 可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。接受4个参数。通常只需第一个参数。
- 要加载的URL
- 窗口目标
- 一个特性字符串
- 一个表示新页面是否取代浏览器历史记录中当前页面的布尔值。
- window.open()返回null,该弹出窗口被屏蔽了。
- 如果是浏览器扩展或其他程序阻止的弹出窗口,window.open()会抛出一个错误。
setTimeout()、clearTimeout()、setInterval()、clearInterval()
- js是一个单线程序的解释器,一定时间内只能执行一段代码。为了控制要执行的代码,就有一个js任务队列。
- setTimeout()第二个参数告诉js再过多长时间把当前任务添加到任务队列中。
- 最好使用setTimeout(),不要用setInterval()。
系统对话框
样式与浏览器网页没有关系,外观由操作系统或浏览器设置决定,而不是css决定的。
对话框都是同步和模态,显示对话框时,代码会停止执行,关掉对话框后,代码恢复执行。
- alert();
- confirm();
- prompt();
还有两个可以通过js打开的对话框,1. 查找 2. 打印
这两个对话框都是异步显示,能给将控制权立即交还给脚本。
- window.print();
- window.find();
location对象
提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能。
- hash
- host
- hostname
- href
- pathname
- port
- protocol
- 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();
九、客户端检测