面向对象的程序设计
一、理解对象
创建自定义对象的最简单方式就是创建一个object实例,然后在为他添加属性和方法
1、属性类型
ECMAScript 中有两种属性:数据属性和访问器属性。
数据属性:包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]]:表示能否通过for-in循环返回属性
[[writable]]:表示能否修改属性的值
[[Value]]:包含这个属性的数据值,读取属性值的时候,丛这个位置读;写入属性值的时候把新值保存在这个位置。
要修改属性的默认的特性,必须使用 object.defineProperty() 这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:configurable、Enumerable、writable、value。设置其中的一个或多个值,可以修改对应的特性値。
可以多次调用 object.defineProperty()方法修改同一个属性值,实在把configurable特性设置为false之后就有限制了。
访问器属性:访问器属性不包含数值;它们包含一对儿getter和setter函数;在读取访问器属性是,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数饼传入新值,这个函数负责决定如何处理数据。访问器属性有4个特性
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]]:表示能否通过for-in循环返回属性
[[Get]]:在读取属性时调用的函数
[[set]]:在写入属性时调用的函数
访问器属性不能直接定义,必须使用object.defineProperty()来定义
定义多个属性
利用object.defineProperties()方法可以通过描述符一次性定义多个属性,这个方法接收两个对象参数:第一个对象是要参加和修改其属性的对象,第二个对象的属性与第一个对象中药添加或修改的属性一一对应。
读取属性的特性
object.getOwnPropertyDescriptro()方法可以取得给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取描述符的属性名称,返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable、enumerable、get、和set ;如果是数值属性,这个对象的属性有configurable、enumerable、writable、和value.
二、创建对象
1、工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的工程。用函数来封装以特定接口创建对象的细节。
函数createPerson() 能够根据接收的参数来构建一个包含所有必要信息的person对象,可以无数次调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。
2、构造函数模式
ECMAScript中的构造函数可用来创建特定类型的对象。像object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
构造函数命名 始终都应该一个大写字母开头,而非构造函数则应该以一个小写字母开头。因为构造函数本身也是函数,只不过可以用来创建对象而已。
调用构造函数的4个步骤
1)创建一个新对象;
2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3)执行构造函数中的代码(为这个新对象添加属性)
4)返回新对象
将构造函数当做函数
构造函数与其他函数的唯一区别,就在于调用他们的方式不同,不过构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以作为构造函数,而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。
3、原型模式
我们创建的每个函数都一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以有特定类型的所有实例共享他属性和方法。如果按照字面意思来理解,那么prototype就是通过调用工造函数而穿件的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换计划说,不必再构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
理解原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性。这个属性是一个指向prototype属性所在的函数指针。
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,不过用delete操作符则可以完全删除实例属性,从而让我们能够重新的访问原型中的属性
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。只在属性存在于实例中才会返回true
in操作符只要通过对象能访问到属性就返回true
in 返回true hasOwnProperty()返回false 就可以确定属性是原型中的属性
4、组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
5、动态原型模式
动态原型模式它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。
6、寄生构造函数模式
这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后在返回新创建的对象。
关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。
7、稳妥构造函数模式
稳妥对象,指的是没有公共属性,而且其方法也不引用this对象
稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this,二是不适用new操作符调用构造函数。
三.继承
1、原型链
原型链作为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
2、借用构造函数
借用构造函数,即在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象。因此通过使用apply()和call()方法也可以在新创建的对象上执行构造函数。
传递参数:可以在子类型构造函数中像超类型构造函数传递参数。
3、组合继承
组合继承,有时候也叫作伪经典继承,值得是将原型链和兼用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。
instanceof 和isprototypeOf()也能够用于识别基于组合继承创建的对象。
4、原型式继承
这种方法并没有使用严格意义上的构造函数,他的想法是借助原型可以基于已有的对象创建新对象,同事还不必因此创建自定义类型。
object.create()方法规范化了原型式继承。这个方法接收两个参数,一个用做新对象原型的对象和一个胃新对象定义额外属性的对象。
5、寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路。与寄生构造函数和工厂模式类似。即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象。
函数表达式
定义函数的方式有两种
一种是函数声明:首先是function关键字,然后是函数名字。 关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明,这就意味着可以把函数声明放在调用它的语句后面。
function functionName(arg0, arg1, arg2){
//函数体
}
另一种就是函数表达式:有几种不同的语法形式
var functionName = function(arg0, arg1, arg1 ){
//函数体
}
这种形式看起来好像是常规变量赋值语句,即创建一个函数并将它赋值给变量functionName。这种情况下创建的函数叫做匿名函数。匿名函数的name属性是空字符串。
函数表达式与其他表达式一样,在使用前必须先赋值。
1、递归
递归函数是在一个函数通过名字调用自身的情况下构成的。
arguments.callee 是一个指向正在执行的函数的指针。
2、闭包
闭包是指有权访问另一个函数作用中的变量函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数。
闭包与变量
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量最后一个值,别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。
关于this对象
在闭包中使用this对象也可能会导致一些问题,我们知道,this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
3、模仿块级作用域
js从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示
(function(){
//这里是块级作用域
})
调用函数的方式是在函数名称后面添加一对圆括号。而函数声明后边不可以放圆括号,然而,函数表达式的后面可以跟圆括号,要将函数声明转换成函数表达式。
4、私有变量
我们有权访问私有变量和私有函数的方法称为特权方法。 用特权方法 publicMethod()来访问。