深入理解javascript构造函数和原型对象


Posted in Javascript onSeptember 23, 2014

常用的几种对象创建模式

使用new关键字创建
最基础的对象创建方式,无非就是和其他多数语言一样说的一样:没对象,你new一个呀!

var gf = new Object();
gf.name = "tangwei";
gf.bar = "c++";
gf.sayWhat = function() {
  console.log(this.name + "said:love you forever");
}

使用字面量创建

这样似乎妥妥的了,但是宅寂的geek们岂能喜欢如此复杂和low土的定义变量的方式,作为一门脚本语言那应该有和其他兄弟们一样的范儿,于是出现了对象字面量的定义方式:

var gf = {
  name : "tangwei",
  bar : "c++",
  sayWhat : function() {
    console.log(this.name + "said:love you forever");
  }
}

工厂模式

实际上这是我们在实际中最常用的对象定义方式,但是我要有好多拥有相似属性的对象(想想都让人激动。。。)怎么办呢?那要是一个个的定义,就会产生大量的代码,何不建个工厂,批量的生产出我们的对象呢,于是,javascript世界中第一个充气娃。。。不,“工厂模式”诞生了!

function createGf(name, bar) {
  var o = new Object();
  o.name = name;
  o.bar = bar;
  o.sayWhat = function() {
    alert(this.name + "said:love you forever");
  }
  return o;
}
var gf1 = createGf("bingbing","d");
var gf2 = createGf("mimi","a");

构造函数

工厂模式解决了多个相似对象的创建问题,但是问题又来了,这些对象都是Object整出来的,怎么区分它们的对象具体类型呢?这时候我们就需要切换到另一种模式了,构造函数模式:

function Gf(name,bar){
  this.name = name;
  this.bar = bar;
  this.sayWhat = function(){
    alert(this.name + "said:love you forever");
  }
}
var gf1 = new Gf("vivian","f");
var gf2 = new Gf("vivian2","f");

这里我们使用一个大写字母开头的构造函数替代了上例中的createGf,注意按照约定构造函数的首字母要大写。在这里我们创建一个新对象,然后将构造函数的作用域赋给新对象,调用构造函数中的方法。
上面的方式似乎没什么不妥,但是我们可以发现,两个实例中调用的构造函数中的sayWhat方法不是同一个Function实例:

console.log(gf1.sayWhat == gf2.sayWhat); //false

调用同一个方法,却声明了不同的实例,实在浪费资源。我们可以优化一下将sayWhat函数放到构造函数外面声明:

function Gf(name,bar){
  this.name = name;
  this.bar = bar;
  this.sayWhat = sayWhat
}
function sayWhat(){
  alert(this.name + "said:love you forever");
}

这样解决了,多个实例多次定义同一个方法实例的问题,但是新问题又来了,我们定义的sayWhat是一个全局作用域的方法,但这个方法其实是没法直接调用的,这就有点矛盾了。如何更优雅的定义一个具备一定封装性的对象呢?我们来看一下javascript原型对象模式。

原型对象模式

理解原型对象
当我们创建一个函数时,该函数就会具备一个prototype属性,这个属性指向通过构造函数创建的那个函数的原型对象。通俗点讲原型对象就是内存中为其他对象提供共享属性和方法的对象。

深入理解javascript构造函数和原型对象

在原型模式中,不必再构造函数中定义实例属性,可以将属性信息直接赋予原型对象:

function Gf(){
  Gf.prototype.name = "vivian";
  Gf.prototype.bar = "c++";
  Gf.prototype.sayWhat = function(){
    alert(this.name + "said:love you forever");
  }
}
var gf1 = new Gf();
gf1.sayWhat();
var gf2 = new Gf();

和构造函数不同的是这里新对象的属性和方法是所有实例都可以共享的,换句话说gf1和gf2访问的是同一份属性和方法。原型对象中除了我们赋予的属性外,还有一些内置的属性,所有原型对象都具备一个constructor属性,这个属性是一个指向包含prototype属性函数的一个指针(敢不敢再绕点!)。通过一幅图我们来清楚的理一下这个绕口的流程:

深入理解javascript构造函数和原型对象

所有的对象都有一个原型对象(prototype),原型对象中有一个constructor属性指向包含prototype属性的函数,Gf的实例gf1和gf2都包含一个内部属性指向原型对象(在firefox浏览器中表现为私有属性proto),当我们访问一个对象中的属性时,首先会询问实例对象中有没有该属性,如果没有则继续查找原型对象。

使用原型对象
在前面的示例中,我们注意到在为原型对象添加属性时,需要每个都增加Gf.prototype,这个工作很重复,在上面对象的创建模式中,我们知道可以通过字面量的形式创建一个对象,这里我们也可以改进一下:

function Gf(){}
Gf.prototype = {
  name : "vivian",
  bar : "c++",
  sayWhat : function(){
    alert(this.name + "said:love you forever");
  }
}

这里有一个地方需要特别注意下,constructor属性不再指向对象Gf,因为每定义一个函数,就会同时为其创建一个prototype对象,这个对象也会自动获取一个新的constructor属性,这个地方我们使用Gf.prototype本质上覆写了原有的prototype对象,因此constructor也变成了新对象的constructor属性,不再指向Gf,而是Object:

var gf1 = new Gf();
console.log(gf1.constructor == Gf);//false
console.log(gf1.constructor == Object)//true

一般情况下,这个微妙的改变是不会对我们造成影响的,但如果你对constructor有特殊的需求,我们也可以显式的指定下Gf.prototype的constructor属性:

Gf.prototype = {
  constructor : Gf,
  name : "vivian",
  bar : "c++",
  sayWhat : function() {
    alert(this.name + "said:love you forever");
  }
}
var gf1 = new Gf();
console.log(gf1.constructor == Gf);//true

通过对原型对象模式的初步了解,我们发现所有的实例对象都共享相同的属性,这是原型模式的基本特点,但往往对于开发者来说这是把“双刃剑”,在实际开发中,我们希望的实例应该是具备自己的属性,这也是在实际开发中很少有人单独使用原型模式的主要原因。

构造函数和原型组合模式

在实际开发中,我们可以使用构造函数来定义对象的属性,使用原型来定义共享的属性和方法,这样我们就可以传递不同的参数来创建出不同的对象,同时又拥有了共享的方法和属性。

function Gf(name,bar){
  this.name = name;
  this.bar = bar;
}
Gf.prototype = {
  constructor : Gf,
  sayWhat : function() {
    alert(this.name + "said:love you forever");
  }
}
var gf1 = new Gf("vivian", "f");
var gf2 = new Gf("vivian1", "c");

在这个例子中,我们再构造函数中定义了对象各自的属性值,在原型对象中定义了constructor属性和sayWhat函数,这样gf1和gf2属性之间就不会产生影响了。这种模式也是实际开发中最常用的对象定义方式,包括很多js库(bootstrap等)默认的采用的模式。

Javascript 相关文章推荐
基于jquery的划词搜索实现(备忘)
Sep 14 Javascript
解析dom中的children对象数组元素firstChild,lastChild的使用
Jul 10 Javascript
JS 按钮点击触发(兼容IE、火狐)
Aug 07 Javascript
js点击选择文本的方法
Feb 09 Javascript
javascript原始值和对象引用实例分析
Apr 25 Javascript
jquery模拟实现鼠标指针停止运动事件
Jan 12 Javascript
javascript获取wx.config内部字段解决微信分享
Mar 09 Javascript
jQuery实现花式轮播之圣诞节礼物传送效果
Dec 25 Javascript
JS失效 提示HTML1114: (UNICODE 字节顺序标记)的代码页 utf-8 覆盖(META 标记)的冲突的代码页 utf-8
Jun 23 Javascript
JavaScript生成指定范围的时间列表
Mar 19 Javascript
JS使用canvas中的measureText方法测量字体宽度示例
Feb 02 Javascript
layui实现鼠标移动到单元格上显示数据的方法
Sep 11 Javascript
常用的jquery模板插件——jQuery Boilerplate介绍
Sep 23 #Javascript
Javascript的setTimeout()使用闭包特性时需要注意的问题
Sep 23 #Javascript
IE6 hack for js 集锦
Sep 23 #Javascript
深入理解javascript作用域和闭包
Sep 23 #Javascript
js变量、作用域及内存详解
Sep 23 #Javascript
js单独获取一个checkbox看其是否被选中
Sep 22 #Javascript
多个checkbox被选中时如何判断是否有自己想要的
Sep 22 #Javascript
You might like
FirePHP 推荐一款PHP调试工具
2011/04/23 PHP
PHP实现设计模式中的抽象工厂模式详解
2014/10/11 PHP
关于php 高并发解决的一点思路
2017/04/16 PHP
PHP开发实现微信退款功能示例
2017/11/25 PHP
thinkPHP5框架实现基于ajax的分页功能示例
2018/06/12 PHP
JavaScript在IE和Firefox浏览器下的7个差异兼容写法小结
2010/06/18 Javascript
关于window.pageYOffset和document.documentElement.scrollTop
2011/04/05 Javascript
基于jQuery的让非HTML5浏览器支持placeholder属性的代码
2011/05/24 Javascript
jquery使用ColorBox弹出图片组浏览层实例演示
2013/03/14 Javascript
JS保存和删除cookie操作 判断cookie是否存在
2013/11/13 Javascript
JS中的THIS和WINDOW.EVENT.SRCELEMENT详解
2015/05/25 Javascript
使用Raygun对Node.js应用进行错误处理的方法
2015/06/23 Javascript
JS中的进制转换以及作用
2016/06/26 Javascript
jQuery插件HighCharts实现的2D面积图效果示例【附demo源码下载】
2017/03/15 Javascript
JS实现的点击表头排序功能示例
2017/03/27 Javascript
Vue computed计算属性的使用方法
2017/07/14 Javascript
vue中echarts3.0自适应的方法
2018/02/26 Javascript
你点的 ES6一些小技巧,请查收
2018/04/25 Javascript
纯javascript前端实现base64图片下载(兼容IE10+)
2018/09/14 Javascript
基于mpvue小程序使用echarts画折线图的方法示例
2019/04/24 Javascript
Vuex,iView UI面包屑导航使用扩展详解
2019/11/04 Javascript
在Python程序中操作文件之isatty()方法的使用教程
2015/05/24 Python
Python IDLE 错误:IDLE''s subprocess didn''t make connection 的解决方案
2017/02/13 Python
对python判断是否回文数的实例详解
2019/02/08 Python
python pyinstaller 加载ui路径方法
2019/06/10 Python
对Python的交互模式和直接运行.py文件的区别详解
2019/06/29 Python
Python3环境安装Scrapy爬虫框架过程及常见错误
2019/07/12 Python
pytorch GAN伪造手写体mnist数据集方式
2020/01/10 Python
法国在线药房:DoctiPharma
2020/10/21 全球购物
九年级化学教学反思
2014/01/28 职场文书
小学生演讲稿大全
2014/04/25 职场文书
学校纪律作风整改措施思想汇报
2014/10/11 职场文书
习近平在党的群众路线教育实践活动总结大会上的讲话
2014/10/21 职场文书
用Python提取PDF表格的方法
2021/04/11 Python
python标准库ElementTree处理xml
2022/05/20 Python
mysql sock 文件解析及作用讲解
2022/07/15 MySQL