JS面向对象的程序设计
面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装、继承、多态的特性!但JS中 对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值、对象或者函数。可以简单理解为JS的对象是 一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法)。
一、理解对象:
第一种:基于Object对象
var person = new Object();person.name = 'My Name';person.age = 18;person.getName = function(){return this.name;}
第二种:对象字面量方式(比较清楚的查找对象包含的属性及方法)
var person = {name : 'My name',age : 18,getName : function(){return this.name;}}
JS的对象可以使用‘.’操作符动态的扩展其属性,可以使用’delete’操作符或将属性值设置为’undefined’来删除属性。如下:
person.newAtt=’new Attr’;//添加属性alert(person.newAtt);//new Attrdelete person.age;alert(person.age);//undefined(删除属性后值为undefined);
二、对象属性类型
ECMA-262第5版定义了JS对象属性中特征(用于JS引擎,外部无法直接访问)。ECMAScript中有两种属性:数据属性和访问器属性
1、数据属性:
数据属性指包含一个数据值的位置,可在该位置读取或写入值,该属性有4个供述其行为的特性:
[[configurable]]:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;
[[Enumberable]]:表示是否可通过for-in循环返回属性。默认true;
[[Writable]]:表示是否可修改属性的值。默认true;
[[Value]]:包含该属性的数据值。读取/写入都是该值。默认为undefined;如上面实例对象person中定义了name属性,其值为’My name’,对该值的修改都反正在这个位置
要修改对象属性的默认特征(默认都为true),可调用Object.defineProperty()方法,它接收三个参数:属性所在对象,属性名和一 个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)。
如下:(浏览器支持:IE9+、Firefox 4+、Chrome、Safari5+)
var person = {};Object.defineProperty(person, 'name', {configurable: false,writable: false,value: 'Jack'});alert(person.name);//Jackdelete person.name;person.name = 'lily';alert(person.name);//Jack
可以看出,delete及重置person.name的值都没有生效,这就是因为调用 defineProperty函数修改了对象属性的特征;值得注意的是一旦将configurable设置为false,则无法再使用 defineProperty将其修改为true(执行会报错:can't redefine non-configurable property);
2、访问器属性:
它主要包括一对getter和setter函数,在读取访问器属性时,会调用getter返回有效值;写入访问器属性时,调用setter,写入新值;该属性有以下4个特征:
[[Configurable]]:是否可通过delete操作符删除重新定义属性;
[[Numberable]]:是否可通过for-in循环查找该属性;
[[Get]]:读取属性时调用,默认:undefined;
[[Set]]:写入属性时调用,默认:undefined;
访问器属性不能直接定义,必须使用defineProperty()来定义,如下:
var person = {_age: 18};Object.defineProperty(person, 'isAdult', {get: function () {if (this._age >= 18) {return true;} else {return false;}}});alert(person.isAdult?'成年':'未成年');//成年
从上面可知,定义访问器属性时getter与setter函数不是必须的,并且,在定义getter与setter时不能指定属性的configurable及writable特性;
此外,ECMA-262(5)还提供了一个Object.defineProperties()方法,可以用来一次性定义多个属性的特性:
var person = {};Object.defineProperties(person,{_age:{value:19},isAdult:{get: function () {if (this._age >= 18) {return true;} else {return false;}}}});alert(person.isAdult?'成年':'未成年');//成年
上述代码使用Object.defineProperties()方法同时定义了_age及isAudlt两个属性的特性
此外,使用Object.getOwnPropertyDescriptor()方法可以取得给定属性的特性:
var descriptor = Object.getOwnPropertyDescriptor(person,'_age');alert(descriptor.value);//19
对于数据属性,可以取得:configurable,enumberable,writable和value;
对于访问器属性,可以取得:configurable,enumberable,get和set
三、创建对象
使用Object构造函数或对象字面量都可以创建对象,但缺点是创建多个对象时,会产生大量的重复代码,因此下面介绍可解决这个问题的创建对象的方法
1、工厂模式
function createPerson(name, age, job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.getName = function () {return this.name;}return o;//使用return返回生成的对象实例}var person = createPerson('Jack', 19, 'SoftWare Engineer');
创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。
2、构造函数模式
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.getName = function () {return this.name;}}var person1 = new Person('Jack', 19, 'SoftWare Engineer');
var person2 = new Person('Liye', 23, 'Mechanical Engineer');
使用自定义的构造函数(与普通函数一样,只是用它来创建对象),定义对象类型(如:Person)的属性和方法。它与工厂方法区别在于:
- 没有显式地创建对象
- 直接将属性和方法赋值给this对象;
- 没有return语句;
此外,要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建;实际创建经过以下4个过程:
- 创建一个对象
- 将函数的作用域赋给新对象(因此this指向这个新对象,如:person1)
- 执行构造函数的代码
- 返回该对象
上述由Person构造函数生成的两个对象person1与person2都是Person的实例,因此可以使用instanceof判断,并且因为所有对象都继承Object,因此person1 instanceof Object也返回真:
alert(person1 instanceof Person);//true;alert(person2 instanceof Person);//true;alert(person1 instanceof Object);//true;alert(person1.constructor === person2.constructor);//ture;
虽然构造函数方式比较不错,但也存在缺点,那就是在创建对象时,特别针对对象的属性指向函数时,会重复的创建函数实例,以上述代码为基础,可以改写为:
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.getName = new Function () {//改写后效果与原代码相同,不过是为了方便理解return this.name;}}
上述代码,创建多个实例时,会重复调用new Function();创建多个函数实例,这些函数实例还不是一个作用域中,当然这一般不会有错,但这会造成内存浪费。当然,可以在函数中定义一个getName = getName的引用,而getName函数在Person外定义,这样可以解决重复创建函数实例问题,但在效果上并没有起到封装的效果,如下所示:
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.getName = getName;}function getName() {//到处是代码,看着乱!!return this.name;}
3、原型模式
JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。原型 对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。
function Person(){}Person.prototype.name = 'Jack';//使用原型来添加属性Person.prototype.age = 29;Person.prototype.getName = function(){return this.name;}var person1 = new Person();alert(person1.getName());//Jackvar person2 = new Person();alert(person1.getName === person2.getName);//true;共享一个原型对象的方法
原型是指向原型对象的,这个原型对象与构造函数没有太大关系,唯一的关系是函数的prototype是指向这个原型对象!而基于构造函数创建的对象实例也包含一个内部指针为:[[prototype]]指向原型对象。
实例属性或方法的访问过程是一次搜索过程:
- 首先从对象实例本身开始,如果找到属性就直接返回该属性值;
- 如果实例本身不存在要查找属性,就继续搜索指针指向的原型对象,在其中查找给定名字的属性,如果有就返回;
基于以上分析,原型模式创建的对象实例,其属性是共享原型对象的;但也可以自己实例中再进行定义,在查找时,就不从原型对象获取,而是根据搜索原则,得到本实例的返回;简单来说,就是实例中属性会屏蔽原型对象中的属性;
原型与in操作符
一句话:无论原型中属性,还是对象实例的属性,都可以使用in操作符访问到;要想判断是否是实例本身的属性可以使用object.hasOwnProperty(‘attr’)来判断;
原生对象中原型
原生对象中原型与普通对象的原型一样,可以添加/修改属性或方法,如以下代码为所有字符串对象添加去左右空白原型方法:
String.prototype.trim = function(){return this.replace(/^\s+/,'').replace(/\s+$/,'');}var str = ' word space ';alert('!'+str.trim()+'!');//!word space!
原型模式的缺点,它省略了为构造函数传递初始化参数,这在一定程序带来不便;另外,最主要是当对象的属性是引用类型时,它的值是不变的,总是引用同一个外部对象,所有实例对该对象的操作都会其它实例:
function Person() {}Person.prototype.name = 'Jack';Person.prototype.lessons = ['Math','Physics'];var person1 = new Person();person1.lessons.push('Biology');var person2 = new Person();alert(person2.lessons);//Math,Physics,Biology,person1修改影响了person2
4、组合构造函数及原型模式
目前最为常用的定义类型方式,是组合构造函数模式与原型模式。构造函数模式用于定义实例的属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会 有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存。此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长。
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.lessons = ['Math', 'Physics'];}Person.prototype = {constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回PersongetName: function () {return this.name;}}var person1 = new Person('Jack', 19, 'SoftWare Engneer');person1.lessons.push('Biology');var person2 = new Person('Lily', 39, 'Mechanical Engneer');alert(person1.lessons);//Math,Physics,Biologyalert(person2.lessons);//Math,Physicsalert(person1.getName === person2.getName);//true,//共享原型中定义方法
在所接触的JS库中,jQuery类型的封装就是使用组合模式来实例的!!!
5、动态原型模式
组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致;动态原型模式将所有构造信息都封装在构造函数中,又保持了组合的优 点。其原理就是通过判断构造函数的原型中是否已经定义了共享的方法或属性,如果没有则定义,否则不再执行定义过程。该方式只原型上方法或属性只定义一次, 且将所有构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例中:
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.lessons = ['Math', 'Physics'];}if (typeof this.getName != 'function') {//通过判断实例封装Person.prototype = {constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回PersongetName: function () {return this.name;} }}var person1 = new Person('Jack', 19, 'SoftWare Engneer');person1.lessons.push('Biology');var person2 = new Person('Lily', 39, 'Mechanical Engneer');alert(person1.lessons);//Math,Physics,Biologyalert(person2.lessons);//Math,Physicsalert(person1.getName === person2.getName);//true,//共享原型中定义方法
转载于:https://www.cnblogs.com/gtaxmjld/p/5341648.html
JS面向对象的程序设计相关推荐
- JS面向对象的程序设计之继承-继承的实现-借用构造函数
JS面向对象的程序设计之继承-继承的实现-借用构造函数 前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方翻译的差强人意,所以用自己所理解的,尝试解读下.如有纰漏或错误 ...
- Js面向对象的程序设计——理解对象
Js面向对象的程序设计 Js面向对象的程序设计 理解对象 属性类型 Js面向对象的程序设计 理解对象 示例 : var person=new Object(); person.name="N ...
- JS面向对象的程序设计之创建对象_工厂模式,构造函数模式,原型模式-1
前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方翻译的差强人意,所以用自己所理解的,尝试解读下.如有纰漏或错误,会非常感谢您的指出.文中绝大部分内容引用自<Ja ...
- js面向对象的程序设计 --- 下篇 继承启蒙
继承是oo语言中一个最为人津津乐道的概念.ECMAScript支持实现继承,而且实现继承只要是靠原型链来实现的 ·原型链 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾一 ...
- js面向对象的程序设计 --- 中篇(创建对象) 之 工厂模式和 构造函数模式
创建对象 虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量重复代码. ·工厂模式 工厂模式是一种广为人知的设计模式,这种模式 ...
- js面向对象的程序设计 --- 中篇(创建对象) 之 原型模式
·原型模式 我们创建的每一个函数都由一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有 实例共享的属性和方法. 如果按照字面意思来理解,那 ...
- js面向对象的程序设计 --- 上篇(理解对象)
前言 ECMAScript中没有类的概念,因此它们的对象与基于类的语言中的对象有所不同. ECMA-262把对象定义为:"无序的集合属性,其属性可以包含基本值,对象或者函数".正因 ...
- 面向对象的程序设计1 理解对象—— JS学习笔记2015-7-4(第75天)
面向对象的程序设计一-- 理解对象 转载于:https://www.cnblogs.com/zhangxg/p/4621520.html
- 理解面向过程和面向对象的程序设计方法
一.结构化程序设计 1,概念: 传统的结构化程序设计思想的核心是功能的分解.将问题分解为多个功能模块,根据模块功能来设计用于存储数据的数据结构,最后编写了过程(函数)对数据进行操作实现模块的功能.程序 ...
最新文章
- 你需要知道的requestAnimationFrame
- 转 OFBiz 菜单组件(menu-widget)指引
- python中怎么比较两个列表的大小_Python:找到两个列表中存在的给定长度的公共子列表...
- 网站页面左右_广州网站优化的技巧是什么?
- LeetCode - Easy - 696. Count Binary Substrings
- 如何正确的通过 C++ Primer 学习 C++?(转自知乎)
- 网页动物园2.0发布,经过几个月的努力,采用JAVA编写!
- 中国地图流动图(一)
- java modbus tcp plc_PLC1200MODBUS TCP 程序实例
- 下载、预览PDF报错问题排查
- 我的人格类型是:ESTP(外向,感觉,思维,知觉)
- linux常用命令:文本编辑
- Buffon's Needle An Analysis and Simulation------蒙特卡洛法模拟蒲丰(Buffon)投针实验-使用Matlab
- Camstar 元数据mdb辅助工具
- spark写 本地文件报错
- 高智商翻木块java_高智商游戏翻木块游戏攻略
- DGIOT国内首家轻量级物联网开源平台——支持远程打印条码/二维码和一码设备全生命周期管理
- 用友系统由于修改辅助核算导致总账与辅助账对账不平问题解决方法
- 计算机学生是否有必要考研?学历对程序员的影响到底有多大?
- layui table 分页 上一页 : 下一页 回调
热门文章
- java 深入io_Java高级编程-IO操作深入
- 20多个Maven命令和选项(备忘单)
- java se面试题_Java SE 8面试问答(第2部分)
- sql 计数_SQL不同,SQL计数不同,SQL选择不同
- C++基础教程之类与对象
- 有赞透明多级缓存解决方案(TMC)设计思路
- underscore 系列之字符实体与 _.escape
- mysql性能测试工具之sysbench
- Windows Server入门系列25 系统内置用户和组
- [C++再学习系列] 虚函数的4条规则