【学习笔记】六:面向对象的程序设计——理解JS中的对象属性、创建对象、JS中的继承
ES中没有类的概念,这也使其对象和其他语言中的对象有所不同,ES中定义对象为:“无序属性的集合,其属性包含基本值、对象或者函数”。现在常用的创建单个对象的方法为对象字面量形式。在常见多个对象时,使用工程模式的一种变体。
1.理解对象
1)对象的属性分两种:数据属性和访问器属性,每个类型的属性都具有相应的特性。
数据属性:包含一个数据值的访问位置,在这个位置可以读取和写入数据。其包含四种特性:[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]四个属性;
访问器属性:不包含数据值,它们包含一对儿getter和setter函数(不过,这两个函数都不是必需的)。访问器属性不能直接定义,必须使用Object.defineProperty()来定义。其包含四种特性:[[Configurable]]、[[Enumerable]]、[[Get]]、[[Set]]。访问器属性常用的使用方式是设置一个属性值导致其他属性发生变化。
2)使用Object.defineProperties()方法给对象同时定义多个属性。
3)使用Object.getOwnPropertyDescriptor()方法获取给定属性的特性。
2.创建对象
1)工厂模式:通过函数,解决创建多个相似对象的问题,但是工厂模式没有解决对象识别的问题(怎么知道一个对象的类型)。
2)构造函数模式:可以将其创建的实例标识为一种特定的类型,这正是构造函数模式胜过工厂模式的地方。(但是构造函数模式也有弊端,在构造函数模式包含函数属性时,通过构造函数建立的不同对象都会有一个对应的函数属性,完成相同的功能,但是这个函数属性却是通过Function函数定义的不同对象实例,这就说明构造函数模式创建的多个实例对象中总是包含完成相同功能但是却占用空间不同的Function实例,有些浪费。)
丨当然我们有种方式可以避免这种弊端:(假如构造函数中需要创建的函数属性名为:sayName),我们把sayName()
丨函数的定义转移到构造函数外部,而在构数内部,我们将sayName属性设置成等于全局的sayName()函数,这样一来,
丨由于sayName属性包含的是一个指向函数的指针,因此不同对象就共享了在全局作用域中定义的同一个sayName()函数了。
丨但是这种方式也存在弊端,如果有很多方法,那么就需要定义很多个全局函数,这就完全失去了封装性了。而这种问题可以通过原型模式来解决。
3)原型模式:不在构造函数中定义对象实例的属性信息,而是将这些信息直接添加到prototype的原型属性值——原型对象中,这样可以让所有创建的对象实例共享它所包含的属性和方法。
a.理解原型对象:每一个函数都有一个prototype原型属性,而这个属性的属性值为一个原型对象,而我们在该原型对象中定义构造函数需要定义的公有属性,这样在构造函数实例化对象时,所有的对象都会通过原型链访问到这些公有的属性,解决了构造函数模式的弊端。isPrototypeOf(测试一个原型对象是和对象实例的隐藏原型之间存在关系)、Object.getPrototypeOf()(获取一个对象的隐藏原型)、Object.hasOwnProperty(属性名)(判断一个对象是否有自己对应的实例属性)。 更具体的内容在我的另一系列博客中已经讲解了,不了解可以跳转到(深入理解js原型和闭包)一文。
b.查询实例对象属性和原型对象属性的常用方法:in:判断某个属性是否能够通过对象访问,无论该属性存在于实例中还是原型中;
for-in:返回对象所有可访问的、可枚举的属性,无论该属性存在于实例中还是原型中。(但是IE8及更早版本,在实例中定义和原型中同名的属性,且如果该属性在原型中是不可枚举时,那该实例中该属性也是不可返回的,其他浏览器正常);
Object.keys():获取对象上所有可枚举的实例属性;
Object.getOwnPropertyNames():获取对象上所有实例属性;
c.更简单的原型语法:每次使用“构造函数名.prototype”形式添加属性太麻烦了,我们使用一个包含所有属性和方法的对象字面量来重写整个原型对象,减少不必要的输入。(但这种方式有个小问题:那就是改变了constructor属性的指向了,如果对constructor属性有特殊需要,可以手动给constructor赋值为“构造函数名”,如果还觉得这样会改变constructor的[[Enumerable]],属性值,可以使用ES5中的Object.defineProperty()函数修改其属性)。但是这种重写原型对象的方式还存在一个弊端:实例对象的_proto_始终指向最初的原型对象,原型对象换为新的后,实例对象并不能找到它,那么实例对象并不能获取新原型对象中修改新增的属性,如下图所示:
d.原型的动态性:可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来(因为它是改变的指针指向)。但是其也存在c中所说的缺点。
e.原生对象(Object、Array、String等等)的原型也是可以修改添加新属性的,但是一般不建议修改。
f.原型模式的弊端:1.原型模式省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。(小问题)
2.(大问题):由于原型中所有属性是被大部分实例所共享的,这种共享对于函数非常合适,对于属性值玩为基本值的属性也可以,但是对于包含引用类型的属性来说,问题比较突出了。因为有的时候我们想对某个实例的这个属性单独作出更改,但是对于引用类型的属性会反映到其他实例属性上,所以我们很少见到有人单数使用原型模式。
4)组合使用构造函数模式和原型模式
这是定义引用类型最创建的一种模式,也是目前在ES中使用最广泛、认同度最高的一种模式。构造函数模式用于定义实例对象专属属性,而原型模式用于定义方法和共享的属性。这样创建的实例都会有自己的一份实例属性副本,但同时又共享着对方的引用,最大限度地节省了内存。
5)动态原型模式:在构造函数中初始化原型(仅在必要的情况下),通常是通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
6)寄生构造函数模式:这种模式只有在前述集中方法不适用的情况下使用,该方法类似于工厂模式,区别在于初始化实例是使用new操作符,这种方法也具有工厂模式的弊端,不能使用instance of来确定对象类型,一般不建议使用。
7)稳妥构造函数模式:这种模式用于构造稳妥对象(稳妥对象:没有公共属性,而且其中方法也不引用this的对象。这种对象适合在一些安全的环境中(这些环境会禁止使用this和new),或者在防止数据被其他应用程序改动时使用)。这种模式与工程模式相似,有两点不同:一是新创建对象的实例方法不引用this;二是不适用new操作符调用函数。同样这种模式也有工厂模式的缺点,不能使用instance of来确定对象类型,一般只在有特殊安全要求的情况下使用。
3.继承
1)原型链:原型链是JS中实现继承的主要方法。实现方式是重写原型对象,代之以一个新类型的实例。比如A想继承B的属性和方法,则A.prototype=new B(),通过重写A构造函数的原型对象,代之以B的一个实例,这样A的所有实例中就可以继承B的属性和方法。
a.默认的继承:因为原型对象也是对象,所有对象都是是Object的实例,所以所有实例中都可以继承访问到Object定义的属性和方法。
b.确定实例和原型的关系:通过instace of,如果一个实例通过_proto_往上找,相应的函数通过prototype这条链往下找,能找到同一个对象引用,则instance of 返回值就是true。同时一个实例通过_proto_往上找的过程,就是原型链。
c.覆盖超类型中的方法或者添加超类型中不存在的方法时,一定要放在继承之后。(覆盖:因为如果放在前面,超类型的方法会覆盖 它;添加新方法或者说定义自己的方法:因为继承时,是替换子类型的prototype,会覆盖原来原型中的方法,而之前我们说过,我们一般讲方法定义在原型中)。
d.原型链继承的问题:1.会造成原型中存在引用类型值(如果父类属性中包含引用类型值,重写子类原型后会造成子类原型中包含引用类型属性,造成该属性共享,之前说过如果想每个实例都有自己的引用类型属性,引用类型属性是不能放在原型中的)。2.创建子类的实例时,不能在不影响所有对象实例的情况下向超类型的构造函数传参。
2)借用构造函数——在子类构造函数的内部调用超类构造函数。
a.优势:借用构造函数解决了原型链无法传参的问题
b.问题:借用构造函数也无法避免之前讲过的构造函数模式存在的问题——方法属性无法共享。同时父类原型中的属性也无法继承,要实现继承父类只能之后构造函数模式,这显然是不合理的,所以借用构造函数模式也很少单独只用。
3)组合继承(伪经典继承)
使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承。既通过在原型上定义方法实现了函数的复用,又能够保证每个实例能够拥有自己的属性。是JS中最常见的继承模式。
问题:组合式继承会造成两次调用父类的构造函数,会在子类原型对象上创造不必要的多余属性,后面会讲寄生组合式继承,解决这一问题。
4)原型式继承(道格拉斯.克罗克福德 2006年提出的),本质是对给定对象的浅复制
使用一个对象作为新对象的一个基础利用objcet函数来创建新对象,ES5新增了Object.create()函数来规范化原型式继承。在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下可以使用原型式继承。
问题:因为使用原型重写,所有包含引用类型值得属性会共享。
5)寄生式继承——和寄生构造函数、工程模式类似,把原型式继承过程封装,并在内部以某种方式增强对象。(比原型式继承做了一层封装罢了)
问题:使用寄生式为对象添加函数,不能做到函数复用。
6)寄生组合式继承
利用寄生式继承来创建超类型的一个副本来赋值给子类原型,而不必使用超类的构造函数,这样只调用一次超类的构造函数,避免了在子类原型对象上创建不必要的属性。寄生组合式继承时引用类型最理想的继承范式。
【学习笔记】六:面向对象的程序设计——理解JS中的对象属性、创建对象、JS中的继承的更多相关文章
- python学习笔记六 面向对象相关下(基础篇)
面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以将多函数中公用的变量封装到对象中) 对象,根据模板创建的 ...
- javascript 高级程序设计学习笔记(面向对象的程序设计) 1
Object构造函数或对象字面量都可以用来创建对象,但这些方式有个明显的缺点:使用相同一个接口创建很多对象,会产生大量重复代码. 工厂模式 //工厂模式 function createDog (nam ...
- javascript 高级程序设计学习笔记(面向对象的程序设计)继承
ECMAScript中描述了原型链的概念,原型链是实现继承的主要方法. 实现原型链继承有一种基本模式 function SuperType () { this.property = true; } S ...
- javascript 高级程序设计学习笔记(面向对象的程序设计) 2
在调用构造函数时会为实例添加一个指向最初原型的指针,我们可以随时为原型添加属性和方法,并且能在实例中体现出来,但如果是重新了原型对象,那就会切断构造函数与最初原型的联系. function Dog ( ...
- 《JAVASCRIPT 高级程序设计》读书笔记六 面向对象的程序设计
一 对象属性 a.对象定义: 无序属性的集合,其属性可以包含基本值.对象或者函数: b.两种创建方式: 1.构造函数: var person = new Object(); person.name ...
- js面向对象知识点之对象属性 创建对象 总结中
昨天面试出了一道面试题 本人我做错了 于是痛定思痛 再过一遍面向对象 var name="一体机"; var value="infolist"; //构造函数 ...
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
随机推荐
- codevs 1046 旅行家的预算
传送门 1046 旅行家的预算 1999年NOIP全国联赛普及组NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold题解 题目描述 Des ...
- Android Studio3.0中dependencies依赖由compile变为implementation的区别
前言 Android Studio版本更新至3.0了,更新后,连带着com.android.tools.build:gradle 工具也升级到了3.0.0,在3.0.0中使用了最新的Gralde 4. ...
- This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x0501 or higher is recommended
VS2005转换成VS2010时出现的问题: This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x05 ...
- 揭开 iOS 7 之 Multipath TCP 的面纱(转)
看到中文圈似乎讨论 iOS 7 的这个特性的还不多,于是我稍微研究了一下这个「Mutlipath TCP」,写点心得.过程是这样的: Olivier Bonaventure 通过 Wireshark ...
- Identity Server 4 原理和实战(完结)_----选看 OAuth 2.0 简介(上)
https://www.yuque.com/yuejiangliu/dotnet/cg95ni 代表资源所有者的凭据 授权 Authorization Grant 授权是一个代表着资源所有者权限的凭据 ...
- jquery中innerheight outerHeight()与height()的区别
1. .height() 获取匹配元素集合中的第一个元素的当前计算高度值 或 设置每一个匹配元素的高度值(带一个参数). 注意:1).css('height')和.height()之间的区别是后者返回 ...
- C#操作句柄
1.直接上例子吧:收集系统信息msinfo32时,会有一个弹窗,现在要隐藏该弹窗,首先看没有通过句柄隐藏弹窗的现象 2.收集系统信息导入到一个位置 代码: Process[] msinfo32proc ...
- 使用pip安装第三方插件
1. 下载Settools和pip,并安装 a. 下载地址: setuptools : https://pypi.python.org/pypi/setuptools#downloadspip: ht ...
- HDU1598【最小生成树拓展】
参考自 http://www.cnblogs.com/nanke/archive/2012/02/13/2350008.html PS: 没想到最小生成树的kruskal算法从小到大枚举边,然后MAX ...
- Unity NGUI学习
环境 Unity4.3 NGUI v3.68 导入 Project界面->右键->import package->custom package载入安装包即可 untiy4.6用 ...