JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)


Posted in Javascript onAugust 16, 2014

什么是面向对象?面向对象是一种思想!(废话)。

面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法。这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作。接下来将为大家讲解在JS中面向对象的实现。

 

工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,而由于在ECMAScript中无法创建类,因此用函数封装以特定接口创建对象。其实现方法非常简单,也就是在函数内创建一个对象,给对象赋予属性及方法再将对象返回即可。

function createBlog(name, url) {
  var o = new Object();
  o.name = name;
  o.url = url;
  o.sayUrl= function() {
    alert(this.url);
  }
  return o;
}

var blog1 = createBlog('wuyuchang', 'https://3water.com/');

可以看到工厂模式的实现方法非常简单,解决了创建多个相似对象的问题,但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,因此出现了构造函数模式。

构造函数模式

ECMAScript中构造函数可以创建特定类型的对象,类似于Array、Date等原生JS的对象。其实现方法如下:

function Blog(name, url) {
  this.name = name;
  this.url = url;
  this.alertUrl = function() {
    alert(this.url);
  }
}

var blog = new Blog('wuyuchang', 'https://3water.com/');
console.log(blog instanceof Blog);  // true, 判断blog是否是Blog的实例,即解决了工厂模式中不能

这个例子与工厂模式中除了函数名不同以外,细心的童鞋应该发现许多不同之处:

函数名首写字母为大写

(虽然标准没有严格规定首写字母为大写,但按照惯例,构造函数的首写字母用大写
没有显示的创建对象
直接将属性和方法赋值给了this对象
没有return语句
使用new创建对象
能够识别对象(这正是构造函数模式胜于工厂模式的地方)

构造函数虽然好用,但也并非没有缺点,使用构造函数的最大的问题在于每次创建实例的时候都要重新创建一次方法(理论上每次创建对象的时候对象的属性均不同,而对象的方法是相同的),然而创建两次完全相同的方法是没有必要的,因此,我们可以将函数移到对象外面(也许有些童鞋已经看出缺点,嘘!)。

function Blog(name, url) {
  this.name = name;
  this.url = url;
  this.alertUrl = alertUrl;
}

function alertUrl() {
  alert(this.url);
}

var blog = new Blog('sc3water', 'http://sc.3water.com/'),
  blog2 = new Blog('3water', 'https://3water.com/');
blog.alertUrl();  // http://sc.3water.com/
blog2.alertUrl();  // https://3water.com/

我们将alertUrl设置成全局函数,这样一来blog与blog2访问的都是同一个函数,可是问题又来了,在全局作用域中定义了一个实际只想让Blog使用的函数,显示让全局作用域有些名副其实,更让人无法接受的是在全局作用域中定义了许多仅供特定对象使用的方法,浪费空间不说,显然失去了面向对象封装性了,因此可以通过原型来解决此问题。

  原型模式

我们创建的每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处就是可以让所有对象实例共享它所包含的属性及方法。

function Blog() {
}

Blog.prototype.name = 'wuyuchang';
Blog.prototype.url = 'http://tools.3water.com/';
Blog.prototype.friend = ['fr1', 'fr2', 'fr3', 'fr4'];
Blog.prototype.alertInfo = function() {
  alert(this.name + this.url + this.friend );
}

// 以下为测试代码
var blog = new Blog(),
  blog2 = new Blog();
blog.alertInfo();  // wuyuchanghttp://tools.3water.com/fr1,fr2,fr3,fr4
blog2.alertInfo();  // wuyuchanghttp://tools.3water.com/fr1,fr2,fr3,fr4

blog.name = 'wyc1';
blog.url = 'http://***.com';
blog.friend.pop();
blog2.name = 'wyc2';
blog2.url = 'http://+++.com';
blog.alertInfo();  // wyc1http://***.comfr1,fr2,fr3
blog2.alertInfo();  // wyc2http://+++.comfr1,fr2,fr3

原型模式也不是没有缺点,首先,它省略了构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得了相同的属性值,这样非常不方便,但这还是不是原型的最大问题,原型模式的最大问题在于共享的本性所导致的,由于共享,因此因此一个实例修改了引用,另一个也随之更改了引用。因此我们通常不单独使用原型,而是结合原型模式与构造函数模式。

混合模式(原型模式 + 构造函数模式)

function Blog(name, url, friend) {
  this.name = name;
  this.url = url;
  this.friend = friend;
}

Blog.prototype.alertInfo = function() {
  alert(this.name + this.url + this.friend);
}

var blog = new Blog('wuyuchang', 'http://tools.3water.com/', ['fr1', 'fr2', 'fr3']),
  blog2 = new Blog('wyc', 'http://**.com', ['a', 'b']);

blog.friend.pop();
blog.alertInfo();  // wuyuchanghttp://tools.3water.com/fr1,fr2
blog2.alertInfo();  // wychttp://**.coma,b

混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每个实例都会有自己的一份实例属性,但同时又共享着方法,最大限度的节省了内存。另外这种模式还支持传递初始参数。优点甚多。这种模式在ECMAScript中是使用最广泛、认同度最高的一种创建自定义对象的方法。

动态原型模式

动态原型模式将所有信息封装在了构造函数中,而通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个可以通过判断该方法是否有效而选择是否需要初始化原型。

function Blog(name, url) {
  this.name = name;
  this.url = url;

  if (typeof this.alertInfo != 'function') {
    // 这段代码只执行了一次
    alert('exe time');
    Blog.prototype.alertInfo = function() {
      alert(thia.name + this.url);
    }
  }
}

var blog = new Blog('wuyuchang', 'http://tools.3water.com'),
  blog2 = new Blog('wyc', 'http:***.com');

可以看到上面的例子中只弹出一次窗,'exe time',即当blog初始化时,这样做blog2就不在需要初始化原型,对于使用这种模式创建对象,可以算是perfect了。

此博文参考《JavaScript高级程序设计》第3版,但语言都经过简化,例子也重写过,如果有什么不懂的地方请留言回复,作者将更新博客。

Javascript 相关文章推荐
无语,javascript居然支持中文(unicode)编程!
Apr 12 Javascript
javascript 面向对象编程  function是方法(函数)
Sep 17 Javascript
jquery使用ColorBox弹出图片组浏览层实例演示
Mar 14 Javascript
jquery Ajax 实现加载数据前动画效果的示例代码
Feb 07 Javascript
Javascript中实现trim()函数的两种方法
Feb 04 Javascript
举例详解AngularJS中ngShow和ngHide的使用方法
Jun 19 Javascript
详解JS正则replace的使用方法
Mar 06 Javascript
Node.js 应用跑得更快 10 个技巧
Apr 03 Javascript
Bootstrap Table 搜索框和查询功能
Nov 30 Javascript
JavaScript 数组去重并统计重复元素出现的次数实例
Dec 14 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
Oct 10 jQuery
js找出5个数中最大的一个数和倒数第二大的数实现方法示例小结
Mar 04 Javascript
Node.js安装教程和NPM包管理器使用详解
Aug 16 #Javascript
Node.js中的事件驱动编程详解
Aug 16 #Javascript
Node.js文件操作详解
Aug 16 #Javascript
Node.js中使用Buffer编码、解码二进制数据详解
Aug 16 #Javascript
Node.js中创建和管理外部进程详解
Aug 16 #Javascript
Node.js模块加载详解
Aug 16 #Javascript
JS遍历Json字符串中键值对先转成JSON对象再遍历
Aug 15 #Javascript
You might like
php一个找二层目录的小东东
2012/08/02 PHP
用来解析.htpasswd文件的PHP类
2012/09/05 PHP
php在数组中查找指定值的方法
2015/03/17 PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
2017/12/26 PHP
php 自定义函数实现将数据 以excel 表格形式导出示例
2019/11/13 PHP
setTimeout函数兼容各主流浏览器运行执行效果实例
2013/06/13 Javascript
jquery模拟SELECT下拉框取值效果
2013/10/23 Javascript
JavaScript返回网页中超链接数量的方法
2015/04/03 Javascript
bootstrap-wysiwyg结合ajax实现图片上传实时刷新功能
2016/05/27 Javascript
【经典源码收藏】jQuery实用代码片段(筛选,搜索,样式,清除默认值,多选等)
2016/06/07 Javascript
vue 封装自定义组件之tabal列表编辑单元格组件实例代码
2017/09/07 Javascript
Three.js利用性能插件stats实现性能监听的方法
2017/09/25 Javascript
原生javascript实现文件异步上传的实例讲解
2017/10/26 Javascript
JavaScript引用类型之基本包装类型实例分析【Boolean、Number和String】
2018/08/09 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
2018/08/30 Javascript
原生js实现针对Dom节点的CRUD操作示例
2019/08/26 Javascript
vue-resourc发起异步请求的方法
2020/02/11 Javascript
详解VUE中的插值( Interpolation)语法
2020/10/18 Javascript
[04:09]显微镜下的DOTA2第十二期—NaVi美如画的团战
2014/06/23 DOTA
[34:08]2018DOTA2亚洲邀请赛3月29日 小组赛B组 VP VS EG
2018/03/30 DOTA
python中使用xlrd、xlwt操作excel表格详解
2015/01/29 Python
Python对CSV、Excel、txt、dat文件的处理
2018/09/18 Python
基于pycharm导入模块显示不存在的解决方法
2018/10/13 Python
在ubuntu16.04中将python3设置为默认的命令写法
2018/10/31 Python
Python使用combinations实现排列组合的方法
2018/11/13 Python
pytorch使用Variable实现线性回归
2019/05/21 Python
Python定时任务框架APScheduler原理及常用代码
2020/10/05 Python
python 检测nginx服务邮件报警的脚本
2020/12/31 Python
爱尔兰灯和灯具网上商店:Lights.ie
2018/03/26 全球购物
美国医生配方营养补充剂供应商:Healthy Directions
2019/07/10 全球购物
OnePlus加拿大官网:中国国际化手机品牌
2020/10/13 全球购物
感恩教育活动总结
2014/05/05 职场文书
中职三好学生事迹材料
2014/08/24 职场文书
2015年保险公司个人工作总结
2015/05/22 职场文书
2016年党校科级干部培训班学习心得体会
2016/01/06 职场文书
浅谈Redis 中的过期删除策略和内存淘汰机制
2022/04/03 Redis