玩转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实现表格奇数偶数行不同样式(有图为证及实现代码)
Jan 23 Javascript
JS运动框架之分享侧边栏动画实例
Mar 03 Javascript
理解和运用JavaScript的闭包机制
Aug 13 Javascript
在JavaScript中使用JSON数据
Feb 15 Javascript
非常漂亮的相册集 使用jquery制作相册集
Apr 28 Javascript
jQuery 中ajax异步调用的四种方式
Jun 28 Javascript
Javascript的动态增加类的实现方法
Oct 20 Javascript
详解用vue-cli来搭建vue项目和webpack
Apr 20 Javascript
详解JS数据类型的值拷贝函数(深拷贝)
Jul 13 Javascript
Vue 让元素抖动/摆动起来的实现代码
May 31 Javascript
ES2020 新特性(种草)
Jan 12 Javascript
Vue打包部署到Nginx时,css样式不生效的解决方式
Aug 03 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
PHP常用技巧总结(附函数代码)
2012/02/04 PHP
PHP HTML JavaScript MySQL代码如何互相传值的方法分享
2012/09/30 PHP
php对大文件进行读取操作的实现代码
2013/01/23 PHP
关于php正则匹配汉字的方法介绍
2013/04/25 PHP
PHP针对多用户实现更换头像功能
2016/09/04 PHP
PHP实现的简单异常处理类示例
2017/05/04 PHP
PHP实现非阻塞模式的方法分析
2018/07/26 PHP
ImageFlow可鼠标控制图片滚动
2008/01/30 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
2012/08/17 Javascript
JavaScript利用构造函数和原型的方式模拟C#类的功能
2014/03/06 Javascript
div失去焦点事件实现思路
2014/04/22 Javascript
plupload+artdialog实现多平台上传文件
2016/07/19 Javascript
js判断价格,必须为数字且不能为负数的实现方法
2016/10/07 Javascript
JS实现移动端按首字母检索城市列表附源码下载
2017/07/05 Javascript
bootstrap表格内容过长时用省略号表示的解决方法
2017/11/21 Javascript
nodejs简单实现TCP服务器端和客户端的聊天功能示例
2018/01/04 NodeJs
Vue单页面应用保证F5强刷不清空数据的解决方案
2018/01/31 Javascript
vue axios登录请求拦截器
2018/04/02 Javascript
vue项目使用微信公众号支付总结及遇到的坑
2018/10/23 Javascript
javascript中函数的写法实例代码详解
2018/10/28 Javascript
了解javascript中的Dom操作
2019/05/27 Javascript
Python编程二分法实现冒泡算法+快速排序代码示例
2018/01/15 Python
Python SqlAlchemy动态添加数据表字段实例解析
2018/02/07 Python
python使用代理ip访问网站的实例
2018/05/07 Python
浅谈PYTHON 关于文件的操作
2019/03/19 Python
python中tkinter窗口位置\坐标\大小等实现示例
2020/07/09 Python
Django3中的自定义用户模型实例详解
2020/08/23 Python
用python实现一个简单的验证码
2020/12/09 Python
高清屏中使用Canvas绘图出现模糊的问题及解决方法
2019/06/03 HTML / CSS
Jdbc数据访问技术面试题
2012/03/30 面试题
食品安全工作实施方案
2014/03/26 职场文书
个人批评与自我批评材料
2014/10/17 职场文书
大四学生个人总结
2015/02/15 职场文书
《圆的周长》教学反思
2016/02/17 职场文书
品牌形象定位,全面分析
2019/07/23 职场文书
Python中使用Opencv开发停车位计数器功能
2022/04/04 Python