在 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 模仿面向对象实例化的背后,实际有三个步骤:

  1. 首先给 Person.prototype 属性所指的原型对象上添加了一个方法 describe

  2. 在使用 new 关键字创建对象时,会默认给该对象添加一个原型属性 __proto__,该属性指向 Person.prototype 原型对象。

  3. 在读取 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 中,类继承的本质依旧是原型对象。

参考文章

原文出处

穿越过来的键盘手,深入理解 JavaScript 中的 class, https://segmentfault.com/a/1190000008338987

深入理解 JavaScript 中的 class的更多相关文章

  1. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  2. 深入理解JavaScript中创建对象模式的演变(原型)

    深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...

  3. 深入理解JavaScript中的属性和特性

    深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...

  4. 深入理解javascript中执行环境(作用域)与作用域链

    深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...

  5. 【干货理解】理解javascript中实现MVC的原理

    理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...

  6. 理解javascript中的策略模式

    理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...

  7. 深入理解javascript中的立即执行函数(function(){…})()

    投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...

  8. 转载 深入理解JavaScript中的this关键字

    转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字   1. 一 ...

  9. js架构设计模式——理解javascript中的MVVM开发模式

    理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...

  10. 全面理解Javascript中Promise

    全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非 ...

随机推荐

  1. 读取数据,并以txt格式保存

    /// <summary> /// 读取数据,并以txt格式保存 /// </summary> /// <param name="data">数 ...

  2. Exceptionless - 本地搭建

    搭建环境:Windows 10 参与文档:https://github.com/exceptionless/Exceptionless/wiki/Self-Hosting 运行环境: .NET 4.6 ...

  3. 浅聊几种主流Docker网络的实现原理

    原文:https://mp.weixin.qq.com/s/Jdxct8qHrBUtkUq-hnxSRw 参考:https://blog.csdn.net/yarntime/article/detai ...

  4. 微信页面script标签添加crossorigin=“anonymous”导致页面加载失败

    公司一个微信企业号项目,突然出现页面数据加载失败,页面报错信息如下 意思是前端向服务端发送跨域资源请求访问这个js文件,但是服务端并不同意,所以服务端拒绝访问这个地址. 后来发现将crossorigi ...

  5. 开发工具--搭建python环境

    工具|搭建python环境 实现python2版本与python3版本的环境搭建. 正文 1.Python下载 官网: www.python.org 下载: ( 64位3.5.2Windows x86 ...

  6. ASP.NET Core使用Quartz定时调度

    在应用程序开发过程中,经常会需要定时任务调度功能,本篇博客介绍Asp.net Core如何使用Quartz完成定时调度 一.Quartz使用步骤 创建调度器scheduler,并开启 创建Job作业 ...

  7. idea万能快捷键,不可不知的17个实用技巧

    说明 IDEA里有一个万能快捷键(alt enter),功能非常强大,同一个快捷键,可以根据不同的语境提示你不同的操作,很多人可能还不了解这些功能,在处理代码的时候还手动处理,了解这些技巧之后,你编码 ...

  8. Linux下getopt()函数

    from https://www.cnblogs.com/qingergege/p/5914218.html 最近在弄Linux C编程,本科的时候没好好学啊,希望学弟学妹们引以为鉴. 好了,虽然啰嗦 ...

  9. 关于ORACLE图形化安装过程中出现的竖线的处理办法

    这种情况上传个jre 并指定下就好了 ~/database/runInstaller -jreLoc /usr/local/jre1.8.0_191/

  10. Django框架(十五)-- cookie和session组件

    一.cookie 1.cookie的由来 HTTP协议是无状态的.无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直 ...