6. javacript高级程序设计-面向对象设计
1. 面向对象设计
1.1 理解对象
1.1.1 属性类型
(1). 数据属性:相当于对象的字段,包含一个数据值的位置,在这个位置可以读取和写入值。数据属性中有4个描述其行为的特性:
l [[Configurable]]:表示能否通过delete删除属性从而重新定义属性
l [[Enumerable]]:表示是否通过for-in循环返回属性
l [[Writable]]:表示能否修改属性的值
l [[Value]]:包含这个属性的数据值
要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。
(2). 访问器属性:相当于对象的属性,访问器属性不包含数据值,它们包含一对儿getter和setter函数,访问器属性有如下4个属性:
l [[Configurable]]:表示能否通过delete删除属性从而重新定义属性
l [[Enumerable]]:表示是否通过for-in循环返回属性
l [[Get]]:在读取属性时调用的函数
l [[Set]]:在写入属性时调用的函数
访问器属性不能直接定义,只能使用Object.defineProperty()来定义
1.1.2 定义多个属性
由于对象定义多个属性的可能性很大,ECMAScript5定义了一个Object.defineProperties()方法,利用该方法可以通过描述符一次性定义多个属性
1.1.3 读取属性的特性
使用ECMAScript5的Object.getOwnPropertyDescriptor()的方法,可以取得给定属性的描述符。如果是数据属性,这个对象的属性有configurable,enumerable,writable和value;如果是访问器属性,这个对象的属性有configurable,enumerable,get和set;
1.2 创建对象
1.2.1 工厂模式
工厂模式是软件工厂领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程,如下列所示:
function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); } return o; } var person1 = createPerson("chuck", 29, "softer"); |
工厂模式虽然解决了创建了多个相似对象的问题,但却没有解决对象识别的问题 |
1.2.2 构造函数模式
可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); } } var person2 = new Person("chuck",27,"tester"); alert(person2.constructor == person) //true alert(person2 instanceof Person) //true |
要创建Person的新实例,必须使用new操作符,以这种方式调用构造函数会经历以下4个步骤:
(1). 创建一个新对象
(2). 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
(3). 执行构造函数中的代码(为这个新对象添加属性)
(4). 返回新对象
对象的constructor属性最初是用来标识对象类型的,使用构造函数的问题就是每个方法都要在每个实例上面实现一遍。
1.2.3 原型模式
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
1.2.3.1 理解原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype的属性,这个函数指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。Person.prototype.constructor 指向Person.
1.2.3.2 原型与in操作符
有两种方式使用in操作符,单独使用和for-in循环使用
单独使用,in操作符会在通过对对象能够访问指定属性时返回true,无论属性存在实例还是原型中
‘name’ in person1
for-in循环使用,返回的是所有能够通过对象访问的、可枚举的属性,无论属性存在实例还是原型中。
1.2.3.3 更简单的原型语法
针对每次添加原型方法和属性都要写一遍Person.prototype,更常见的做法是直接给原型对象赋值一个对象:
function Person() { } Person.prototype = { name: "chuck", sayName: function() { alert(this.name); } } |
上诉代码存在一个弊端,constructor属性不再指向Person,可以收到赋值给constructor
function Person() { } Person.prototype = { constructor:Person, name: "chuck", sayName: function() { alert(this.name); } } |
1.2.3.4 原型的动态性
原型中查找值是一次搜索,因此我们对原型对象做的任何修改能够立即从实例中反映出来
function Person() { } var friend = new Person(); Person.prototype.sayName=function(){ alert("chuck"); } friend.sayName(); // chuck |
但是对于重写原型对象的赋值,会切断现有原型与已经存在的对象实例之间的联系
function Person() { } var friend = new Person(); Person.prototype = { name: "chuck", sayName: function() { alert(this.name); } } friend.sayName(); // error |
1.2.3.5 原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。
1.2.3.6 原型对象的问题
原型模式的最大缺点就是所有的原型对象的属性是被所有实例共享的,修改某一实例的属性会影响到其他实例的属性值。
1.2.4 组合模式
创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例类型,而原型模式用于定义方法和属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对对象的引用,最大限度的节省了内存。这是最常用的创建对象的方式。例如:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; } Person.prototype = { constructor: Person, sayName: function() { alert(this.name); } } var person1 = createPerson("chuck", 29, "softer"); var person2 = new Person("chuck", 27, "tester"); alert(person1.name == person2.name) //false alert(person1.sayName == person2.sayName) //true |
1.2.5 动态原型模式
动态原型模式把所有的信息都封装在构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。
例如:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); } } } |
1.2.6 寄生构造函数模式
使用寄生构造函数的方式和工厂构造函数基本一致,只是调用的方式有所不同。构造函数在不返回值的情况下,默认会返回新构造函数,而通过在构造函数的末尾添加一个return语句,可以重写构造函数的返回值。返回的对象与构造函数或者与构造函数的原型属性之间没有关系。
function Person(name, age) { var obj = new Object(); obj.name = name; obj.age = age; return obj; } var friend = new Person("chuck", 30); |
1.2.7 稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this对象。例如friend对象只有sayName方法可以使用。
function Person(name, age) { var obj = new Object(); //可以在这里定义私有变量和类型 //添加方法 obj.sayName = function(){ alert(name) } return obj; } var friend = new Person("chuck", 30); friend.sayName(); |
1.3 继承
1.3.1 原型链
原型链为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
1.3.1.1 别忘记默认的原型
所有引用对象都默认继承了Object,这个继承也是通过原型链实现的。
1.3.1.2 确定原型和实例的关系
可以通过两种方式来确定原型和实例之间的关系。
第一种方式是使用instanceof操作符,只要用这个操作符才测试实例与原型链中出现过的构造函数
alert(instance instanceof Obejct); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true |
第二种方式是使用isPrototypeOf()方法。使用原型链中出现的原型来检测是否是该对象实例的原型
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true |
1.3.1.3 谨慎的定义方法
需要谨慎的给原型链定义方法,以免造成代码错误。
1.3.1.4 原型链问题
原型链的最主要问题是所有的原型链方法和属性都是共享的,针对某个实例的原型链属性的修改会影响到其他对象的属性。
1.3.2 借用构造函数
在解决原型中包含引用类型值所带来的过程中,开发人员使用一种叫做借用构造函数的技术。这种技术的基本思想相当简单,即在子构造函数中调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,因此可以使用apply()和call()函数在新创建的对象上执行构造函数
function SuperType(){ this.colors = ["red","blue"]; } function SubType(){ SuperType.call(this); } var ins1 = new SubType(); var ins2 = new SubType(); ins1.colors.push("black"); alert(ins1.colors); // red,blue,black alert(ins2.colors); //red,blur |
借用构造函数可以向超类构造函数中传递参数,但构造函数存在问题,就是构造函数中的函数无法共享,在每个实例中都单独存在。
1.3.3 组合继承
组合继承指的是将原型链和借用构造函数的技术组合在一起,思路的使用原型链实现对原型方法进行继承,而通过借用构造函数实现对实例属性的继承。
function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } 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); } |
1.3.4 原型式继承
借助原型可以基于已有的对象创建新的实例,同时还不必因此创建自定义类型,在object内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的实例对象。
function object(o){ function F(){}; F.prototype = o; return new F(); } |
ECMAScript5通过新增Object.create()方法规划了原型式继承。这个方法接受两个参数,一个用作新对象原型的对象和(可选的)一个新对象定义额外属性的对象,第二个参数定义的属性会覆盖原型对象上同名属性。
1.3.5 寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象。
function object(o){ function F(){}; F.prototype = o; return new F(); } function createAnother(original){ var clone = object(original); clone.sayHi = function(){ alert('hi'); } return clone; } |
1.3.6 寄生式组合继承
组合继承是JavaScript最常见的继承模式,不过,它也有自己的不足。组合继承方式的最大问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一种是在子类型构造函数内部,所谓寄生式组合继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们需要的无非就是超类型原型的一个副本而已。
function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name, age) { SuperType.call(this,name); this.age = age; } SubType.prototype = inheritPrototype(SubType,SuperType); SubType.prototype.sayAge = function(){ alert(this.age); } var ins1 = new SubType(); var ins2 = new SubType(); ins1.colors.push("black"); alert(ins1.colors); // red,blue,black alert(ins2.colors); //red,blur function object(o){ function F(){}; F.prototype = o; return new F(); } function inheritPrototype(SubType,SuperType){ var prototype = object(SuperType.prototype); prototype.constructor = SubType; SubType.prototype = prototype; } |
这种方式不仅只调用了一次SuperType的构造函数,而且避免了在SubType.prototype上面创建不必要的属性。
6. javacript高级程序设计-面向对象设计的更多相关文章
- Javascript高级程序设计——面向对象小结
ECMAScript支持面向对象编程,对象可以在代码执行时创建,具有动态扩展性而非严格意义上的实体. 创建对象方法: 工厂模式:简单的函数创建引用类型 构造函数模式:可以创建自定义引用类型,可以想创建 ...
- 2020/06/06 JavaScript高级程序设计 面向对象的程序设计
ECMAScript虽然是一种面向对象的语言,但是他没有类的概念.所以他的对象也与其他语言中的对象有所不同. ECMA-262定义对象:一组没有特定顺序的值. 6.1 理解对象 创建对象的方法: 1. ...
- Javascript高级程序设计——面向对象之实现继承
原型链: 构造函数中都有一个prototype属性指针,这个指针指向原型对象,而创建的实例也有指向这个原型对象的指针__proto__.当实例查找方法时先在实例上找,找不到再通过__proto__到原 ...
- Javascript高级程序设计——面向对象之创建对象
对象创建方法: 工厂方法 构造函数模式 原型模式 组合构造函数和原型模式 寄生构造函数模式 问题构造函数模式 工厂模式: function Person(name, age){ var obj = n ...
- Javascript高级程序设计——面向对象之理解对象
在面向对象语言中都有类的概念,通过类来创建具有属性和方法的对象.而ECMAScript中没有类的概念,ECMAScript中定义了对象:无需属性的集合,其属性值可以包含基本值.对象.或者函数. 在Ja ...
- 25. javacript高级程序设计-新兴的API
1. 新兴的API requestAnimationFrame():是一个着眼于优化js动画的api,能够在动画运行期间发出信号.通过这种机制,浏览器就能够自动优化屏幕重绘操作 Page Visibi ...
- 24. javacript高级程序设计-最佳实践
1. 最佳实践 l 来自其他语言的代码约定可以用于决定何时进行注释,以及如何进行缩进,不过JavaScript需要针对其松散类型的性质创造一些特殊的约定 l javascript应该定义行为,html ...
- 23. javacript高级程序设计-数据存储
1. 数据存储 1.1 cookie HTTP Cookie, cookie,最初用来在客户端存储回话信息. (1). 限制,不同浏览器对特定域名下的cookie 的个数有限制,每个域名下最好不要操过 ...
- 22. javacript高级程序设计-高级技巧
1. 高级技巧 1.1 函数 l 可以使用惰性载入函数,将任何分支推迟到第一个调用函数的时候 l 函数绑定可以让你创建始终在指定环境中运行的函数,同时函数柯里化可以让你创建已经填写了某些参数的函数 l ...
随机推荐
- Apache CXF实现WebService入门教程(附完整源码)
Apache CXF实现WebService非常简单实用,只需要几步就可以实现一个简单的web service. 首先我们需要新建一个maven项目,在pom中添加依赖和jetty作为测试的web s ...
- MYSql存储过程的作用及语法
1.使用了存过程,很多相似性的删除,更新,新增等操作就变得轻松了,并且以后也便于管理! 2.存储过程因为SQL语句已经预编绎过了,因此运行的速度比较快. 3.存储过程可以接受参数.输出参数.返回单个或 ...
- 2015年12月13日 spring初级知识讲解(四)面向切面的Spring
2015年12月13日 具体内容待补充...
- iOS之下拉放大,上推缩小,一个方法搞定
先来看看效果吧. 讲讲大概的实现思路:1、创建头部的视图和tableview,需要注意的是tableview要设置contentInset,contentInsent 的顶部要和头部视图的背景图的高度 ...
- firefox浏览器不能使用window.close的解决方案
javascript中window.close()函数用来关闭窗体,而且IE.google.firefox浏览均支持,但由于firefox浏览器dom.allow_scripts_to_close_w ...
- 微信电脑版即将到来了 安装QQ浏览器微信版体验吧
之前说过在手机上微信打字慢,tx最终还是想开了,最近TX邀请测试微信电脑版,想要尝鲜的朋友可以去exp.qq.com申请QQ浏览器微信版体验,不过体验将要结束了,相信正式版很快就要出来了.[微信网页版 ...
- webrtc第二篇 聊天室
聊天室模型不一样考虑的问题也不一样 1.websocket文本聊天 * step1 : 向聊天室所有用户(不包括该用户自己)发送当前用户上线信息.客户端用户栏回添加此用户 * step2 : 将该用户 ...
- [开源类库/项目] android保存崩溃时的错误信息log至本地【源码+jar包+使用说...
不知大家是否经常遇到这种情况:自己的项目有时会在没有连接到电脑时发生崩溃,好不容易发现的bug结果连接到电脑时又复现不出来了:又或者自己写的一个功能在开机启动时产生小bug导致崩溃,而刚启动的机器想让 ...
- 使用IzPack打包JAVA Web应用程序
使用IzPack打包JAVA Web应用程序步骤如下: 这里使用IzPack-4.3.5 + launch4j-3.1.0-beta1-win32.zip(绿色版)对Web应用程序打包,打包后即可对我 ...
- HDU1102--最小生成树
Constructing Roads Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...