diff options
Diffstat (limited to 'files/vi/web/javascript/reference/classes/index.html')
-rw-r--r-- | files/vi/web/javascript/reference/classes/index.html | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/files/vi/web/javascript/reference/classes/index.html b/files/vi/web/javascript/reference/classes/index.html new file mode 100644 index 0000000000..02a693e6fc --- /dev/null +++ b/files/vi/web/javascript/reference/classes/index.html @@ -0,0 +1,410 @@ +--- +title: Classes +slug: Web/JavaScript/Reference/Classes +tags: + - Class trong ES6 + - JavaScript + - biểu thức class + - class declarations + - class expressions + - class trong javasript + - khai báo class + - thừa kế +translation_of: Web/JavaScript/Reference/Classes +--- +<div>{{JsSidebar("Classes")}}</div> + +<div>Lớp (class) trong JavaScript được giới thiệu trong ECMAScript 2015 chủ yếu là cú pháp cải tiến (syntactical sugar) dựa trên nền tảng thừa kế nguyên mẫu (prototypal inheritance) sẵn có trong JavaScript. Cú pháp class <em>không</em> giới thiệu mô hình thừa kế hướng đối tượng mới cho JavaScript.</div> + +<h2 id="Định_nghĩa_class">Định nghĩa class</h2> + +<p>Thực tế các class giống như các "function đặc biệt", và cũng giống như bạn có thể định nghĩa hàm biểu thức (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function">function expressions</a>) và khai báo hàm (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function">function declarations</a>), cú pháp class có hai thành phần: <strong>biểu thức class</strong> (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/class">class expressions</a>) và <strong>khai báo class</strong> (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class">class declarations</a>).</p> + +<h3 id="Khai_báo_class">Khai báo class</h3> + +<p>Một cách để định nghĩa class là sử dụng khai báo lớp <strong>class declaration</strong>. Để khai báo một class, bạn sử dụng từ khóa <code>class</code> với tên của class đằng sau (ví dụ "Rectangle" như dưới đây).</p> + +<pre class="brush: js">class Rectangle { + constructor(height, width) { + this.height = height; + this.width = width; + } +}</pre> + +<h4 id="Hoisting_(sự_kéo_lên)">Hoisting (sự kéo lên)</h4> + +<p>Một sự khác biệt quan trọng giữa <strong>khai báo hàm</strong> và <strong>khai báo class</strong> mà các bạn cần chú ý đó là khai báo hàm được kéo lên đầu ({{Glossary("Hoisting", "hoisted")}}), và khai báo class thì không. Bạn cần khai báo class của bạn trước tiên sau đó mới có thể gọi và sử dụng nó, ngược lại nếu viết code giống như phía dưới đây thì sẽ xảy ra lỗi {{jsxref("ReferenceError")}}:</p> + +<pre class="brush: js example-bad">var p = new Rectangle(); // ReferenceError + +class Rectangle {} +</pre> + +<h3 id="Biểu_thức_class">Biểu thức class</h3> + +<p>Một biểu thức class là một cách khác để khai báo một class. Biểu thức class có thể có tên hoặc không tên. Biểu thức class nếu có tên, thì tên đó chỉ được nhìn thấy bên trong thân class. (Tuy nhiên từ bên ngoài nó có thể được lấy ra thông qua thuộc tính {{jsxref("Function.name", "name")}} của class (không phải instance)).</p> + +<pre class="brush: js">// biểu thức class không tên +var Rectangle = class { + constructor(height, width) { + this.height = height; + this.width = width; + } +}; + +console.log(Rectangle.name); +// output: "Rectangle" (tên của biến được gán) + +// biểu thức class có tên +var Rectangle = class Rectangle2 { + constructor(height, width) { + this.height = height; + this.width = width; + } +}; + +console.log(Rectangle.name); +// output: "Rectangle2" (tên phía sau từ khóa class) +</pre> + +<div class="blockIndicator note"> +<p><strong>Lưu ý</strong>: Các biểu thức class cũng theo quy luật hoisting như của {{anch("Class declarations")}} đề cập ở trên.</p> +</div> + +<h2 id="Thân_class_và_định_nghĩa_phương_thức">Thân class và định nghĩa phương thức</h2> + +<p>Phần thân của một class là phần nằm trong dấu ngoặc nhọn <code>{}</code>. Tại đó bạn có thể định nghĩa các thành phần của class như phương thức (method) hoặc hàm khởi tạo (constructor).</p> + +<h3 id="Strict_mode">Strict mode</h3> + +<p>Các phần tử khai báo trong <em>class declarations</em> và <em>class expressions đã </em>được thực hiện ở chế độ<em> </em><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a> như constructor, static tương tự đối với prototype methods, setter, getter functions. (Nếu bạn chưa hiểu chế độ strict mode thì hãy tìm hiểu thêm <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode">tại đây</a>).</p> + +<h3 id="Constructor_(hàm_khởi_tạo)">Constructor (hàm khởi tạo)</h3> + +<p>Hàm khởi tạo (constructor) là một hàm đặc biệt, nhiệm vụ của nó là khởi tạo một đối tượng cho một class. Trong một class chỉ có thể tồn tại duy nhất một hàm khởi tạo, nghĩa là bạn chỉ có thể khai báo duy nhất một hàm với tên "constructor". Nếu bạn cố gắng làm ngược lại (khai báo nhiều hơn một hàm constructor) thì sẽ xuất hiện lỗi {{jsxref("SyntaxError")}}.</p> + +<p>Một constructor có thể sử dụng từ khóa super để gọi tới hàm constructor của class cha.</p> + +<h3 id="Phương_thức_Prototype">Phương thức Prototype</h3> + +<p>Xem thêm <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions">định nghĩa hàm</a>.</p> + +<pre class="brush: js">class Rectangle { + constructor(height, width) { + this.height = height; + this.width = width; + } + + get area() { + return this.calcArea(); + } + + calcArea() { + return this.height * this.width; + } +} + +const square = new Rectangle(10, 10); + +console.log(square.area);</pre> + +<h3 id="Phương_thức_Static_(phương_thức_tĩnh)">Phương thức Static (phương thức tĩnh)</h3> + +<p>Từ khóa <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static">static</a></code> định nghĩa một hàm static (hàm tĩnh) trong một class. Nếu muốn gọi hàm static này thì bạn không cần gọi chúng thông qua các <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#The_object_(class_instance)" title='An example of class instance is "var john = new Person();"'>instantiating</a> của class đó và bạn cũng không thể gọi chúng thông qua cách khởi tạo class. Hàm static thường được sử dụng vào mục đích tạo ra một hàm tiện ích (có thể gọi là hàm dùng chung) cho cả một ứng dụng.</p> + +<pre class="brush: js">class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + static distance(a, b) { + const dx = a.x - b.x; + const dy = a.y - b.y; + + return Math.hypot(dx, dy); + } +} + +const p1 = new Point(5, 5); +const p2 = new Point(10, 10); + +console.log(Point.distance(p1, p2));</pre> + +<h3 id="Boxing_với_phương_thức_prototype_và_static">Boxing với phương thức prototype và static</h3> + +<p>Khi hai hàm static và prototype được gọi trực tiếp (không cần tạo đối tượng có giá trị "this") thì bây giờ bên trong hàm gọi, giá trị this sẽ là <strong><code>undefine.</code></strong> Autoboxing sẽ không xảy ra. Hành vi này sẽ giống nhau ngay cả khi chúng ta viết code ở strict mode bởi vì tất cả các hàm, phương thức, hàm khởi tạo, setter và getter đều thực thi mặc định ở strict mode. Chính vì vậy nếu chúng ta không chỉ định giá trị "this" thì giá trị của "this" sẽ là <strong><code>undefined.</code></strong></p> + +<pre class="brush: js">class Animal { + speak() { + return this; + } + static eat() { + return this; + } +} + +let obj = new Animal(); +obj.speak(); // Animal {} +let speak = obj.speak; +speak(); // undefined + +Animal.eat() // class Animal +let eat = Animal.eat; +eat(); // undefined +</pre> + +<p>Nếu chúng ta chỉnh sửa code như trên bằng cách sửa dụng prototype thì autoboxing sẽ tự động hiểu rằng giá trị this bấy giờ là dựa trên cái hàm được gọi. Tham khảo code bên dưới.</p> + +<pre class="brush: js">function Animal() { } + +Animal.prototype.speak = function() { + return this; +} + +Animal.eat = function() { + return this; +} + +let obj = new Animal(); +let speak = obj.speak; +speak(); // global object + +let eat = Animal.eat; +eat(); // global object +</pre> + +<h2 id="Tạo_lớp_con_với_extends">Tạo lớp con với extends</h2> + +<p>Từ khóa <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends">extends</a> </code>được sử dụng trong <em>class declarations</em> hoặc <em>class expressions </em>để tạo ra một class con kế thừa từ một class sẵn có (class cha).</p> + +<pre class="brush: js">class Animal { + constructor(name) { + this.name = name; + } + + speak() { + console.log(this.name + ' makes a noise.'); + } +} + +class Dog extends Animal { + speak() { + console.log(this.name + ' barks.'); + } +} + +var d = new Dog('Mitzie'); +d.speak(); // Mitzie barks. +</pre> + +<p>Nếu có một constructor trong lớp con (sub-class), nó cần gọi hàm super() trước khi có thể sử dụng "this".</p> + +<p>Một cách khác cũng có thể gọi và mở rộng hàm có sẵn là dùng prototype:</p> + +<pre class="brush: js">function Animal (name) { + this.name = name; +} + +Animal.prototype.speak = function () { + console.log(this.name + ' makes a noise.'); +} + +class Dog extends Animal { + speak() { + console.log(this.name + ' barks.'); + } +} + +var d = new Dog('Mitzie'); +d.speak(); // Mitzie barks. +</pre> + +<p>Cần lưu ý rằng các class không thể extend một object bình thường trong javascript (regular object). Do đó nếu bạn muốn kế thừa một hàm từ object bình thường này, bạn cần thay thế và sử dụng {{jsxref("Object.setPrototypeOf()")}}:</p> + +<pre class="brush: js">var Animal = { // regular object + speak() { + console.log(this.name + ' makes a noise.'); + } +}; + +class Dog { // + constructor(name) { + this.name = name; + } +} + +Object.setPrototypeOf(Dog.prototype, Animal);// Nếu bạn không làm điều này khi gọi hàm speak thì sẽ sinh ra lỗi + +var d = new Dog('Mitzie'); // đối tượng của class Dog +d.speak(); // Mitzie makes a noise. +</pre> + +<h2 id="Species">Species</h2> + +<p>Bạn có thể muốn trả về các đối tượng {{jsxref("Array")}} trong mảng của class <code>MyArray</code>. Mô hình species sẽ cho phép bạn ghi đè lên các hàm khởi tạo mặc định.</p> + +<p>Ví dụ, khi sử dụng những phương thức như là {{jsxref("Array.map", "map()")}} điều đó sẽ trả về giá trị khởi tạo mặc định, bạn muốn những phương thức đó trả về một mảng đối tượng của Array, thay vì đối tượng của <code>MyArray. </code>{{jsxref("Symbol.species")}} sẽ cho phép bạn thực hiện điều này:</p> + +<pre class="brush: js">class MyArray extends Array { + // Overwrite species to the parent Array constructor + static get [Symbol.species]() { return Array; } +} + +var a = new MyArray(1,2,3); +var mapped = a.map(x => x * x); + +console.log(mapped instanceof MyArray); // false +console.log(mapped instanceof Array); // true +</pre> + +<h2 id="Gọi_class_cha_sử_dụng_super">Gọi class cha sử dụng <code>super</code></h2> + +<p>Từ khóa <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super">super</a></code> dùng để gọi một hàm có sẵn ở đối tượng cha.</p> + +<pre class="brush: js">class Cat { + constructor(name) { + this.name = name; + } + + speak() { + console.log(this.name + ' makes a noise.'); + } +} + +class Lion extends Cat { + speak() { + super.speak(); + console.log(this.name + ' roars.'); + } +} + +var l = new Lion('Fuzzy'); +l.speak(); +// Fuzzy makes a noise. +// Fuzzy roars. + +</pre> + +<h2 id="Mix-ins">Mix-ins</h2> + +<p>Abstract subclasses or <em>mix-ins</em> are templates for classes. An ECMAScript class can only have a single superclass, so multiple inheritance from tooling classes, for example, is not possible. The functionality must be provided by the superclass.</p> + +<p>A function with a superclass as input and a subclass extending that superclass as output can be used to implement mix-ins in ECMAScript:</p> + +<p>Tập hợp các class con hoặc mix-ins được gọi là khuôn mẫu cho các class. Trong ECMAScript một class chỉ có thể có một lớp cha, vì vậy để thừa kế từ tập hợp các class (kế thừa nhiều class) là điều không thể. Các chức năng phải được cung cấp bởi lớp mà nó kế thừa (cung cấp bởi lớp cha).</p> + +<p>Một hàm mà đã được định nghĩa ở lớp cha và lớp con muốn kế thừa và mở rộng hàm đó ra thì có thể sự dụng các lệnh mix-ins trong ECMAScript như sau:</p> + +<pre class="brush: js">var calculatorMixin = Base => class extends Base { + calc() { } +}; + +var randomizerMixin = Base => class extends Base { + randomize() { } +}; +</pre> + +<p>Một class để sử dụng mix-ins này có thể viết code như thế này:</p> + +<pre class="brush: js">class Foo { } +class Bar extends calculatorMixin(randomizerMixin(Foo)) { }</pre> + +<h2 id="Đặc_điểm_kỹ_thuật">Đặc điểm kỹ thuật</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Initial definition.</td> + </tr> + <tr> + <td>{{SpecName('ES2016', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2016')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ES2017', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2017')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td></td> + </tr> + </tbody> +</table> + +<h2 id="Tính_tương_thích_với_các_trình_duyệt">Tính tương thích với các trình duyệt</h2> + +<p>{{CompatibilityTable}}</p> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Edge</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatChrome(42.0)}}<sup>[1]</sup><br> + {{CompatChrome(49.0)}}</td> + <td>{{CompatGeckoDesktop(45)}}</td> + <td>13</td> + <td>{{CompatNo}}</td> + <td>{{CompatOpera(43.0)}}</td> + <td>{{CompatSafari(9.0)}}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>IE Mobile</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + <th>Chrome for Android</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatGeckoMobile(45)}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatUnknown}}</td> + <td>9</td> + <td>{{CompatChrome(56.0)}}</td> + </tr> + </tbody> +</table> +</div> + +<p>[1] Yêu cầu chế độ strict (luôn chạy javascript ở chế độ này). Tắt strict mode trong flag "Enable Experimental JavaScript", mặc định flag này bị vô hiệu hóa.</p> + +<h2 id="Xem_thêm">Xem thêm</h2> + +<ul> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Functions">Functions</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/class"><code>class</code> declaration</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Operators/class"><code>class</code> expression</a></li> + <li>{{jsxref("Operators/super", "super")}}</li> + <li><a href="https://hacks.mozilla.org/2015/07/es6-in-depth-classes/">Blog post: "ES6 In Depth: Classes"</a></li> +</ul> |