Javascript 面向对象编程(一) 封装


Posted in Javascript onAugust 28, 2011

学习Javascript,最难的地方是什么?

我觉得,Object(对象)最难。因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握。

下面就是我的学习笔记,希望对大家学习这个部分有所帮助。我主要参考了
以下两本书籍:

Javascript 面向对象编程(一) 封装

《面向对象的Javascript》(Object-Oriented JavaScript)

Javascript 面向对象编程(一) 封装

《Javascript高级程序设计(第二版)》(Professional JavaScript for Web Developers, 2nd Edition)

它们都是非常优秀的Javascript读物,推荐阅读。

笔记分成三部分。今天的第一部分是讨论"封装"(Encapsulation),后面的第二部分和第三部分讨论"继承"(Inheritance)。

============================

Javascript 面向对象编程(一):封装

作者:阮一峰

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

那么,如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?

1. 生成对象的原始模式

假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。

var Cat = { 

name : '', 


color : '' 

}

现在,我们需要根据这个原型对象,生成两个实例对象。
var cat1 = {}; // 创建一个空对象 


cat1.name = "大毛"; // 按照原型对象的属性赋值 


cat1.color = "黄色"; 

var cat2 = {}; 


cat2.name = "二毛"; 


cat2.color = "黑色";

好了,这就是最简单的封装了。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。
2. 原始模式的改进
我们可以写一个函数,解决代码重复的问题。
function Cat(name,color){ 


return { 



name:name, 



color:color 


} 

}

然后生成实例对象,就等于是在调用函数:
var cat1 = Cat("大毛","黄色"); 

var cat2 = Cat("二毛","黑色");

这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。
3. 构造函数模式
为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
比如,猫的原型对象现在可以这样写,
function Cat(name,color){ 


this.name=name; 


this.color=color; 

}

我们现在就可以生成实例对象了。
var cat1 = new Cat("大毛","黄色"); 

var cat2 = new Cat("二毛","黑色"); 

alert(cat1.name); // 大毛 

alert(cat1.color); // 黄色

这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。
alert(cat1.constructor == Cat); //true 

alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。
alert(cat1 instanceof Cat); //true 

alert(cat2 instanceof Cat); //true

4. 构造函数模式的问题
构造函数方法很好用,但是存在一个浪费内存的问题。
请看,我们现在为Cat对象添加一个不变的属性"type"(种类),再添加一个方法eat(吃老鼠)。那么,原型对象Cat就变成了下面这样:
function Cat(name,color){ 


this.name = name; 


this.color = color; 


this.type = "猫科动物"; 


this.eat = function(){alert("吃老鼠");}; 

}

还是采用同样的方法,生成实例:
var cat1 = new Cat("大毛","黄色"); 

var cat2 = new Cat ("二毛","黑色"); 

alert(cat1.type); // 猫科动物 

cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

alert(cat1.eat == cat2.eat); //false
能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。
5. Prototype模式
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

function Cat(name,color){ 


this.name = name; 


this.color = color; 

} 

Cat.prototype.type = "猫科动物"; 

Cat.prototype.eat = function(){alert("吃老鼠")};

然后,生成实例。
var cat1 = new Cat("大毛","黄色"); 

var cat2 = new Cat("二毛","黑色"); 

alert(cat1.type); // 猫科动物 

cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

alert(cat1.eat == cat2.eat); //true
6. Prototype模式的验证方法
6.1 isPrototypeOf()
这个方法用来判断,某个proptotype对象和某个实例之间的关系。

alert(Cat.prototype.isPrototypeOf(cat1)); //true 

alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()
每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
alert(cat1.hasOwnProperty("name")); // true 

alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符
in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。
alert("name" in cat1); // true 

alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }
未完,请继续阅读这个系列的第二部分《构造函数的继承》和第三部分《非构造函数的继承》。

(完)

Javascript 相关文章推荐
Javascript学习笔记8 用JSON做原型
Jan 11 Javascript
js jquery数组介绍
Jul 15 Javascript
js将控件隐藏的方法及display属性介绍
Jul 04 Javascript
Javascript 浮点运算精度问题分析与解决
Mar 26 Javascript
node.js中的fs.fchmodSync方法使用说明
Dec 16 Javascript
RequireJS 依赖关系的实例(推荐)
Jan 21 Javascript
微信小程序 解析网页内容详解及实例
Feb 22 Javascript
Vue2.0利用vue-resource上传文件到七牛的实例代码
Jul 28 Javascript
纯JS实现可用于页码更换的飞页特效示例
May 21 Javascript
Vue编程式跳转的实例代码详解
Jul 10 Javascript
jquery实现直播弹幕效果
Nov 28 jQuery
Vue + Element-ui的下拉框el-select获取额外参数详解
Aug 14 Javascript
Javascript继承机制的设计思想分享
Aug 28 #Javascript
有关JavaScript的10个怪癖和秘密分享
Aug 28 #Javascript
JS面向对象编程浅析
Aug 28 #Javascript
用JS实现一个TreeMenu效果分享
Aug 28 #Javascript
JS target与currentTarget区别说明
Aug 28 #Javascript
IE6,IE7,IE8下使用Javascript记录光标选中范围(已补全)
Aug 28 #Javascript
range 标准化之获取
Aug 28 #Javascript
You might like
如何把PHP转成EXE文件
2006/10/09 PHP
发布一个用PHP fsockopen写的HTTP下载的类
2007/02/22 PHP
浅析HTTP消息头网页缓存控制以及header常用指令介绍
2013/06/28 PHP
PHP获取某个月最大天数(最后一天)的方法
2015/07/29 PHP
分享PHP源码批量抓取远程网页图片并保存到本地的实现方法
2015/12/01 PHP
PHP面向对象程序设计重载(overloading)操作详解
2019/06/13 PHP
PHP如何解决微信文章图片防盗链
2020/12/09 PHP
JavaScript DOM 学习第五章 表单简介
2010/02/19 Javascript
JQuery Ajax通过Handler访问外部XML数据的代码
2010/06/01 Javascript
Javascript获取当前时间函数和时间操作小结
2014/10/01 Javascript
JS实现自动变换的菜单效果代码
2015/09/09 Javascript
js判断当前页面用什么浏览器打开的方法
2016/01/06 Javascript
AngularJS ng-blur 指令详解及简单实例
2016/07/30 Javascript
使用Angular缓存父页面数据的方法
2017/01/03 Javascript
nodejs处理图片的中间件node-images详解
2017/05/08 NodeJs
原生js简单实现放大镜特效
2017/05/16 Javascript
详解Vue如何支持JSX语法
2017/11/10 Javascript
微信小程序用户拒绝授权的处理方法详解
2019/09/20 Javascript
React服务端渲染原理解析与实践
2021/03/04 Javascript
[02:19]2014DOTA2国际邀请赛 专访820少年们一起去追梦吧
2014/07/14 DOTA
5种Python单例模式的实现方式
2016/01/14 Python
Python中urllib+urllib2+cookielib模块编写爬虫实战
2016/01/20 Python
Python爬取qq music中的音乐url及批量下载
2017/03/23 Python
python实现图片转字符小工具
2019/04/30 Python
Python中判断子串存在的性能比较及分析总结
2019/06/23 Python
简单了解python中对象的取反运算符
2019/07/01 Python
python GUI库图形界面开发之PyQt5切换按钮控件QPushButton详细使用方法与实例
2020/02/28 Python
Python流程控制语句的深入讲解
2020/06/15 Python
中职应届生会计求职信
2013/10/23 职场文书
北京大学自荐信范文
2014/01/28 职场文书
总经理助理的职责
2014/03/14 职场文书
国土资源局开展党的群众路线教育实践活动整改措施
2014/09/26 职场文书
公司法定代表人授权委托书
2014/09/29 职场文书
筑梦中国心得体会
2016/01/18 职场文书
Win11应用商店打开闪退怎么解决? win11应用商店打不开的多种解决办法
2022/04/05 数码科技
如何开启Apache,Nginx和IIS服务器的GZIP压缩功能
2022/04/29 Servers