玩转JavaScript OOP - 类的实现详解


Posted in Javascript onJune 08, 2016

概述

当我们在谈论面向对象编程时,我们在谈论什么?

我们首先谈论的是一些概念:对象、类、封装、继承、多态。

对象和类是面向对象的基础,封装、继承和多态是面向对象编程的三大特性。

JavaScript提供了对象却缺乏类,它不能像C#一样能显式地定义一个类。

但是JavaScript的函数功能非常灵活,其中之一就是构造函数,结合构造函数和原型对象可以实现”类”。

对象和类的概念

对象

“对象”是面向对象编程中非常重要的一个概念,一个对象是一个“东西”(某个人或某件事)的描述。

人和事都来源于现实生活,我们对现实世界的认知就是对人和事的认知。

在编程的领域,代码对于常人来说是抽象的,代码构成的应用是为了更好地解决现实世界的问题。

在分析和设计阶段,使用“对象”的概念能够更好地反应现实世界的问题。

反过来说,代码是包含一些逻辑的,这些逻辑用于描述业务,业务是包含一些业务知识的,业务知识是通过对现实世界的理解和分析总结出来的,这些问题是由现实世界的“对象”构成的。

对象包含特征和行为,用OOP的术语来说,特征是对象的属性,行为是对象的方法。

在现实世界中,相似的对象可以按照一定的标准来分组。例如“蜂鸟”和“老鹰”都被划分到鸟类,鸟类不是一个具体的对象,它是人们根据“蜂鸟”、“老鹰”等那些具体的鸟分析出相似的特征和行为后,归纳出来的一个概念。类相当于一个模板,我们可以基于这个模板创建不同的具体的对象。

在C#中,我们可以定义一个鸟类。

/// <summary>
/// 鸟类
/// </summary>
public class Bird
{
  public void Fly()
  {
    Console.WriteLine("I can fly!");
  }
}

虽然JavaScript是一门面向对象编程语言,但它没有提供class的语法支持。

在JavaScript中,一切都是基于对象的,即使后面要讲的“原型”也都是对象,JavaScript的继承和重用也都是通过原型来实现的。

但是结合构造函数和原型对象可以实现JavaScript的“类”。

构造函数

之前我们使用new Array()创建一个数组,使用new Object()创建一个对象,Array()和Object()是JavaScript内置的两个构造函数,尽管JavaScript没有提供类,但我们可以将Array和Object理解为“类”的概念。

需要注意的是,JavaScript的“类”是由构造函数实现的。

定义构造函数

构造函数也是函数,定义构造函数和其他函数并没有语法上的区别。

唯一的区别是构造函数的首字母应该大写,这也是JavaScript的编程规范。

以下定义了一个Person()构造函数,我们可以将它理解为Person类。

function Person(){
	console.log('I am keepfool.');
}

JavaScript的“类”和构造函数是同时被定义的,在JavaScript中定义“类”时,就同时定义了构造器。

使用构造函数

JavaScript使用类的方式和C#一样,new关键字后面跟着构造函数。

var p = new Person();

玩转JavaScript OOP - 类的实现详解

定义属性和方法

现在我们已经定义好了Person类,可以为Person类添加一些属性和方法。

定义属性

在讲JavaScript对象时,我们讲了对象的属性设置和访问。
这段代码展示了定义对象属性的两种方式:

var cat = {
	color: 'black'
};
cat.name = 'Tom';
console.log(cat.color);
console.log(cat.name);

使用this定义属性

JavaScript类的属性定义方式则有些不同,在构造函数中使用this关键字定义属性:

function Person(name){
	this.name = name;
}

•第一行代码,定义了Person类,并定义了构造函数。

•第二行代码,定义了name属性。

创建并使用对象

以下2行代码创建了两个Person类的对象

var p1 = new Person('James');
var p2 = new Person('Cury');

在Chrome控制台中输出p1.name和p2.name

玩转JavaScript OOP - 类的实现详解

p1和p2是两个不同的对象,修改p1.name不会影响p2.name。

p1.name = 'Lebron James';

玩转JavaScript OOP - 类的实现详解

定义方法

首先,我们区分一下术语“函数”和“方法”,“函数”是独立的单元,而“方法”是依赖于类这个主体存在的。

使用this定义方法

在JavaScript中,类的方法是定义在构造函数中的函数,在构造函数中使用this关键字定义方法:

 

function Person(name) {
	// 定义属性
	this.name = name;
	// 定义方法
	this.sayHello = function() {
		return 'Hello, I am ' + this.name;
	}
}

使用方法

在Chrome控制台分别调用p1和p2对象的sayHello()方法

玩转JavaScript OOP - 类的实现详解

constructor属性

当创建一个对象时,一个特殊的属性被JavaScript自动地分配给对象了,这个属性就是constructor属性。
在chrome控制台输入p1.constructor,可以看到p1对象的constructor属性指向一个函数。

玩转JavaScript OOP - 类的实现详解

瞧瞧这个函数的内容,这不正是Person()构造函数吗?

玩转JavaScript OOP - 类的实现详解

这表示我们也可以通过p1.constructor属性创建对象,

var p3 = new p1.constructor('Steve Nash');

这行代码阐述了一句话:“我不关心p1对象是怎么创建的,但我想让另一个对象如p1一样创建!”

玩转JavaScript OOP - 类的实现详解

在Chrome控制台使用instanceof操作符,可以看到p1、p2、p3都是Person类的实例

玩转JavaScript OOP - 类的实现详解

另外,当我们以{}方式创建对象时,实际上也调用了Object()构造函数。

var o = {};

这行代码声明了一个对象,尽管我们没有设置任何属性和方法,但JavaScript引擎默认给它设置了constructor属性。
o.constructor指向的是Object()构造函数,[native code]显示了Object()是JavaScript内置的函数。

玩转JavaScript OOP - 类的实现详解

原型对象

在JavaScript中,定义一个函数时,函数就会拥有prototype属性,构造函数也不例外。
下图说明了Person()构造函数的prototype属性是一个对象,它是属于函数的,我们称这个属性为原型对象。
从Person类的角度出发,我们也可理解为prototype属性是属于Person类的。

玩转JavaScript OOP - 类的实现详解

同时Person类的实例是没有prototype属性的,上图的p1.prototype是undefined,这说明prototype属性是共享的,这有点像C#中的静态属性。

设置prototype

既然prototype是一个对象,那就可以为它添加属性和方法。

在函数的protpotype属性上定义属性和方法,与设置普通对象的属性和方法没什么区别。
下面的代码为Person.prototype定义了属性和方法。

 

function Person(name){
	this.name = name;
	this.sayHello = function() {
		return 'Hello, I am ' + this.name;
	}
}

// 在构造函数的prototype对象上定义属性和方法
Person.prototype.height = 176;
Person.prototype.run = function(){
	return 'I am ' + this.name + ', I am running!';
}

var p1 = new Person('James');

 使用prototype

在Person.prototype中定义的属性和方法,可以直接被Person类的实例使用,仍然是以object.property的方式使用。

玩转JavaScript OOP - 类的实现详解

需要特别注意的是,namesayHello()是属于Person类的实例,而heightrun()是不属于Person类的实例。

玩转JavaScript OOP - 类的实现详解

小技巧:通过hasOwnProperty方法可以查看对象是否包含某个属性或方法。

自有属性 vs. prototype的属性

Person类的实例既可以使用Person类中的属性,又可以使用Person.prototype中的属性。
那么Person类的属性和Person.prototype的属性有什么差别呢?

首先,我们可以将Person类中的属性和方法理解为“实例属性”。
由于prototype是共享的,我们可以将prototype中的属性和方法理解为“共享属性”。

“实例属性”和“共享属性”的差别主要体现在性能上。
每创建一个Person的实例,就会产生一个name属性和sayHello()方法的副本,而height属性和run()方法则是所有实例共享一个副本。

既然如此,这意味着sayHello()方法可以提到prototype中。
另外,不同的Person实例height可能会不一样,应将它放到Person类中更合理。

 

function Person(name,height){
	this.name = name;
	this.height = height;
}

Person.prototype.sayHello = function(){
	return 'Hello, I am ' + this.name + ', my height is ' + this.height + 'cm.';
}
Person.prototype.run = function(){
	return 'I am ' + this.name + ', I am running!';
}

var p1 = new Person('James',203);
var p2 = new Person('Cury',190);

 

玩转JavaScript OOP - 类的实现详解

类的实现总结

 JavaScript没有类,但构造函数可以实现“类”。

按照JavaScript编程规范,构造函数的首字母应该大写。

“类”的属性和方法是用this.property方式定义在构造函数中的。

在对象创建时JavaScript分配了constructor属性给对象,constructor属性是对象构造函数的一个引用。

函数在定义时就已经有了prototype属性,prototype属性也是一个对象。

prototype是共享的,定义在prototype上的属性和方法可以被“类”的实例使用。

如果属性或方法能够定义在prototype上,就不要定义在构造函数上,使用prototype可以减少内存开销。

以上这篇玩转JavaScript OOP - 类的实现详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jquery的修改当前TAB显示标题的代码
Dec 11 Javascript
基于jquery的跟随屏幕滚动代码
Jul 24 Javascript
javascript克隆对象深度介绍
Nov 20 Javascript
jquery 自定义容器下雨效果可将下雨图标改为其他
Apr 23 Javascript
Javascript验证用户输入URL地址是否为空及格式是否正确
Oct 09 Javascript
基于jQuery实现弹出可关闭遮罩提示框实例代码
Jul 18 Javascript
node.js 中间件express-session使用详解
May 20 Javascript
OkHttp踩坑随笔为何 response.body().string() 只能调用一次
Jan 08 Javascript
Angular4 组件通讯方法大全(推荐)
Jul 12 Javascript
Vue项目引进ElementUI组件的方法
Nov 11 Javascript
在Node.js中将SVG图像转换为PNG,JPEG,TIFF,WEBP和HEIF格式的方法
Aug 22 Javascript
JavaScript indexOf()原理及使用方法详解
Jul 09 Javascript
Javascript之String对象详解
Jun 08 #Javascript
浅谈JavaScript函数的四种存在形态
Jun 08 #Javascript
jQuery Ajax和getJSON获取后台普通json数据和层级json数据用法分析
Jun 08 #Javascript
关于session和cookie的简单理解
Jun 08 #Javascript
jQuery封装的屏幕居中提示信息代码
Jun 08 #Javascript
Ext JS框架程序中阻止键盘触发回退或者刷新页面的代码分享
Jun 07 #Javascript
JavaScript基础语法之js表达式
Jun 07 #Javascript
You might like
DSP接收机前端设想
2021/03/02 无线电
PHP中if和or运行效率对比
2014/12/12 PHP
php实现的递归提成方案实例
2015/11/14 PHP
laravel5.2实现区分前后台用户登录的方法
2017/01/11 PHP
PHP实现对xml进行简单的增删改查(CRUD)操作示例
2017/05/19 PHP
使用Laravel中的查询构造器实现增删改查功能
2019/09/03 PHP
用按钮控制iframe显示的网页实现方法
2013/02/04 Javascript
js中的scroll和offset 使用比较的实例与分析
2013/09/29 Javascript
js统计页面的来访次数实现代码
2014/05/09 Javascript
DOM节点删除函数removeChild()用法实例
2015/01/12 Javascript
倾力总结40条常见的移动端Web页面问题解决方案
2016/05/24 Javascript
通过原生JS实现为元素添加事件的方法
2016/11/23 Javascript
slideToggle+slideup实现手机端折叠菜单效果
2017/05/25 Javascript
实现图片首尾平滑轮播(JS原生方法—节流)
2017/10/17 Javascript
Three.js开发实现3D地图的实践过程总结
2017/11/20 Javascript
Vue数据驱动表单渲染,轻松搞定form表单
2019/07/19 Javascript
js实现转动骰子模型
2019/10/24 Javascript
Vue-resource安装过程及使用方法解析
2020/07/21 Javascript
Openlayers实现距离面积测量
2020/09/28 Javascript
windows如何把已安装的nodejs高版本降级为低版本(图文教程)
2020/12/14 NodeJs
Python判断变量是否为Json格式的字符串示例
2017/05/03 Python
详解Django之admin组件的使用和源码剖析
2018/05/04 Python
python使用RNN实现文本分类
2018/05/24 Python
Python判断两个文件是否相同与两个文本进行相同项筛选的方法
2019/03/01 Python
Python基于BeautifulSoup和requests实现的爬虫功能示例
2019/08/02 Python
PyCharm2019安装教程及其使用(图文教程)
2019/09/29 Python
flask框架渲染Jinja模板与传入模板变量操作详解
2020/01/25 Python
Python基于os.environ从windows获取环境变量
2020/06/09 Python
超级实用的8个Python列表技巧
2020/08/24 Python
利用css3如何设置没有上下边的列表间隔线
2017/07/03 HTML / CSS
详解三种方式实现平滑滚动页面到顶部的功能
2019/04/23 HTML / CSS
优秀的自荐信要注意哪些
2014/01/03 职场文书
电子商务专业毕业生求职信
2014/06/12 职场文书
自荐信格式范文
2015/03/04 职场文书
Python预测分词的实现
2021/06/18 Python
mybatis 解决从列名到属性名的自动映射失败问题
2021/06/30 Java/Android