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 ...
随机推荐
- underflow 、overflow 下溢和上溢
在strtoull函数返回值中,就提到上溢和下溢的问题,现在把这俩个概念拿出来涨涨见识! 上溢 Overflow 是当一个超长的数据进入到缓冲区时,超出部分被写入上级缓冲区,上级缓冲区存放的可能是数 ...
- PPTP(Point to Point Tunneling Protocol),即点对点隧道协议。
PPTP PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网(VPN),可 ...
- 如何在JDK1.8中愉快地处理日期和时间
如何在JDK1.8中愉快地处理日期和时间 JDK1.8新增了LocalDate和LocalTime接口,为什么要搞一套全新的处理日期和时间的API?因为旧的java.util.Date实在是太难用了. ...
- 安装opensuse的笔记-重庆linux开源组织
mate: 伙伴 matey: 融洽的, 易于亲近的. get matey with sb. poison: a. 有毒的, n.毒药/毒酒v. 下毒, 破坏, 污染 slander [ 撕烂的~~] ...
- Linux 运行 apt-get install 就出现jdk installer 错误的解决方法
解决办法如下: sudo rm /var/lib/dpkg/info/oracle-java7-installer* sudo apt-get purge oracle-java7-installer ...
- ExtJS请求验证方法
//登录连接数据库验证 function loginCheck() { var UserName = Ext.getCmp("UserName").getValue(); var ...
- 在OS X中使用Homebrew
Homebrew可以很方便的进行软件包管理,用官网的一句话来形容就是 Homebrew 使 OS X 更完整.用 gem 来安装您的 gems.用 brew 来搞定它们的依赖包. 安装Homebrew ...
- Markdown 學習
Markdown 格式由John Gruber 創建,是一種便於閱讀,非常簡潔直觀的純文本文件格式,可以方便地轉為html等其他格式,很適合與寫作,不需要關注排版問題 常用學習資源有: ###標題用 ...
- MS Project 使用之创建项目信息
1. 我们打开MS Project 2013,创建一个空白文档. 2. 切换“项目”选项卡,点击“项目信息”,设置项目开始时间等信息,项目一般是需要设置开始时间和使用日历的,下面我们分别进行设置 如, ...
- iOS开发——项目需求-快速回到当前界面的顶部
利用UIWindow实现快速到达顶部 如下图,在状态栏添加一个没有颜色的UIWindow(里面添加一个按钮),实现点击这个按钮时能快速的回到当前界面的顶部 核心代码 一.利用UIWindow实现到达顶 ...