深入理解 JavaScript 中的 class
在 ES6 规范中,引入了 class
的概念。使得 JS 开发者终于告别了,直接使用原型对象模仿面向对象中的类和类继承时代。
但是JS 中并没有一个真正的 class
原始类型, class
仅仅只是对原型对象运用语法糖。所以,只有理解如何使用原型对象实现类和类继承,才能真正地用好 class
。
ES6:class
通过类来创建对象,使得开发者不必写重复的代码,以达到代码复用的目的。它基于的逻辑是,两个或多个对象的结构功能类似,可以抽象出一个模板,依照模板复制出多个相似的对象。就像自行车制造商一遍一遍地复用相同的蓝图来制造大量的自行车。
使用 ES6 中的 class
声明一个类,是非常简单的事。它的语法如下:
class Person {
constructor(name){
this.name = name
}
hello(){
console.log('Hello, my name is ' + this.name + '.');
}
}
var xiaoMing = new Person('xiaoMing');
xiaoMing.hello() // Hello, my name is xiaoMing.
xiaoMing
是通过类 Person
实例化出来的对象。对象 xiaoMing
是按照类 Person
这个模板,实例化出来的对象。实例化出来的对象拥有类预先订制好的结构和功能。
ES6 的语法很简单,但是在实例化的背后,究竟是什么在起作用呢?
class
实例化的背后原理
使用 class
的语法,让开发者告别了使用 prototype
模仿面向对象的时代。但是,class
并不是 ES6 引入的全新概念,它的原理依旧是原型继承。
typeof class
== "function"
通过类型判断,我们可以得知,class
的并不是什么全新的数据类型,它实际只是 function
(或者说 object
)。
class Person {
// ...
}
typeof Person // function
为了更加直观地了解 Person
的实质,可以将它在控制台打印出来,如下。
Person
的属性并不多,除去用 [[...]]
包起来的内置属性外,大部分属性根据名字就能明白它的作用。需要我们重点关注的是 prototype
和 __proto__
两个属性。
(关于 __proto__
可以在本文的姊妹篇 找到答案)
实例化的原理: prototype
先来讲讲 prototype
属性,它指向一个特殊性对象:原型对象。
原型对象所以特殊,是因为它拥有一个普通对象没有的能力:将它的属性共享给其他对象。
在 ES6 规范 中,对 原型对象 是如下定义的:
object that provides shared properties for other objects
原型对象是如何将它的属性分享给其他对象的呢?
这里使用 ES5 创建一个类,并将它实例化,来看看它的实质。
function Person() {
this.name = name
}
// 1. 首先给 Person.prototype 原型对象添加了 describe 方法 。
Person.prototype.describe = function(){
console.log('Hello, my name is ' + this.name + '.');
}
// 2. 实例化对象的 __proto__ 指向 Person.prototype
var jane = new Person('jane');
jane.__proto__ === Person.prototype;
// 3. 读取 describe 方法时,实际会沿着原型链查找到 Person.prototype 原型对象上。
jane.describe() // Hello, my name is jane.
上述使用 JS 模仿面向对象实例化的背后,实际有三个步骤:
首先给
Person.prototype
属性所指的原型对象上添加了一个方法describe
。在使用
new
关键字创建对象时,会默认给该对象添加一个原型属性__proto__
,该属性指向Person.prototype
原型对象。在读取
describe
方法时,首先会在jane
对象查找该方法,但是jane
对象并不直接拥有describe
方法。所以会沿着原型链查找到 Person.prototype 原型对象上,最后返回该原型对象的describe
方法。
JS 中面向对象实例化的背后原理,实际上就是 原型对象。
为了方便大家理解,从网上扒了一张的图片,放到这来便于大家理解。
prototype
与 __proto__
区别
理解上述原理后,还需要注意 prototype
与 __proto__
属性的区别。
__proto__
所指的对象,真正将它的属性分享给它所属的对象。所有的对象都有 __proto__
属性,它是一个内置属性,被用于继承。
prototype
是一个只属于 function
的属性。当使用 new
方法调用该构造函数的时候,它被用于构建新对象的 __proto__
。另外它不可写,不可枚举,不可配置。
( new Foo() ).__proto__ === Foo.prototype
( new Foo() ).prototype === undefined
class
定义属性
当我们使用 class
定义属性(方法)的时候,实际上等于是在 class
的原型对象上定义属性。
class Foo {
constructor(){ /* constructor */ }
describe(){ /* describe */ }
}
// 等价于
function Foo (){
/* constructor */
}
Foo.prototype.describe = function(){ /* describe */ }
constructor
是一个比较特殊的属性,它指向构造函数(类)本身。可以通过以下代码验证。
Foo.prototype.constructor === Foo // true
类继承
在传统面向对象中,类是可以继承类的。这样子类就可以复制父类的方法,达到代码复用的目的。
ES6 也提供了类继承的语法 extends
,如下:
class Foo {
constructor(who){
this.me = who;
}
identify(){
return "I am " + this.me;
}
}
class Bar extends Foo {
constructor(who){
// super() 指的是调用父类
// 调用的同时,会绑定 this 。
// 如:Foo.call(this, who)
super(who);
}
speak(){
alert( "Hello, " + this.identify() + "." );
}
}
var b1 = new Bar( "b1" );
b1.speak();
当实例 b1
调用 speak
方法时,b1
本身没有 speak
,所以会到 Bar.prototype
原型对象上查找,并且调用原型对象上的 speak
方法。调用 identify
方式时,由于 this
指向的是 b1
对象。所以也会先在 b1
本身查找,然后沿着原型链,查找 Bar.prototype
,最后在 Foo.prototype
原型对象上找到 identify
方法,然后调用。
实际上,在 JavaScript 中,类继承的本质依旧是原型对象。
参考文章
(ES6 规范)[http://www.ecma-international...
原文出处
穿越过来的键盘手,深入理解 JavaScript 中的 class, https://segmentfault.com/a/1190000008338987
深入理解 JavaScript 中的 class的更多相关文章
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 深入理解JavaScript中的属性和特性
深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
- 【干货理解】理解javascript中实现MVC的原理
理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...
- 理解javascript中的策略模式
理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
- 转载 深入理解JavaScript中的this关键字
转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一 ...
- js架构设计模式——理解javascript中的MVVM开发模式
理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...
- 全面理解Javascript中Promise
全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非 ...
随机推荐
- USE11 上oracle11导入数据中文乱码
分类专栏: 数据库 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/suqimm/artic ...
- 【翻译】Tusdotnet中文文档(1)配置和用法
TUSDOTNET Tusdotnet是tus协议的一个dotnet实现.tus协议是用来规范文件上传的整个过程,tus基于http协议,规定了一些上传过程中的头部(headers)和对上传过程的描述 ...
- C++中的Point类与vector类的简单处理
首先设计Vector与Point的表示方法,再依次完善两个类中的构造函数,复制构造函数等. 向量由两个点表示,当进行运算的时候,转化起点坐标为(0,0): 第14行:由于Vector需要用到Point ...
- 2019 多点Dmalljava面试笔试题 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.多点Dmall等公司offer,岗位是Java后端开发,因为发展原因最终选择去了多点Dmall,入职一年时间了 ...
- Mybatis源码解析(一) —— mybatis与Spring是如何整合的?
Mybatis源码解析(一) -- mybatis与Spring是如何整合的? 从大学开始接触mybatis到现在差不多快3年了吧,最近寻思着使用3年了,我却还不清楚其内部实现细节,比如: 它是如 ...
- 藏红花StigmaCroci西红花StigmaCroci番红花
伊朗藏红花(StigmaCroci)是一种耐旱植物,适于生长在冬季最低气温不低于零下20度,夏季最高气温不高于零上35度且气候干燥的地区. 因其水土和气候条件的限制,除了伊朗能大量种植外,希腊.印度. ...
- 由于找不到mingwm10.dll 由于找不到QtCore4.dll
出现如下错误: 由于找不到mingwm10.dll 由于找不到QtCore4.dll 解决办法 将qt库加入环境变量
- Python3报错:ModuleNotFoundError: No module named '_bz2'
系统信息 系统:CentOS Linux release 7.6.1810 (Core) python版本:python3.7 报错信息 from _bz2 import BZ2Compresso ...
- Maven打包时出现“Show Console View”错误弹出框,错误详情为“An internal error has occurred. java.lang.NullPointerException”的解决方法
今天为项目打包时出现了下面的错误提示: 打开Details里面写的是“An internal error has occurred. java.lang.NullPointerException”.在 ...
- ubuntu 16.04 创建NFS
快速设置 安装NFS服务 sudo apt-get install nfs-kernel-server 编辑/etc/exports文件 vim /etc/exports /opt/nfs *(rw, ...