亲自上手es6的class

新一代标准已经提出有些日子了,最近在写react的时候,react用了大量的class的语法去写组件,对于class的理解不是很深刻,于是这里把从babel转换的代码分享给大家。

类=构造函数+原型

es6标准的类和其他语言的类很相似,但是这只是一种语法糖,底层还是通过原型继承实现的,首先看一个简单的类的形式

 class A {
   constructor(){
     this.name='xiaoming'
   }
   sayHello(){
     console.log('hello')
   }
 }

包含一个构造函数,和一个类里面的函数,如果大家对原型继承有所了解的话,这种形式可以近似写成

function A(){
  this.name='xiaoming';
}
A.prototype={
  sayHello:function(){
    console.log('hello')
  }
}

这就是类的雏形,但是实际操作上还是有些不同的,下面就是babel翻译的es5的语法

'use strict';

var _createClass = function () { 
function defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i]; 
    descriptor.enumerable = descriptor.enumerable || false; 
    descriptor.configurable = true; 
    if ("value" in descriptor)
      descriptor.writable = true; 
    Object.defineProperty(target, descriptor.key, descriptor);
   } 
}
return function (Constructor, protoProps, staticProps) {
  if (protoProps) 
    defineProperties(Constructor.prototype, protoProps);
  if (staticProps) 
    defineProperties(Constructor, staticProps); 
return Constructor; 
}}();

function _classCallCheck(instance, Constructor) { 
  if (!(instance instanceof Constructor))  {
    throw new TypeError("Cannot call a class as a function");
  } 
}

var A = function () {
  function A() {
    _classCallCheck(this, A);
    this.name = 'xiaoming';
  }

  _createClass(A, [{
    key: 'sayHello',
    value: function sayHello() {
      console.log('hello');
    }
  }]);

  return A;
}();

这个代码感兴趣大家可以看看,其中有几个地方需要注意,这个类A不能当成函数去调用,A()这种方法调用会报错,可以

let a = new A();

这样去实例化一个类,当然类都是需要继承的,新版本中继承用extends来实现,考虑这样一个类B继承类A

 class A {
  constructor(){
    this.name='xiaoming'
  }
  sayHello(){
    console.log('hello')
  }
}
  class B extends A{
  constructor(){
    super() 
    this.age=12
  }
  sayHello(){
    console.log('hello')
  }
}

这是加完继承后的代码

'use strict';

var _createClass = function () { 
function defineProperties(target, props) { 
...//和前面一样
 }();

function _possibleConstructorReturn(self, call) { if (!self) {
  throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } 
  return call && (typeof call === "object" || typeof call === "function") ? call : self; 
}

function _inherits(subClass, superClass) { 
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function,not " + typeof superClass); 
  } 
  subClass.prototype = Object.create(superClass && superClass.prototype, {
  constructor: {
    value: subClass, 
    enumerable: false,
    writable: true,
    configurable: true 
  }
 }); 
if (superClass) 
  Object.setPrototypeOf ?
  Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) { 
  throw new TypeError("Cannot call a class as a function");
  } 
}

var A = function () {
  function A() {
    _classCallCheck(this, A);

    this.name = 'xiaoming';
    this.say = function () {
      console.log("123");
    };
  }

  _createClass(A, [{
    key: 'sayHello',
    value: function sayHello() {
      console.log('hello');
    }
  }]);

  return A;
}();

var B = function (_A) {
  _inherits(B, _A);

  function B() {
    _classCallCheck(this, B);

    var _this = _possibleConstructorReturn(this, (B.__proto__ || Object.getPrototypeOf(B)).call(this));

    _this.age =12;
    return _this;
  }

  _createClass(B, [{
    key: 'sayHello',
    value: function sayHello() {
      console.log('hello');
    }
  }]);

  return B;
}(A);

代码很长,都不用看,只看_inherits这个函数,从这一句

subClass.prototype = Object.create(superClass && superClass.prototype, {
  constructor: { 
    value: subClass, 
    enumerable: false, 
    writable: true, 
    configurable: true 
  }
});

这里有个小技巧,&&当计算前面为false时,就不计算后面的表达式了,当然返回的是false或者最后一个值,这是从左向右计算的,这里意思就是如果 superClass 存在,那就计算 superClass.prototype,当然也就是存在的,这一句就是将B的原型设为一个以 A 的 prototype 为原型的对象,也就是说

B.prototype.__proto__=A.prototype

proto VS prototype

这里__proto__这个属性是每个对象都有的,就是因为这个属性的存在,对象可以继承很多不是他自己的属性或方法,比如toString()。虽然toString()不是这个对象自己的方法,但是去调用一个对象的这个方法时,这个对象自己没有,就会去找这个对象的__proto__,在它的__proto__所指向的对象中去找,如果找不到,就会继续去在这个__proto____proto__中去找,这就形成了一个原型链,直到找到为止,找不到就会报错。
prototype__proto__之间的区别很明显,prototype是函数对象所特有的,他作为一个属性指向另一个对象,即这个函数的原型对象,它存在的目的只是为了生产对象,通过这个函数new出来的对象都有一个__proto__属性指向这个函数的原型对象,从下面代码就可以看出来

function A(){
  this.name='xiaoming'
}
A.prototype={
  sayhi:function(){
    console.log('hi')
  }
}
var a=new A();
console.log(a.__proto__===A.prototype)
// true

也就是说,对象的__proto__属性指向那个制造这个对象的构造函数的原型对象,通过对象字面量形式创建的对象的__proto__就是Object.prototype,

o = {};// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);

那么继续之前的话题,_inherits函数中有这一句

if (superClass) 
Object.setPrototypeOf ? 
Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 

这样子类的__proto__就指向了父类,就将原型链头指向了父类,也就是在B中可以使用A的方法和属性,注意是在B这个构造函数内,B的原型对象在之前的代码已经解释了,通过Object.create方法把B的原型对象绑定到了A的原型上,B的原型对象可以通过原型链原型继承使用A的原型对象的属性和方法。
总之最后的情况是这样的

B.__proto__=A
B.prototype=对象xx
对象xx:{
__proto__:A.prototype
constructor:B
...
}

当使用new去创建一个B的实例b时会发生这样的过程
在constructor中会得到以this.xx,比如this.age this.age...
等等还有一个不能忘了在B的构造函数中会有个super(),这样A的构造函数也会执行了,不然没有name属性
然后就是将这个对象的__proto__指向那个对象xx,也就是B这个构造函数的原型对象,这样就能访问这个原型链上的属性和方法了。

总结

新版本的类也是基于原型继承的,所以只要把基础打好了,遇到新的东西也理解的比较清楚,class中constructor对应的还是以前的构造函数,整个类里面的内容就是这个构造函数的原型对象的内容,如果有继承还要加上继承的对象的内容,我们依然可以用类名xx来指代以前的构造函数,xx.prototype来指代原型对象。新的语法形式,对外隐藏了实现的细节,写起来更加简洁,还有会在不正当时使用时的错误提示。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容

  • class的基本用法 概述 JavaScript语言的传统方法是通过构造函数,定义并生成新对象。下面是一个例子: ...
    呼呼哥阅读 4,092评论 3 11
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 2,997评论 4 14
  • 特别说明,为便于查阅,文章转自https://github.com/getify/You-Dont-Know-JS...
    杀破狼real阅读 1,135评论 0 4
  • 在JavaScript中,原型链作为一个基础,老生长谈,今天我们就来深入的解读一下原型链。 本章主要讲的是下面几点...
    Devinnn阅读 1,403评论 1 6
  • 大概有四年了,连暗恋都没有过,爱情匮乏得像一片荒芜的沙漠。但是最近,最近好像喜欢上了一个人,想起他,嘴角就不自觉上...
    淳安阅读 234评论 0 1