es6クラスは糖衣構文ですか?

青灯夜游
青灯夜游オリジナル
2022-10-17 18:03:101916ブラウズ

クラスは糖衣構文です。理由: クラスはプロトタイプ継承の実装に基づいており、言語の機能には影響を与えません。これは文法の記述と読み取りを容易にするだけであり、クラスの本質は関数であり、これによりオブジェクト プロトタイプの記述がより明確になり、オブジェクト指向プログラミングの文法に似ています。

es6クラスは糖衣構文ですか?

このチュートリアルの動作環境: Windows 7 システム、ECMAScript バージョン 6、Dell G3 コンピューター。

ES6 クラス class - 糖衣構文

class (クラス) はオブジェクトのテンプレートとして導入され、クラスを介してクラスを定義できます。キーワード。その本質は糖衣構文とみなすことができる関数であり、オブジェクト プロトタイプの記述方法をより明確にし、オブジェクト指向プログラミングの構文に近づけます。

そのクラスは他の言語とは異なります。このクラスは依然としてプロトタイプ継承の実装に基づいています。言語の機能には影響しません。ただ書きやすく、読みやすくなっています。

ES6 クラスは糖衣構文であると言われるのはなぜですか?

以下の内容を質問しながら読んでみましょう:

  • なぜES6 クラスは糖鎖構文であると言われていますか?
  • クラスはプロトタイプの糖鎖構文ですか?
  • それでは、プロトタイプを使用してクラスの糖鎖構文を実装するにはどうすればよいでしょうか?

1. プロトタイプに基づく OOP

まずプロトタイプの例を見てみましょう:

function Person (name, sex) {
	this.name = name
	this.sex = sex
}
 
function Man (name) {
	this.name = name
}
 
Man.prototype = new Person('', 'male')
 
let Jy = new Man('Jy')
 
console.log(Jy.name, Jy.sex) // Jy, male

これは非常に単純なプロトタイプの例を使用します。人は名前と性別を持ち、Man は男性の性別を持つ人、Jy は男性です。まずこの例を覚えて、以下のクラスを使用してこの例を書き直します。

ヒント: new、this などは、Java の OOP に近づけるために Brendan Eich によって追加されました。興味のある読者は、関連情報を自分で確認してください。

2. ES6 クラスの OOP

class Person {
	constructor (name, sex) {
		this.name = name
		this.sex = sex
	}
}
 
class Man extends Person {
	constructor (name) {
		super('', 'male')
		this.name = name
	}
}
 
let Jy = new Man('Jy')
 
console.log(Jy.name, Jy.sex) // Jy, 'male'

この例を書き直し、class、constructor、extends、super を使用します。 、ES6 仕様でそれらに何が行われるかについて具体的に説明しましょう。

3. プロトタイプを使用して実装されたクラス OOP (ES6 仕様)

ES6 より前では、JS オブジェクトは実際には属性のコレクションであり、属性はキーと値のペア (キー、値) のセット。キーには文字列またはシンボルを指定できます。値には、データ属性の特性値とアクセサーの特性値が含まれます。

普通の属性で良いとのことですが、オブジェクトの下にメソッドはないのでしょうか?どのようにして属性のコレクションになったのでしょうか?

実は、ES5仕様に登場するメソッドの定義は「プロパティの値である関数」であり、オブジェクトの関数属性に過ぎず、メソッドとは言えません。 ES6 が登場するまで、メソッドは仕様に含まれませんでした。

ES3 の OOP に関連するものとしては、プロトタイプ、new、this、コンストラクター、instanceof が考えられますが、標準の __proto__ プロパティさえありません。

幸いなことに、ES5 では、これを完成させるための多くのメソッドが追加されました:

  • Object.defineProperty
  • Object.freeze
  • Object.create
  • Object.getPrototypeOf
  • Object.setPrototypeOf
  • isPrototypeOf
  • ....

みましょうコードをもう一度見てください:

let obj = {
	name: 'Jy',
	speak () { // Note: it's not speak: function () {}
		console.log(this.name, super.name)
	}
}
 
obj.speak() // Jy, undefined
 
Object.setPrototypeOf(obj,  { name: 'super' })
 
obj.speak() // Jy, super
 
let speak = obj.speak
speak() // undefined, super

obj.speak は ES6 ではメソッドとして定義されています。属性 [[homeObject]] があり、homeObject はメソッドが呼び出されるオブジェクトを指します (これはオブジェクトにバインドされた内部スロットであるため、変更することはできません。これはハードコードされているのと同じです。

それでは、homeObject は何に役立つのでしょうか? super と密接に関係しており、super キーワードを解析すると homeObject のプロトタイプが見つかります。

簡単に言うと、次の 2 つの式に要約できます。

  • let homeObj = Method[[HomeObject]] = obj
  • super = Object .getPrototypeOf(homeObj )

#注: homeObject は内部スロットに静的にバインドされますが、super は動的に検索されます。

スーパーについて話した後は、extends とコンストラクターについて話しましょう。

class A extends B { }
 
class A extends B {
	constructor (...args) {
		super(args)
	}
}
 
class C extends null { }

extends は主に次の 2 つのことを行います。

  • Object.setPrototypeOf(A , B)
  • Object.setPrototypeOf(A.prototype, B.prototype)

親クラスが null の場合は、Object.setPrototypeOf(C.prototype, null)

上記のコードの最初の部分と 2 番目の部分の違いは、コンストラクターが明示的に宣言されているかどうかです。では、これら 2 つのコードは同等ですか?答えは同等です。

仕様では次のように定義されています:

コードの 3 番目の部分は null を継承します。構文エラーは報告されませんが、呼び出しが行われるため、新しい C を作成できません。 null のコンストラクターですが、null にはコンストラクターがありません。

これを見ると、ES6のクラスoopや仕様宣言はすべてプロトタイプを使って動作しているので、クラスはプロトタイプの構文糖であると言えるでしょうか?

4. babel によってコンパイルされたクラス

実際のプロジェクトでは、ES6 および 7 のコードをコンパイルするために babel がよく使用されるため、このセクションでは次の Babel コンパイル済みコードを分析してみましょう。プロトタイプを使用して OOP を実装するというテーマをより適切に示すために、一部のエラー報告と型検出関連のコードが省略されています。

コンパイル前:

class A extends B {}
 
console.log(new A)

コンパイル後:

"use strict";
 
function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
    return o.__proto__ || Object.getPrototypeOf(o);
  };
  return _getPrototypeOf(o);
}
 
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      writable: true,
      configurable: true
    }
  });
  if (superClass) _setPrototypeOf(subClass, superClass);
}
 
function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
    o.__proto__ = p;
    return o;
  };
  return _setPrototypeOf(o, p);
}
 
var A =
  /*#__PURE__*/
  function (_B) {
    _inherits(A, _B);
 
    function A() {
 
      return _getPrototypeOf(A).apply(this, arguments);
    }
 
    return A;
  }(B);
 
console.log(new A());

我们重点看_inherits 方法,跟我们上述说的extends做的两件事是一样的:

  • Object.setPrototypeOf(subClass, superClass)
  • Object.setPrototypeOf(subClass.prototype, superClass.prototype)

只不过它采用的是Object.create方法,这两个方法的区别可以去MDN上查看。

再看function A内部,其实就是执行了B的构造器函数来达到super(arguments)的效果, 这个与规范:如果没有显示声明constructor会自动加上constructor是一致的。

5. 总结

至此,我们终于理解了为什么class是原型的语法糖以及如何使用原型来实现class这一语法糖。

但切记我们使用原型的目的并不是来模拟class oop的,prototype based的oop应该用prototype去理解而不是class。

ES6的class oop 是不完备的 ,例如abstract class 、interface、private等都还没有,不过有些功能已经在提案中了,大家可以拥抱它,或者TypeScript是个不错的选择,如果你的项目中使用到了TS, 欢迎你到评论区分享你的感受。

【相关推荐:javascript视频教程编程视频

以上がes6クラスは糖衣構文ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。