Đối tượng JavaScript

Tram Ho

JavaScript series

Chương này giới thiệu các đối tượng trong JavaScript.

A. Object

1. Overview

Well, về object các bạn có thể đã biết về khái niệm object (đối tượng). Object đại diện cho một thực thể, gồm các thông tin chứa trong những thuộc tính (property) và có những hành vi là những phương thức (method). Object trong JS cũng thế.

Các tính chất của OOP không thể hiện rõ ràng như một số ngôn ngữ khác, điển hình như Java.

2. Creating object

Có nhiều cách để tạo ra một object trong JS:

  • Tạo single object bằng object literal (initializer)
  • Tạo object bằng new và Object constructor
  • Tạo object bằng một constructor tùy chỉnh
  • Tạo từ class

Từ ES5 có thể dùng method Object.create() để tạo object.

Object literal

Đây là cách đơn giản và rõ ràng nhất để tạo object. Nhược điểm là nó chỉ tạo ra một object duy nhất, nên muốn tạo ra nhiều object cùng lúc cần dùng cách khác.

Chú ý hai cách viết trên, cách 1 ngắn gọn dùng cho object nhỏ, đơn giản. Cách 2 tuy dài hơn nhưng rõ ràng và phù hợp cho các object phức tạp. Và chú ý luôn cả cách mình đặt space trong các dòng.

New keyword & Object constructor

Cách này sử dụng từ khóa new và Object constructor. Đầu tiên dòng 1 chỉ tạo ra object rỗng, và rồi bạn mới thêm các thuộc tính vào bằng cách gán value cho nó.

Define a constructor

Cách này khá giống cách ở trên, nhưng chúng ta sử dụng constructor tùy chỉnh thay vì Object. Constructor chỉ là một function bình thường, gán giá trị cho this, và được gọi bằng new keyword.

Cách này trước khi JS có class được dùng khá nhiều, vì nó có thể tạo ra được nhiều object tương tự nhau.

Instantiate from class

ES6 bổ sung thêm từ khóa class, nên có thể khởi tạo nhiều object từ một class đã định nghĩa. Việc này tương tự trong các ngôn ngữ khác nên dễ làm quen hơn.

Phần về class sẽ bàn sâu hơn trong các phần tiếp theo.

Object.create()

Object.create() là method rất hay để tạo object khi bạn muốn chọn prototype cho object tạo ra.

3. Display object

Đôi khi chúng ta muốn xem tất cả giá trị của object, thì có thể dùng một số cách sau.

  • Hiển thị một property
  • Dùng vòng lặp for in để hiển thị tất cả property và value của chúng
  • Dùng method Object.keys() hoặc Object.values() để lấy tất cả key hoặc value của object dạng array.
  • Dùng method toString() hoặc JSON.stringify().
  • Xuất ra bằng console.log().

Tuy nhiên, trong phần này mình muốn giới thiệu tới các bạn method console.table() sẽ hiển thị toàn bộ object dưới dạng bảng trong console rất tiện. Nhược điểm là các method sẽ không hiển thị trong bảng, nên nó rất phù hợp khi bạn muốn xem nhanh dữ liệu trong object.

B. Property & method

1. Property

Thuộc tính (property) là nơi chứa dữ liệu cho object, mỗi thuộc tính là một cặp key: value. Giữa tên thuộc tính keyvalue tách nhau bởi dấu hai chấm, và giữa các thuộc tính với nhau cách nhau bởi dấu phẩy.

Trong JS, các phương thức cũng được xem là thuộc tính, như trên.

Tên thuộc tính đặc biệt có thể chứa khoảng trắng, khi đó cần dùng dấu nháy bao lại.

Access property

Có hai cách để truy cập thuộc tính, nghĩa là đọc value hoặc thay đổi value của nó.

Cách 1 sử dụng cú pháp object.property được dùng nhiều hơn.

Cách 2 sử dụng cách truy cập như mảng object["property"], trong đó property là tên của thuộc tính. Cách 2 thường dùng khi tên property có dấu cách, hoặc truy cập các computed name property.

Add, delete property

Để thêm thuộc tính vào object, chỉ cần gán một value cho nó.

Để xóa thuộc tính, dùng toán tử delete.

Chú ý, việc thêm và xóa thuộc tính có thể bị chặn vì object không cho phép (các phần sau sẽ bàn tới). Trong strict mode, delete bị cấm. Và nên hạn chế thêm hoặc xóa thuộc tính của object.

For in loop

Để duyệt qua các thuộc tính, sử dụng vòng lặp for in.

Biến prop mỗi lần lặp sẽ nhận lần lượt các property name, và sử dụng john[prop] để lấy giá trị property đang trỏ tới. Chú ý phải dùng john[prop] mà không phải john.prop, vì prop ở đây không phải là thuộc tính cố định.

Property attributes

Thuộc tính trong JS có ba tính chất đặc biệt:

  • Writable: cho phép thay đổi thuộc tính hay không
  • Enumerable: cho phép thuộc tính được tìm thấy bằng vòng lặp for in hoặc các method như Object.keys hoặc Object.values.
  • Configurable: cho phép cấu hình thuộc tính, nghĩa là thay đổi được các thông số như writable, enumerable trên. Chỉ những thuộc tính có configurable mới có thể xóa bởi toán tử delete.

Mọi thuộc tính trong JS đều có thể đọc được, do đó muốn bảo mật thì nên dùng closure thay thế.

2. Method

Method là những function bên trong object, và các method cũng là property có value là function ẩn danh.

Hai method trên khai báo hai cách khác nhau. Cách 1 là cách cũ trước đây, và cách 2 là cách viết method gọn hơn được giới thiệu từ ES6.

This keyword

Trong method, từ khóa this chỉ tới owner của method, là chính object chứa nó. Do đó, chúng ta có thể gọi các method và property khác bên trong cùng object nhờ this.

Trường hợp ngoại lệ khi gọi method bằng call() hoặc apply(). Khi đó this sẽ là đối số đầu tiên (dạng object) khi gọi call(), apply().

Add method

Để thêm method vào, tương tự như thêm thuộc tính, chỉ cần gán value là một function ẩn danh cho nó.

Delete method

Các method của object không thể bị xóa.

C. Accessor, constructor & prototype

1. Accessor

Getter & setter

Từ ES5 đã giới thiệu getter và setter, nhờ đó thay vì truy cập thẳng vào thuộc tính thì phải thông qua một accessor (đối tượng truy cập). Sử dụng accessor tương tự như property, khác biệt ở cách chúng hoạt động:

  • Thuộc tính bình thường đọc ghi trực tiếp vào property.
  • Accessor sử dụng getter để đọc và setter để ghi mỗi khi có yêu cầu tới thuộc tính. Setter và getter là các hàm, bạn có thể viết code cho nó để chúng xử lý dữ liệu trước khi đưa ra ngoài (getter) hoặc ghi vào property (setter).

Thuộc tính _name là thuộc tính thực, trong đó name là accessor tạo bởi get name()set name(). Ở đây mình viết thuộc tính thực có dấu underscore _ phía trước.

Khi đọc ghi trên accessor, thì tùy vào hành động (đọc hoặc ghi) thì getter (đọc) hoặc setter (ghi) sẽ được gọi tương ứng.

Một thuộc tính có thể không có getter, setter, có một trong hai hoặc cả hai đều được.

Cấu trúc getter, setter

Getter có cấu trúc một method, không tham số và return một giá trị.

Setter là method nhận vào một value, và không return giá trị nào.

Tên của method ở getter và setter là tên accessor phải trùng nhau, để trỏ tới cùng một accessor. Và tên accessor được sử dụng như tên property thông thường.

Tại sao dùng accessor

Một số lý do để sử dụng accessor như sau:

  • Cú pháp đơn giản và giống nhau giữa property và method
  • Đảm bảo luồng dữ liệu vào ra object hợp lệ và chuẩn
  • Giúp thực hiện các thao tác khác trong nền khi truy cập thuộc tính

Accessor cho property có sẵn

Có thể định nghĩa thêm accessor cho một thuộc tính đã có mà không cần chỉnh sửa code object đó bằng method Object.defineProperty().

Method trên nhận vào ba tham số. Tham số 1 là object chỉ định, tham số 2 là tên thuộc tính. Tham số 3 là một object đại diện cho thuộc tính.

Cú pháp chỉ định getter setter của method trên hơi khác với định nghĩa trực tiếp trong object. Có thể hiểu tham số 3 của Object.defineProperty() là một object cấu hình, có hai method là get()set() đại diện cho getter và setter, do đó nó không có tên mà chính get và set là tên rồi.

Method này sẽ được nói kĩ hơn trong những phần sau.

2. Constructor

Constructor chỉ là function dùng tạo object, và được gọi với từ khóa new. Constructor sử dụng từ khóa this đại diện object sẽ tạo ra, this sẽ được tự động return ra ngoài constructor thành một object mới.

Với constructor, có thể tạo ra nhiều object có cấu trúc giống nhau nhanh chóng, mặc dù giá trị trong đó có thể khác nhau.

Constructor thường nhận vào các tham số, tương ứng với các thuộc tính sẽ có của object, và được gán cho this như trên.

Chú ý nên sử dụng constructor đúng với chức năng của nó. Không nên gọi function với new, cũng như không gọi constructor như function bình thường (không có new).

This keyword

Tùy thuộc cách gọi function, gọi bình thường thì this là owner của function (object window hoặc object chứa method). Nếu gọi function có từ khóa new, thì function thành constructor, thì this mang ý nghĩa khác.

This trong constructor là object tạm, khi constructor thực hiện xong thì object tạm sẽ được tạo ra và được gán tham chiếu cho một biến object.

Add property, method in constructor

Thêm như object bình thường, nhưng sử dụng this cho object chứ không phải bản thân constructor.

Type constructors

Các kiểu dữ liệu có những constructor xây dựng sẵn, ví dụ object thì có Object(), string thì có String(), tương tự với Number(), Boolean(),… kể cả kiểu primitive.

Tuy có thể tạo primitive type với new, nhưng không nên dùng vì nhiều hạn chế.

3. Prototype

JS trước ES6 thì chưa có khái niệm class, nên việc thực hiện kế thừa (inheritance) giữa các object phải thông qua prototype. Có thể hiểu nếu hai object có cùng prototype, thì chúng sẽ có những thuộc tính và phương thức chung của prototype đó.

Prototype thường có trong constructor, khi đó mọi object tạo ra từ constructor này sẽ chia sẻ chung các member của prototype.

Mọi object đều có một thuộc tính tên là __proto__, chứa các thuộc tính và phương thức có trong prototype của nó.

Thêm thuộc tính vào prototype tương tự như object. Thường thì prototype chỉ chứa method chứ ít khi có property.

Chú ý, khi thay đổi, thêm thuộc tính vào thuộc tính __proto__ của object bất kì cũng sẽ ảnh hưởng tới toàn bộ object khác có sử dụng chung prototype (hoặc constructor).

D. Object constructor

Object trong JS là một constructor dùng để tạo ra các object. Bên cạnh đó, nó cũng chứa một số thuộc tính và phương thức xử lý cho mọi object, ví dụ như.

Các method xử lý object của Object được sử dụng như trên, giống như ví dụ về các static method.

1. Object properties

Chỉ có hai property của Object là lengthprototype.

Thuộc tính length luôn trả về 1, và prototype cho phép bổ sung thêm các thành viên khác cho các object tạo ra từ Object.

2. Object methods

Tất cả các method của Object đều nhận vào một tham số là một object nào đó.

Object.create method

Tạo một object mới với prototype của một object khác được chỉ định. Chi tiết đã nói ở phần trên.

Object.keys & Object.values method

Method Object.keys() trả về một mảng các tên thuộc tính (còn gọi là key vì thuộc tính có tên: value, tương ứng với key: value).

Nếu method không truyền object mà nhận vào string hoặc array, thì danh sách key lấy ra có dạng 1, 2, 3,… (vì string và array đánh index cho từng phần tử, nên key sẽ là index).

Method Object.values() tương tự, nhưng nó trả về các value của thuộc tính thay vì key.

Object.getOwnPropertyNames method

Method Object.getOwnPropertyNames() tương tự như Object.keys(), dùng lấy ra một mảng các tên thuộc tính (key hoặc property name). Hoặc có một cách nữa là dùng vòng lặp for in để lặp qua mọi thuộc tính.

Khác biệt ở chỗ Object.getOwnPropertyNames() lấy ra cả những thuộc tính enumerable là false, còn Object.keys()for in không tìm thấy những thuộc tính không enumerable.

Object.seal & Object.freeze method

Method Object.seal() ngăn chặn việc xóa các thuộc tính của object. Method Object.freeze() cũng không cho phép xóa, và cũng không cho phép thay đổi value của bất kì thuộc tính nào trong object.

Có hai method khác để kiểm tra là Object.isSealed() để kiểm tra object có được sealed không, và Obect.isFrozen() để kiểm tra tình trạng freeze của object.

Object.preventExtensions & Object.isExtensible

Method Object.preventExtensions() không cho phép thêm thuộc tính và method vào method.

Và dùng method Object.isExtensible() để kiểm tra object có thể thêm thuộc tính và method vào không. Nếu đã dùng Object.preventExtensions() ở trên thì Object.isExtensible() là false.

Object,is method

Dùng method Object.is() để kiểm tra xem hai value có giống nhau hay không:

  • Cả hai có cùng giá trị, undefined, null, hoặc true, hoặc false, hoặc string giống nhau.
  • Đối với object (tính cả array), thì method trả về true khi cả hai có cùng địa chỉ bộ nhớ (hoặc tham chiếu cùng một object).
  • Đối với số, thì nó phải giống nhau hoàn toàn, ví dụ +0-0 là khác nhau, Number.NaNNaN là giống nhau, Number.POSITIVE_INFINITYInfinity giống nhau.

Nói chung thì Object.is() và hai phép so sánh ===== khá giống nhau, nhưng Object.is() giống với === hơn. Ví dụ.

Khác biệt giữa toán tử === và method Object.is() nằm ở chỗ chúng so sánh các số, như đề mục thứ 3 ở trên. Hãy xem ví dụ sau.

JS thật vl 😄.

Object.assign method

Dùng method này để sao chép đối tượng nguồn (source) sang đối tượng đích (target) và trả về đối tượng đích. Nếu source không phải object, JS sẽ cố chuyển nó thành object bằng cách dùng wrapper cho nó.

Method Object.assign() sao chép theo đúng nghĩa copy dữ liệu các thuộc tính và method vào object khác, còn phép gán = chỉ là gán tham chiếu thôi. Và chú ý các thuộc tính enumerable là false và accessor sẽ không được sao chép.

Chú ý, method trên copy từ source vào target, các thuộc tính trùng tên đã có trong target sẽ bị source ghi đè lên (do đó b có giá trị là 20 chứ không phải 30, vì b của source đã ghi đè lên b của target).

Thêm một ví dụ nữa đơn giản hơn về sao chép đối tượng, nghĩa là tạo đối tượng mới có nội dung y chang đối tượng đã có.

Như trên sau khi copy thì hai object có nội dung giống nhau hoàn toàn. Nhưng thường người ta sẽ lợi dụng tính trả về của Object.assign(), mà rút gọn đi dòng khai báo thứ 2 ở trên.

Method Object.assign() có thể nhận nhiều hơn một source, khi đó cấu trúc sẽ có dạng như sau.

Chú ý object source đứng sau sẽ ghi đè lên thuộc tính trùng tên đã có.

Object.defineProperty method

Dùng method này để định nghĩa một thuộc tính mới, hoặc thay đổi các thông số của thuộc tính đã có.

Method trên nhận vào 3 tham số:

  • Tham số 1 là object sẽ được thêm thuộc tính vào
  • Tham số 2 là tên thuộc tính (có thể là thuộc tính mới hoặc đã có rồi)
  • Tham số 3 là descriptor, là một object chứa các thông số của thuộc tính. Chú ý tới tham số này.

Bạn có thể chỉ định value, getter, setter cho thuộc tính thông qua descriptor (tham số 3). Desriptor là một object quy định các thông số của thuộc tính như writable, enumerable, configurable, getter và setter như ví dụ sau.

Method Object.definePropery() cũng được dùng để modify lại những thuộc tính đã có, có thể là thay đổi value, thêm getter hoặc setter cho nó, hoặc sửa lại các thông số khác.

Object.defineProperties method

Method Object.defineProperties() khá giống với Object.defineProperty(), khác biệt ở chỗ method số nhiều sẽ định nghĩa liên tiếp nhiều thuộc tính.

Ví dụ về cú pháp method này, gồm tham số 1 là object cần sửa, tham số 2 là một object lớn, chứa bên trong là những cặp thuộc tính, có tên thuộc tính là tên object, value của thuộc tính là một object descriptor như trên phần Object.defineProperty() đã nói.

Others

Trên đây chỉ là những thuộc tính và method cơ bản, hay dùng của Object, có thể tìm hiểu nhiều thêm nữa.

E. Classes

1. Overview

Từ phiên bản ES6 (ECMAScript 2015) đã giới thiệu thêm class. Class về bản chất là một function constructor theo đúng nghĩa (typeof một class sẽ ra function 😃), nhưng được viết với cú pháp gần với những ngôn ngữ khác hơn.

Definition

Cú pháp định nghĩa class như sau.

Trong class luôn có một method constructor, là nơi định nghĩa các thuộc tính. Còn những method khác được định nghĩa ở ngoài (cùng cấp với constructor).

Việc tạo object từ class tương tự như constructor, có từ khóa new như trong ví dụ trên.

Inheritance

ES6 bổ sung thêm từ khóa extends để kế thừa class.

Để truy cập ngược lên lớp cha, sử dụng từ khóa super. Chú ý, khi kế thừa constructor lớp con thì luôn luôn gọi constructor lớp cha (nếu có) ở đầu tiên.

Hoisting

Class thì không được hoisting, mặc dù nó cũng là function.

Strict mode

Cú pháp viết class luôn phải tuân thủ strict mode, điều này là bắt buộc.

2. Methods

Constructor

Constructor là một method đặc biệt, là nơi khởi tạo các thuộc tính cũng như kế thừa lớp cha. Constructor luôn được tự động gọi mỗi khi tạo object mới từ class.

Một class chỉ có duy nhất một constructor, nếu không chỉ định constructor thì JS sẽ tự thêm constructor ẩn không làm gì cả.

Constructor có các tham số như của function constructor, nhận những giá trị được truyền vào khi tạo object với new.

Method

Method cũng không có gì để nói nhiều. Trong khi các thuộc tính định nghĩa bên trong constructor, thì các method sẽ được viết cùng cấp với constructor.

Method cũng có hai cách viết, cách mới ES6 và cách cũ như trình bày ở trên.

Static method

Các static method sẽ được gọi từ class thay vì gọi từ object. Chỉ cần thêm từ khóa static vào đầu khai báo method.

Vì static có từ ES6, nên nó phải dùng cách khai báo method mới của ES6, là static func() {} chứ không phải static func: function () {}.

Static method thường dùng để viết các tập method xử lý chính đối tượng đó, vì vậy tham số nhận vào là object tạo ra từ class đó.

3. Property

Định nghĩa property trong class thì nó phải ở trong constructor.

Đối với getter và setter thì tương tự method.

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo