详解JavaScript基于面向对象之创建对象(2)


Posted in Javascript onDecember 10, 2015

接着上文《详解JavaScript基于面向对象之创建对象(1)》继续学习。

4、原型方式
       我们创建的每个函数都有一个通过prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototypt通过条用构造函数而创建的那个对象的原型对象。使用原型的好处就是可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是直接将这些信息添加到原型中。
       原型方式利用了对象的prototype 属性,可以把它看成创建新对象所依赖的原型。这里,首先用空构造函数来设置函数名。然后所有的属性和方法都被直接赋予prototype属性。我重写了前面的例子,代码如下:

function Car() { }; 
//将所有的属性的方法都赋予prototype属性 
Car.prototype.color = "blue"; 
Car.prototype.doors = 4; 
Car.prototype.mpg = 25; 
Car.prototype.showColor = function() { 
  return this.color; 
}; 
var Car1 = new Car(); 
var Car2 = new Car(); 
document.write(Car1.showColor()+"<br/>");//输出:blue 
document.write(Car2.showColor());//输出:blue

      在这段代码中,首先定义构造函数Car(),其中无任何代码。接下来的几行代码,通过给Car的 prototype 属性添加属性去定义Car对象的属性。调用new Car()时,原型的所有属性都被立即赋予要创建的对象,意味着所有Car实例存放的都是指向 showColor() 函数的指针。从语义上讲,所有属性看起来都属于一个对象,因此解决了前面的工厂方式和构造函数方式存在的问题。
       此外,使用这种方式,还能用 instanceof 运算符检查给定变量指向的对象的类型:

<span style="font-size:18px;">document.write(Car1 instanceof Car);    //输出:true</span> 

      原型方式看起来是个不错的解决方案。遗憾的是,它并不尽如人意。首先,这个构造函数没有参数。使用原型方式,不能通过给构造函数传递参数来初始化属性的值,因为Car1和Car2的color属性都等于 "blue",doors属性都等于4,mpg属性都等于25。这意味着必须在对象创建后才能改变属性的默认值,这点很令人讨厌,但还没完。真正的问题出现在属性指向的是对象,而不是函数时。函数共享不会造成问题,但对象却很少被多个实例共享。请思考下面的例子:
function Car() { };//定义一个空构造函数,且不能传递参数 
Car.prototype.color = "blue"; 
Car.prototype.doors = 4; 
Car.prototype.mpg = 25; 
Car.prototype.drivers = new Array("Mike","John"); 
Car.prototype.showColor = function() { 
  return this.color; 
}; 
var Car1 = new Car(); 
var Car2 = new Car(); 
Car1.drivers.push("Bill"); 
document.write(Car1.drivers+"<br/>");//输出:Mike,John,Bill 
document.write(Car2.drivers);//输出 :Mike,John,Bill

       上面的代码中,属性drivers是指向Array对象的指针,该数组中包含两个名"Mike"和 "John"。由于 drivers是引用值,Car的两个实例都指向同一个数组。这意味着给Car1.drivers添加值 "Bill",在 Car2.drivers 中也能看到。输出这两个指针中的任何一个,结果都是显示字符串 "Mike,John,Bill"。由于创建对象时有这么多问题,你一定会想,是否有种合理的创建对象的方法呢?答案是有,需要联合使用构造函数和原型方式。
5、混合的构造函数/原型方式(推荐使用)
       混合使用构造函数方式和原型方式,就可像用其他程序设计语言一样创建对象。这种概念非常简单,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。结果是,所有函数都只创建一次,而每个对象都具有自己的对象属性实例。我们重写了前面的例子,代码如下:

function Car(Color,Doors,Mpg) { 
 this.color = Color; 
 this.doors = Doors; 
 this.mpg = Mpg; 
 this.drivers = new Array("Mike","John"); 
}; 
Car.prototype.showColor = function() { 
   return this.color; 
}; 
var Car1 = new Car("red",4,23); 
var Car2 = new Car("blue",3,25); 
Car1.drivers.push("Bill"); 
document.write(Car1.drivers+"<br/>");//输出:Mike,John,Bill 
documnet.write(Car2.drivers);//输出:Mike,John

       现在就更像创建一般对象了。所有的非函数属性都在构造函数中创建,意味着又能够用构造函数的参数赋予属性默认值了。因为只创建showColor()函数的一个实例,所以没有内存浪费。此外,给Car1的drivers数组添加 "Bill" 值,不会影响到Car2的数组,所以输出这些数组的值时,Car1.drivers 显示的是 "Mike,John,Bill",而 Car2.drivers 显示的是 "Mike,John"。因为使用了原型方式,所以仍然能利用 instanceof运算符来判断对象的类型。
       这种方式是ECMAScript采用的主要方式,它具有其他方式的特性,却没有他们的副作用。不过,有些开发者仍觉得这种方法不够完美。
6、动态原型方式
      对于习惯使用其他语言的开发者来说,使用混合的构造函数/原型方式感觉不那么和谐。毕竟,定义类时,大多数面向对象语言都对属性和方法进行了视觉上的封装。请考虑下面的 Java 类:

class Car { 
 public String color = "blue"; 
 public int doors = 4; 
 public int mpg = 25; 
 public Car(String color, int doors, int mpg) { 
  this.color = color; 
  this.doors = doors; 
  this.mpg = mpg; 
 } 
 public void showColor() { 
  System.out.println(color); 
 } 
}

        Java很好地打包了Car类的所有属性和方法,因此看见这段代码就知道它要实现什么功能,它定义了一个对象的信息。批评混合的构造函数/原型方式的人认为,在构造函数内部找属性,在其外部找方法的做法不合逻辑。因此,他们设计了动态原型方法,以提供更友好的编码风格。
       动态原型方法的基本想法与混合的构造函数/原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置。下面是用动态原型方法重写的Car:

function Car(Color,Doors,Mpg) { 
 this.color = Color; 
 this.doors = Doors; 
 this.mpg = Mpg; 
 this.drivers = new Array("Mike","John"); 
 //如果Car对象中的_initialized为undefined,表明还没有为Car的原型添加方法 
 if (typeof Car._initialized == "undefined") { 
   Car.prototype.showColor = function() { 
    return this.color; 
   }; 
   Car._initialized = true; //设置为true,不必再为prototype添加方法 
 } 
} 
var Car1 = new Car("red",4,23);//生成一个Car对象 
var Car2 = new Car("blue",3,25); 
Car1.drivers.push("Bill");//向Car1对象实例的drivers属性添加一个元素 
document.write(Car1.drivers+"<br/>");//输出:Mike,John,Bill 
document.write(Car2.drivers);//输出:Mike,John

       直到检查typeof Car._initialize是否等于"undefined"之前,这个构造函数都未发生变化。这行代码是动态原型方法中最重要的部分。如果这个值未定义,构造函数将用原型方式继续定义对象的方法,然后把 Car._initialized设置为true。如果这个值定义了(它的值为 true时,typeof 的值为Boolean),那么就不再创建该方法。简而言之,该方法使用标志(_initialized)来判断是否已给原型赋予了任何方法。该方法只创建并赋值一次,传统的 OOP开发者会高兴地发现,这段代码看起来更像其他语言中的类定义了。
       我们应该采用哪种方式呢?
       如前所述,目前使用最广泛的是混合的构造函数/原型方式。此外,动态原型方式也很流行,在功能上与构造函数/原型方式等价。可以采用这两种方式中的任何一种。不过不要单独使用经典的构造函数或原型方式,因为这样会给代码引入问题。总之JS是基于面向对象的一门客户端脚本语言,我们在学习它的面向对象技术的时候要的留意JS与其他严谨性高的程序语言的不同。也要正确使用JS创建对象的合理的方式,推荐使用构造函数与原型方式的混合方式创建对象实例。这样可以避免许多不必要的麻烦。

以上就是JavaScript基于面向对象之创建对象的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
背景音乐每次刷新都可以自动更换
Feb 01 Javascript
js修改table中Td的值(定义td的单击事件)
Jan 10 Javascript
关于include标签导致js路径找不到的问题分析及解决
Jul 09 Javascript
node.js中的console.error方法使用说明
Dec 10 Javascript
简介JavaScript中toUpperCase()方法的使用
Jun 06 Javascript
jquery简单实现网页层的展开与收缩效果
Aug 07 Javascript
js鼠标按键事件和键盘按键事件用法实例汇总
Oct 03 Javascript
Chart.js 轻量级HTML5图表绘制工具库(知识整理)
May 22 Javascript
jQuery创建及操作xml格式数据示例
May 26 jQuery
Vue 使用typescript如何优雅的调用swagger API
Sep 01 Javascript
关于better-scroll插件的无法滑动bug(2021通过插件解决)
Mar 01 Javascript
vue+elementUI实现表格列的显示与隐藏
Apr 13 Vue.js
JS提交form表单实例分析
Dec 10 #Javascript
详解JavaScript基于面向对象之创建对象(1)
Dec 10 #Javascript
AngularJs实现ng1.3+表单验证
Dec 10 #Javascript
理解AngularJs指令
Dec 10 #Javascript
详解AngularJS实现表单验证
Dec 10 #Javascript
jquery实现鼠标悬浮停止轮播特效
Aug 20 #Javascript
JavaScript动态创建form表单并提交的实现方法
Dec 10 #Javascript
You might like
解析php扩展php_curl.dll不加载的解决方法
2013/06/26 PHP
php判断正常访问和外部访问的示例
2014/02/10 PHP
thinkphp的静态缓存用法分析
2014/11/29 PHP
Laravel 5.3 学习笔记之 配置
2016/08/28 PHP
PHP实现大数(浮点数)取余的方法
2017/02/18 PHP
yii2项目实战之restful api授权验证详解
2017/05/20 PHP
javascript中的toFixed固定小数位数 简单实例分享
2013/07/12 Javascript
一个JavaScript变量声明的知识点
2013/10/28 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
2013/12/02 Javascript
jquery 插件实现多行文本框[textarea]自动高度
2015/03/04 Javascript
小巧强大的jquery layer弹窗弹层插件
2015/12/06 Javascript
JavaScript实现ASC转汉字及汉字转ASC的方法
2016/01/23 Javascript
动态加载JavaScript文件的两种方法
2016/04/22 Javascript
AngularJS  $modal弹出框实例代码
2016/08/24 Javascript
JavaScript中const、var和let区别浅析
2016/10/11 Javascript
Javascript中 带名 匿名 箭头函数的重要区别(推荐)
2017/01/29 Javascript
js表单序列化判断空值的实例
2017/09/22 Javascript
jQuery实现监听下拉框选中内容发生改变操作示例
2018/07/13 jQuery
node(koa2) web应用模块介绍详解
2019/03/29 Javascript
解决Vue 移动端点击出现300毫秒延迟的问题
2020/07/21 Javascript
Django中模版的子目录与include标签的使用方法
2015/07/16 Python
Python实现的绘制三维双螺旋线图形功能示例
2018/06/23 Python
python 梯度法求解函数极值的实例
2019/07/10 Python
wxpython多线程防假死与线程间传递消息实例详解
2019/12/13 Python
python numpy--数组的组合和分割实例
2020/02/24 Python
pandas中ix的使用详细讲解
2020/03/09 Python
python实现四人制扑克牌游戏
2020/04/22 Python
python+adb命令实现自动刷视频脚本案例
2020/04/23 Python
Python基于gevent实现文件字符串查找器
2020/08/11 Python
浅谈python锁与死锁问题
2020/08/14 Python
Marc Jacobs彩妆官网:Marc Jacobs Beauty
2017/07/03 全球购物
酒店端午节活动方案
2014/08/26 职场文书
2015年团队工作总结范文
2015/05/04 职场文书
2016学校先进集体事迹材料
2016/02/29 职场文书
用React Native制作一个简单的游戏引擎
2021/05/27 Javascript
Python函数对象与闭包函数
2022/04/13 Python