jsのクラスを従来のクラスパターンに変換するコンストラクターを詳しく解説(例付き)
この記事では、js のクラスを従来のクラス パターンに変換するコンストラクターについて詳しく説明します (例を示します)。必要な方は参考にしていただければ幸いです。あなたは助けてくれました。
序文
JS のプロトタイプ ベースの「クラス」は、フロントエンドの職業に転職したプログラマーを常に驚かせてきましたが、クラス キーワードを使用した定義の出現です。従来のモデルに近いことにより、一部のフロントエンド同僚が非常に残念に思い、「独自の JS を返してください」、「実体のないものをいくつか構築してください」、「クラスがないので、人の授業に頼る」、さらには「転職した」など。結局のところ、感情があるのは正常のことであり、新しい知識を得るにはより多くの時間とエネルギーを費やす必要があり、ただ目を閉じて楽しむことができるものではありません。
しかし、歴史の軸は前に進み続けます。授業に関して確かなことの 1 つは、面接官に「お願いします、私が理解していないのではなく、単に私が理解していないだけです」とは言えないということです。 「理解したくない。質問を変更してください!」 class は単なる構文糖ですが、extends は継承を非常によく改善します。一方、将来「クラス」に登場する可能性のある新しい機能は、コンストラクターではなくクラスによって実行される必要があります。それが将来どのように美しくなるかは誰にもわかりません。さあ、この熱々の黒砂糖と生姜のスープをゆっくり飲みましょう
1 クラス
ECMAScript にはクラスの概念がありません。プロトタイプに基づいてコンストラクターによって生成されます。動的プロパティとメソッドを使用します。ただし、国際標準に準拠し、それをよりシンプルかつエレガントに説明するために、「クラス」という言葉が引き続き使用されます。したがって、JS クラスはコンストラクターと同等です。 ES6 クラスは単なる糖衣構文であり、その定義によって生成されるオブジェクトは依然としてコンストラクターです。ただし、コンストラクター パターンと区別するために、これをクラス パターンと呼びます。クラスの学習にはコンストラクターとプロトタイプ オブジェクトの知識が必要です。詳細については、Baidu で検索してください。
// --- 使用构造函数 function C () { console.log('New someone.'); } C.a = function () { return 'a'; }; // 静态方法 C.prototype.b = function () { return 'b'; }; // 原型方法 // --- 使用class class C { static a() { return 'a'; } // 静态方法 constructor() { console.log('New someone.'); } // 构造方法 b() { return 'b'; } // 原型方法 };
1.1 変数との比較
キーワード クラスは、関数を定義するキーワード関数と似ており、宣言と式 (匿名および名前付き) の 2 つの定義方法があります。宣言式によって定義された変数の性質は、関数の性質とは異なります。これらは事前に解析されず、変数の昇格がなく、グローバル スコープにリンクされず、一時的なデッド ゾーンがあります。クラス定義によって生成される変数はコンストラクターであるため、クラスは即時実行モードで作成できます。
// --- 声明式 class C {} function F() {} // --- 匿名表达式 let C = class {}; let F = function () {}; // --- 命名表达式 let C = class CC {}; let F = function FF() {}; // --- 本质是个函数 class C {} console.log(typeof C); // 'function' console.log(Object.prototype.toString.call(C)); // '[object Function]' console.log(C.hasOwnProperty('prototype')); // true // --- 不存在变量提升 C; // 报错,不存在C。 class C {} // 存在提前解析和变量提升 F; // 不报错,F已被声明和赋值。 function F() {} // --- 自执行模式 let c = new (class { })(); let f = new (function () { })();
1.2 オブジェクトとの比較
クラスの内容 ({} 内) の形式は、オブジェクト リテラルの形式と似ています。ただし、クラスのコンテンツで定義できるのはメソッドのみであり、属性は定義できません。メソッドの形式は関数の省略形のみであり、メソッドをカンマで区切ることはできません。メソッド名には、括弧で囲まれた式またはシンボル値を指定できます。メソッドは、コンストラクター メソッド (コンストラクター メソッド)、プロトタイプ メソッド (コンストラクターのプロトタイプ属性に存在)、および静的メソッド (コンストラクター自体に存在) の 3 つのカテゴリに分類されます。
class C { // 原型方法a a() { console.log('a'); } // 构造方法,每次生成实例时都会被调用并返回新实例。 constructor() {} // 静态方法b,带static关键字。 static b() { console.log('b'); } // 原型方法,带括号的表达式 ['a' + 'b']() { console.log('ab'); } // 原型方法,使用Symbol值 [Symbol.for('s')]() { console.log('symbol s'); } } C.b(); // b let c = new C(); c.a(); // a c.ab(); // ab c[Symbol.for('s')](); // symbol s
プロパティは直接定義できません。表現クラスはプロトタイプや静的プロパティを持つことができません。クラスを解決するとコンストラクターが形成されるため、コンストラクターにプロパティを追加するのと同じように、クラスにプロパティを追加するだけです。読み取り専用プロパティの定義にはゲッター関数のみを使用する方が簡単であり、推奨されます。プロパティを直接設定できないのはなぜですか?技術が未熟なのでしょうか?当局はある考えを伝えたいと考えているのでしょうか?それとも作者が勝手に投げかけた質問なのでしょうか?
// --- 直接在C类(构造函数)上修改 class C {} C.a = 'a'; C.b = function () { return 'b'; }; C.prototype.c = 'c'; C.prototype.d = function () { return 'd'; }; let c = new C(); c.c; // c c.d(); // d // --- 使用setter和getter // 定义只能获取不能修改的原型或静态属性 class C { get a() { return 'a'; } static get b() { return 'b'; } } let c = new C(); c.a; // a c.a = '1'; // 赋值没用,只有get没有set无法修改。
1.3 コンストラクターとの比較
以下は、コンストラクターとクラスを使用して同じ機能を実現するコードです。直感的には、クラスはコードを簡素化し、コンテンツをより集約します。コンストラクター メソッドの本体は、コンストラクター関数の関数本体と同等です。このメソッドが明示的に定義されていない場合は、新しいインスタンスを返すために空のコンストラクター メソッドがデフォルトで追加されます。 ES5 と同様に、新しいインスタンスの代わりに別のオブジェクトを返すようにカスタマイズすることもできます。
// --- 构造函数 function C(a) { this.a = a; } // 静态属性和方法 C.b = 'b'; C.c = function () { return 'c'; }; // 原型属性和方法 C.prototype.d = 'd'; C.prototype.e = function () { return 'e'; }; Object.defineProperty(C.prototype, 'f', { // 只读属性 get() { return 'f'; } }); // --- 类 class C { static c() { return 'c'; } constructor(a) { this.a = a; } e() { return 'e'; } get f() { return 'f'; } } C.b = 'b'; C.prototype.d = 'd';
クラスは関数ですが、インスタンスを生成できるのは new を通じてのみであり、直接呼び出すことはできません。クラス内で定義されたすべてのメソッドは列挙可能ではありませんが、コンストラクター自体とプロトタイプに追加されたプロパティとメソッドは列挙可能です。クラス内で定義されたメソッドはデフォルトで厳密モードになっており、明示的に宣言する必要はありません。上記の 3 つの点により、クラスの厳密性が高まりますが、プライベート プロパティとメソッドを直接定義する方法はまだありません。
// --- 能否直接调用 class C {} C(); // 报错 function C() {} C(); // 可以 // --- 是否可枚举 class C { static a() {} // 不可枚举 b() {} // 不可枚举 } C.c = function () {}; // 可枚举 C.prototype.d = function () {}; // 可枚举 isEnumerable(C, ['a', 'c']); // a false, c true isEnumerable(C.prototype, ['b', 'd']); // b false, d true function isEnumerable(target, keys) { let obj = Object.getOwnPropertyDescriptors(target); keys.forEach(k => { console.log(k, obj[k].enumerable); }); } // --- 是否为严格模式 class C { a() { let is = false; try { n = 1; } catch (e) { is = true; } console.log(is ? 'true' : 'false'); } } C.prototype.b = function () { let is = false; try { n = 1; } catch (e) { is = true; } console.log(is ? 'true' : 'false'); }; let c = new C(); c.a(); // true,是严格模式。 c.b(); // false,不是严格模式。
メソッドの前に static キーワードを追加すると、そのメソッドがクラス自体に存在し、インスタンスから直接アクセスできないことを示します。静的メソッドの this はクラス自体を指します。静的メソッドとプロトタイプ メソッドは異なるオブジェクト上にあるため、同じ名前を持つことができます。 ES6 では、new の背後にあるコンストラクターまたはクラスを参照する新しいコマンド new.target が追加されています。詳細については、次の例を参照してください。
// --- static class C { static a() { console.log(this === C); } a() { console.log(this instanceof C); } } let c = new C(); C.a(); // true c.a(); // true // --- new.target // 构造函数 function C() { console.log(new.target); } C.prototype.a = function () { console.log(new.target); }; let c = new C(); // 打印出C c.a(); // 在普通方法中为undefined。 // --- 类 class C { constructor() { console.log(new.target); } a() { console.log(new.target); } } let c = new C(); // 打印出C c.a(); // 在普通方法中为undefined。 // --- 在函数外部使用会报错 new.target; // 报错
2 extends
ES5 の古典的な継承方法は、寄生結合継承であり、サブクラスは親クラスのインスタンスとプロトタイプのプロパティとメソッドをそれぞれ継承します。 ES6 における継承の本質は同じですが、次のコードに示すように実装方法が変更されています。プロトタイプの継承は extends キーワードを使用した従来の言語に近い形式であり、インスタンスの継承は super を呼び出すことでサブクラスの形成を完了することであることがわかります。表面的には、このアプローチはより統一され、簡潔になっています。
class C1 { constructor(a) { this.a = a; } b() { console.log('b'); } } class C extends C1 { // 继承原型数据 constructor() { super('a'); // 继承实例数据 } }
2.1 与构造函数对比
使用extends继承,不仅仅会将子类的prototype属性的原型对象(__proto__)设置为父类的prototype,还会将子类本身的原型对象(__proto__)设置为父类本身。这意味着子类不单单会继承父类的原型数据,也会继承父类本身拥有的静态属性和方法。而ES5的经典继承只会继承父类的原型数据。不单单是财富,连老爸的名气也要获得,不错不错。
class C1 { static get a() { console.log('a'); } static b() { console.log('b'); } } class C extends C1 { } // 等价,没有构造方法会默认添加。 class C extends C1 { constructor(...args) { super(...args); } } let c = new C(); C.a; // a,继承了父类的静态属性。 C.b(); // b,继承了父类的静态方法。 console.log(Object.getPrototypeOf(C) === C1); // true,C的原型对象为C1 console.log(Object.getPrototypeOf(C.prototype) === C1.prototype); // true,C的prototype属性的原型对象为C1的prototype
ES5中的实例继承,是先创造子类的实例对象this,再通过call或apply方法,在this上添加父类的实例属性和方法。当然也可以选择不继承父类的实例数据。而ES6不同,它的设计使得实例继承更为优秀和严谨。
在ES6的实例继承中,是先调用super方法创建父类的this(依旧指向子类)和添加父类的实例数据,再通过子类的构造函数修饰this,与ES5正好相反。ES6规定在子类的constructor方法里,在使用到this之前,必须先调用super方法得到子类的this。不调用super方法,意味着子类得不到this对象。
class C1 { constructor() { console.log('C1', this instanceof C); } } class C extends C1 { constructor() { super(); // 在super()之前不能使用this,否则报错。 console.log('C'); } } new C(); // 先打印出C1 true,再打印C。
2.2 super
关键字super比较奇葩,在不同的环境和使用方式下,它会指代不同的东西(总的说可以指代对象或方法两种)。而且在不显式的指明是作为对象或方法使用时,比如console.log(super),会直接报错。
作为函数时。super只能存在于子类的构造方法中,这时它指代父类构造函数。
作为对象时。super在静态方法中指代父类本身,在构造方法和原型方法中指代父类的prototype属性。不过通过super调用父类方法时,方法的this依旧指向子类。即是说,通过super调用父类的静态方法时,该方法的this指向子类本身;调用父类的原型方法时,该方法的this指向该(子类的)实例。而且通过super对某属性赋值时,在子类的原型方法里指代该实例,在子类的静态方法里指代子类本身,毕竟直接在子类中通过super修改父类是很危险的。
很迷糊对吧,疯疯癫癫的,还是结合着代码看吧!
class C1 { static a() { console.log(this === C); } b() { console.log(this instanceof C); } } class C extends C1 { static c() { console.log(super.a); // 此时super指向C1,打印出function a。 this.x = 2; // this等于C。 super.x = 3; // 此时super等于this,即C。 console.log(super.x); // 此时super指向C1,打印出undefined。 console.log(this.x); // 值已改为3。 super.a(); // 打印出true,a方法的this指向C。 } constructor() { super(); // 指代父类的构造函数 console.log(super.c); // 此时super指向C1.prototype,打印出function c。 this.x = 2; // this等于新实例。 super.x = 3; // 此时super等于this,即实例本身。 console.log(super.x); // 此时super指向C1.prototype,打印出undefined。 console.log(this.x); // 值已改为3。 super.b(); // 打印出true,b方法的this指向实例本身。 } }
2.3 继承原生构造函数
使用构造函数模式,构建继承了原生数据结构(比如Array)的子类,有许多缺陷的。一方面由上文可知,原始继承是先创建子类this,再通过父类构造函数进行修饰,因此无法获取到父类的内部属性(隐藏属性)。另一方面,原生构造函数会直接忽略call或apply方法传入的this,导致子类根本无法获取到父类的实例属性和方法。
function MyArray(...args) { Array.apply(this, args); } MyArray.prototype = Array.prototype; // MyArray.prototype.constructor = MyArray; let arr = new MyArray(1, 2, 3); // arr为对象,没有储存值。 arr.push(4, 5); // 在arr上新增了0,1和length属性。 arr.map(d => d); // 返回数组[4, 5] arr.length = 1; // arr并没有更新,依旧有0,1属性,且arr[1]为5。
创建类的过程,是先构造一个属于父类却指向子类的this(绕口),再通过父类和子类的构造函数进行修饰。因此可以规避构造函数的问题,获取到父类的实例属性和方法,包括内部属性。进而真正的创建原生数据结构的子类,从而简单的扩展原生数据类型。另外还可以通过设置Symbol.species属性,使得衍生对象为原生类而不是自定义子类的实例。
class MyArray extends Array { // 实现是如此的简单 static get [Symbol.species]() { return Array; } } let arr = new MyArray(1, 2, 3); // arr为数组,储存有1,2,3。 arr.map(d => d); // 返回数组[1, 2, 3] arr.length = 1; // arr正常更新,已包含必要的内部属性。
需要注意的是继承Object
的子类。ES6改变了Object
构造函数的行为,一旦发现其不是通过new Object()
这种形式调用的,构造函数会忽略传入的参数。由此导致Object
子类无法正常初始化,但这不是个大问题。
class MyObject extends Object { static get [Symbol.species]() { return Object; } } let o = new MyObject({ id: 1 }); console.log(o.hasOwnPropoty('id')); // false,没有被正确初始化
以上がjsのクラスを従来のクラスパターンに変換するコンストラクターを詳しく解説(例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PHP と Vue: フロントエンド開発ツールの完璧な組み合わせ 今日のインターネットの急速な発展の時代において、フロントエンド開発はますます重要になっています。 Web サイトやアプリケーションのエクスペリエンスに対するユーザーの要求がますます高まっているため、フロントエンド開発者は、より効率的で柔軟なツールを使用して、応答性の高いインタラクティブなインターフェイスを作成する必要があります。フロントエンド開発の分野における 2 つの重要なテクノロジーである PHP と Vue.js は、組み合わせることで完璧なツールと見なされます。この記事では、PHP と Vue の組み合わせと、読者がこれら 2 つをよりよく理解し、適用できるようにするための詳細なコード例について説明します。

jQuery は、Web 開発で広く使用されている古典的な JavaScript ライブラリで、イベントの処理、DOM 要素の操作、Web ページ上でのアニメーションの実行などの操作を簡素化します。 jQueryを使っていると要素のクラス名を置き換える場面がよくありますが、この記事ではその実践的な方法と具体的なコード例を紹介します。 1.removeClass() メソッドと addClass() メソッドを使用する jQuery には、削除用の RemoveClass() メソッドが用意されています。

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

フロントエンド開発のインタビューでは、HTML/CSS の基本、JavaScript の基本、フレームワークとライブラリ、プロジェクトの経験、アルゴリズムとデータ構造、パフォーマンスの最適化、クロスドメイン リクエスト、フロントエンド エンジニアリング、デザインパターン、新しいテクノロジーとトレンド。面接官の質問は、候補者の技術スキル、プロジェクトの経験、業界のトレンドの理解を評価するように設計されています。したがって、候補者はこれらの分野で自分の能力と専門知識を証明するために十分な準備をしておく必要があります。

Django は、迅速な開発とクリーンなメソッドを重視した Python で書かれた Web アプリケーション フレームワークです。 Django は Web フレームワークですが、Django がフロントエンドなのかバックエンドなのかという質問に答えるには、フロントエンドとバックエンドの概念を深く理解する必要があります。フロントエンドはユーザーが直接対話するインターフェイスを指し、バックエンドはサーバー側プログラムを指し、HTTP プロトコルを通じてデータと対話します。フロントエンドとバックエンドが分離されている場合、フロントエンドとバックエンドのプログラムをそれぞれ独立して開発して、ビジネス ロジックとインタラクティブ効果、およびデータ交換を実装できます。

Go 言語は、高速で効率的なプログラミング言語として、バックエンド開発の分野で広く普及しています。ただし、Go 言語をフロントエンド開発と結びつける人はほとんどいません。実際、フロントエンド開発に Go 言語を使用すると、効率が向上するだけでなく、開発者に新たな視野をもたらすことができます。この記事では、フロントエンド開発に Go 言語を使用する可能性を探り、読者がこの分野をよりよく理解できるように具体的なコード例を示します。従来のフロントエンド開発では、ユーザー インターフェイスの構築に JavaScript、HTML、CSS がよく使用されます。

PHP コードを記述するとき、クラスを使用するのは非常に一般的な方法です。クラスを使用すると、関連する関数とデータを 1 つのユニットにカプセル化できるため、コードがより明確になり、読みやすく、保守しやすくなります。この記事では、PHPClass の使用法を詳しく紹介し、クラスを適用して実際のプロジェクトでコードを最適化する方法を読者がよりよく理解できるように、具体的なコード例を示します。 1. クラスの作成と使用 PHP では、キーワード class を使用してクラスを定義し、クラス内のプロパティとメソッドを定義できます。

Django: フロントエンド開発とバックエンド開発の両方を処理できる魔法のフレームワークです。 Django は、効率的でスケーラブルな Web アプリケーション フレームワークです。 MVCやMTVなど複数のWeb開発モデルをサポートし、高品質なWebアプリケーションを簡単に開発できます。 Django はバックエンド開発をサポートするだけでなく、フロントエンド インターフェイスを迅速に構築し、テンプレート言語を通じて柔軟なビュー表示を実現します。 Django はフロントエンド開発とバックエンド開発をシームレスに統合するため、開発者は学習に特化する必要がありません。
