深入理解 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中非 ...
随机推荐
- axios安装及使用
使用npm安装 $ npm install axios 使用 bower安装 $ bower install axios 使用 cdn: <script src="https://un ...
- Sqlserver表值函数来获取逗号分隔的ID
其功能为: 将字符串如'1,2,3,4,5,6' 拼接成SQL里面的id 1:使用: select * from Student where id IN( SELECT * FROM dbo.F_SP ...
- 活动任务出现bug
之前做的一个活动任务发现一个bug,是以前和离职同事一起对逻辑的时候没有考虑到的,配置活动的时候应该要先查询下,如果这个产品线上在这段时间已经配置了并且上线了,则不能在做活动处理了,否则就和前面的活动 ...
- Java解析复杂JSON数据的一种方法
1.需解析JSON数据: { "code": 0, "message": "success", "sid": " ...
- Celery简介以及Django中使用celery
目录 Celery简介 消息中间件 任务执行单元 任务结果存储 使用场景 Celery的安装和配置 Celery执行异步任务 基本使用 延时任务 定时任务 异步处理Django任务 案例: Celer ...
- Python 字符串多替换时性能基准测试
结论 先说结果, 直接替换是最好的. replace 一层层用, 方法笨了一点, 还可以. 时间消耗: tx2 < tx3 < tx1 < tx4 t2 < t3 < t ...
- EHLIB 安装方法
Ehlib安装方法 路人甲 2010-05-05 23:01:37 安装文件自带的Readme.txt中的安装过程如下: 1. Delphi 5.x - 7.x, Delphi 9.X Win32, ...
- 10. Javascript 前后端数据加密
为了加强项目的接口安全程度,需求如下 var options = { // 前端需要传送的数据加密 data: { abc: 123, bcd: 123, cds: '撒旦教付货款12313', }, ...
- CSS泣鬼神
博主网站 一.CSS介绍和语法 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 每个CSS样式由两个组成部分:选择器和声明.声明又包括属性和属性值.每个声明 ...
- terminator
terminator 能够实现linux 终端的分屏显示. 安装 sudo add-apt-repository ppa:gnome-terminator sudoapt-get update sud ...