深入理解 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中非 ...
随机推荐
- 《 .NET并发编程实战》阅读指南 - 第9章
先发表生成URL以印在书里面.等书籍正式出版销售后会公开内容.
- uni-app如何解决在for循环里调用异步请求获取数据顺序混乱问题?
总结/朱季谦 先前有一次做uni-app的js接口对接时,遇到过这样的情况,在for循环里,调用一个异步请求时,返回来的值顺序是乱的,因此,在以下的代码里,push到数组里的值,每次的顺序可能都是不一 ...
- 单片机成长之路(51基础篇)- 024 基于 N76E003 的按键按键状态机
前端时间要用按键,搞了个状态机(如图): main.c #include <N76E003.H> #include <SFR_Macro.h> //N76E003 SFR寄存器 ...
- C#循环 — break VS continue
一.简介 1.break语句:循环-循环中断并停止,退出当前循环: 流程图: 2.continue:循环-循环下一次迭代继续执行. 流程图: 执行过程:立即结果本次循环,判断循环条件,如果成立,则进入 ...
- Linux之《荒岛余生》(三)内存篇
原文:https://juejin.im/post/5c00aee06fb9a049be5d3641 小公司请求量小,但喜欢滥用内存,开一堆线程,大把大把往jvm塞对象,最终问题是内存溢出. 大公司并 ...
- 性能监控工具的配置及使用 - Spotlight On Oracle(oracle)
一. Spotlight On Oracle(oracle)1.1. 工具简介Spotlight是一个强有力的Oracle数据库实时性能诊断工具,提供了一个直观的.可视化的数据库活动展现.S ...
- “You don't have permission to access /phpmyadmin/ on this server.”
<Directory "I:/1/wamp/apps/phpmyadmin3.4.10.1/"> Options Indexes FollowSymLinks Mult ...
- 基于Netty的IdleStateHandler实现Mqtt心跳
基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...
- springboot入门介绍
1. SpringBoot学习之@SpringBootApplication注解 下面是我们经常见到SpringBoot启动类代码: @SpringBootApplicationpublic clas ...
- Solr缓存原理分析及配置优化
一.缓存原理 缓存,带来急速性能体验! Solr提供了一系列的内置缓存来优化查询性能.Solr的缓存原理主要涉及以下4个方面: 1.缓存大小及缓存置换法 从缓存大小的角度来看,不能将缓存设置的太大,否 ...