JavaScript ES6 Class 类定义、继承与静态成员详解

作者:青柠

时间:2026-10-30

浏览:341

本文深入解析 JavaScript ES6 中 Class 类的定义与使用。涵盖构造函数、静态方法与原型方法的语法差异,重点剖析 ES6 基于原型的继承机制(extends 与 super),对比 ES5 传统继承模式的优劣,并介绍通过 Mix-in 实现多重继承的方案。

文章目录

ES6 Class 概述与定义

ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony(和谐之意,显然没有跟上我国的步伐,我们已经进入中国梦版本了)。上一次标准的制订还是2009年出台的ES5。目前ES6的标准化工作正在进行中,预计会在14年12月份放出正式敲定的版本。但大部分标准已经就绪,且各浏览器对ES6的支持也正在实现中。

ES6 中的类定义语法可视为 ES3 和 ES5 中类定义语法的语法糖。虽然存在细微差别,但整体定义方式更为简洁,且继承机制更加便捷。建议读者在深入理解 ES6 继承前,先掌握 ES5 中的原型继承原理,相关资源可通过技术博客获取。

定义一个 class:

使用 class 关键字定义的类,默认包含一个 constructor 构造函数。该函数负责初始化实例,其内部的 this 指向新创建的实例对象。花括号内的 say() {} 定义的是原型上的方法。以下是一个简单的类定义示例:

运行下面代码


"use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
 say () {
  console.log("say hi");
 }
};
new Person().say(); //控制台会输出say hi

注意:ES6 中声明的类不存在变量提升(hoisting)现象,即类必须先声明后使用,否则将抛出异常。若将上述 Demo 代码的执行顺序颠倒,会立即报错。这与 ES5 中 var 声明的提升机制不同,class 类名 {} 的声明方式类似于 var 类名 = function(){},但不具备提升特性。

运行下面代码


 "use strict";
new Person().say();
class Person {
 constructor(name) {
  this.name = name;
 }
 say () {
  console.log("say hi");
 }
};

静态方法与原型方法

定义函数的静态方法:

在类定义的大括号内部,若函数名前添加了 static 关键字,则该函数被定义为静态方法。静态方法与原型链无关,直接挂载在类本身而非实例上。

运行下面代码


 "use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
 static say () {
  console.log("say hi");
 }
};
Person.say();

定义原型方法:

在 ES6 中,定义原型方法只需直接声明函数名后跟括号和代码块即可,括号内为参数列表,大括号内为代码块。相比之下,ES5 中定义原型方法需通过构造函数.prototype.原型方法名() {} 的形式,书写较为繁琐。ES6 的这种定义方式更接近 Java 和 C# 等高级语言的特征。

运行下面代码


 "use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
 say () {
  console.log("say hi");
 }
 sing () {
  console.log("lalalalala");
 }
};
new Person().say(); //输出 :say hi
new Person().sing(); //输出 :lalalalala

静态属性与原型访问器

静态属性和原型属性:

静态属性只能在类定义完成后进行定义。这种设计可能是为了避免代码混乱,确保所有静态属性在统一位置定义,从而提升代码规范性。

运行下面代码


 "use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
};
Person.hands = 2;
console.log(Person.hands);

在原型上直接定义普通属性已不再适用,我们只能在原型上定义 getter 和 setter 访问器。需要注意的是,取值器和设值器是定义在原型上的。

运行下面代码


class Person {
 constructor(_name) {
  this._name = _name;
 }
 get name() {
  return this._name;
 }
 set name(_name) {
  this._name = _name;
 }
}
var p = new Person();
p.name = "heheda";
console.log(p.name); //输出:heheda
console.log(p._name); //输出:heheda

若需定义实例属性,可直接在 constructor 构造函数内部进行定义。在继承场景下,子类会自动继承父类中定义的这些属性。

运行下面代码


 class Person {
 constructor() {
  this.name = "default";
 }
}
class Man extends Person{
 constructor() {
  super();
 }
}
console.log( new Man().name );

类的继承机制

类的继承extends:

ES5 已具备继承机制,但实现过程往往较为复杂。ES6 的继承本质上仍是基于原型继承的封装(语法糖),尽管代码简洁度大幅提升,但学习曲线仍可能高于 Java 等语言。以下 Demo 中的 SMan 意为“超人”,请勿产生歧义。

运行下面代码


 "use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
 say () {
  console.log("say hi");
  return this;
 }
};
class SMan extends Person {
 constructor (name, power) {
  super(name);
  this.superPower = power;
 }
 show () {
  console.log(this.superPower);
  return this;
 }
}
console.log( new SMan("Clark", "pee").show().say().name ); //输出: pee say hi Clark

在使用继承时,子类构造函数中必须调用 super() 以执行父类逻辑,否则编译器将报错。在子类中,super 关键字有三种用途:一是作为构造函数直接调用;二是作为父类实例引用;三是在子类的静态方法中调用父类的静态方法。

ES6 与 ES5 继承的主要区别在于实现机制。ES5 常用做法是将子类的原型设置为父类的实例,从而使子类获得父类的所有方法和属性。

运行下面代码


 var Sup = function() {
 this.sub = true;
};
Sup.prototype.protoSup = {sup:"sup"};
var Sub = function() {
 this.sub = true;
};
Sub.prototype = new Sup(); //继承原型;
Sub.prototype.constructor = Sub; //修正constructor;

ES6 实现的继承机制更为精巧,避免了父类干扰。这种继承方式结合了 apply 继承和原型继承,属于组合继承的一种实现。

运行下面代码


 var Sup = function() {
 this.sub = true;
};
var Sub = function() {
 this.sup = true;
 Sup.apply(this); //继承this的属性和方法;
};
Sub.__proto__ = Sup; //继承Sup静态属性;
Sub.prototype = Object.create( Sup.prototype, {constructor : { value: Sub, enumerable: false, writable: true, configurable: true }}); //继承原型属性,并覆写constructor;

通过图片可以更直观地看出两者的区别,图示 ES5 和 ES6 继承的区别:http://keenwon.com/1524.html ;

ES5 模拟 ES6 继承

ES5模拟ES6的继承:

借助转码器 babel,我们可以通过 ES5 代码来窥探 ES6 继承的具体实现原理。以下是 ES6 继承的代码示例:

运行下面代码


"use strict";
class Person {
 constructor(name) {
  this.name = name;
 }
 say () {
  console.log("say hi");
  return this;
 }
};
class SMan extends Person {
 constructor (name, power) {
  super(name);
  this.superPower = power;
 }
 show () {
  console.log(this.superPower);
  return this;
 }
}
console.log( new SMan("Clark", "pee").show().say().name );

使用 babel 转化为 ES5 后,代码结构发生如下变化,此处添加了一些注释以便理解:

运行下面代码


 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");
  }
 }
 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;
 }
 //下面是ES6继承使用ES5表达出来的代码,_inherits实现的是原型的继承和父类状态属性的继承:
 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);
  }
  //继承父类的原型,并修正constructor为子类;
  subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
  //又给子类这个对象定义__proto__ 为父类, 这样能够实现静态属性继承;
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  //最后的如果开发者:new 子类, 实际的状态为: 对象{__proto__:父类,constuctor:子类}
 };
 /*
 var Sup = function() {};
 var Sub = function() {};
 _inherits(Sub, Sup);
 //这个继承实现的意思; 作为对象的子类继承父类, 作为构造函数的话,子类继承
 Sub.prototype.__proto__ === Sup.prototype //true
 Sub.prototype.constructor === Sub;//true
 Sub.__proto__ === Sup;//true
 */
 var Person = function () {
  function Person(name) {
   _classCallCheck(this, Person);
   this.name = name;
  }
  _createClass(Person, [{
   key: "say",
   value: function say() {
    console.log("say hi");
    return this;
   }
  }]);
  return Person;
 }();
 ;
 var SMan = function (_Person) {
  _inherits(SMan, _Person);
  function SMan(name, power) {
   //此时的this.__proto__已经指向 构造函数的prototyp了
   _classCallCheck(this, SMan);
   //这句话相当于是ES6中的super(), 把父类的属性通过call, 执行继承;
   var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(SMan).call(this, name));
   _this.superPower = power;
   //动态返回_this;
   return _this;
  }
  _createClass(SMan, [{
   key: "show",
   value: function show() {
    console.log(this.superPower);
    return this;
   }
  }]);
  return SMan;
 }(Person);
 console.log(new SMan("Clark", "pee").show().say().name);

多重继承方案

多重继承:

使用 mix-in 模式可以实现多重继承,书写格式为:class Sub extends mix(obj0, obj1, obj2)。其中 mix 是一个需要自定义的方法。

运行下面代码


 <html>
<head>
 <meta charset="utf-8">
</head>
<body>
<script>
 "use strict";
 function mix(...mixins) {
  class Mix {}
  for (let mixin of mixins) {
   copyProperties(Mix, mixin);
   copyProperties(Mix.prototype, mixin.prototype);
  }
  return Mix;
 }
 function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
   if ( key !== "constructor"
     && key !== "prototype"
     && key !== "name"
     ) {
    let desc = Object.getOwnPropertyDescriptor(source, key);
    Object.defineProperty(target, key, desc);
   }
  }
 }
 class Man{
  work () {
   console.log("working");
  }
 }
 class Woman{
  say () {
   console.log("saying");
  }
 }
 class SuperMan extends mix(Man, Woman) {
  constructor () {
   super();
  }
 }
 var sm = new SuperMan();
 sm.work();
 sm.say();
 //实际上它们不存在继承关系, 只是把属性复制到子类上;
 console.log(sm instanceof Man);
 console.log(sm instanceof Woman);
</script>
</body>
</html> 

以上所述是小编给大家介绍的JavaScript ES6的新特性使用新方法定义Class的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章