NỘI DUNG BÀI VIẾT
Trong OOP,
getter
và
setter
là các hàm hoặc phương thức được dùng để lấy ra hoặc thiết lập giá trị cho các biến. Khái niệm
getter
và
setter
rất phổ biến trong ngôn ngữ lập trình. Hầu hết các ngôn ngữ lập trình bậc cao đều có bộ cú pháp để thực hiện getter và setter, bao gồm cả JavaScript.
Trong bài viết này, chúng ta sẽ tìm hiểu
getter
,
setter
là gì và cách khởi tạo cũng như sử dụng chúng trong JavaScript.
getter và setter và việc đóng gói
getter
và
setter
luôn được đề cập đến cùng với việc đóng gói. Chúng ta có thể hiêu việc đóng gói theo 2 cách :
- Thiết lập bộ 3
data
–getter
–setter
để truy cập và sửa đổi dữ liệu. Định nghĩa này rất hữu ích khi một số hành động kiểu như làvalidation
– phải được thực hiện trên dữ liệu trước khi lưu hoặc là xem nó.getter
vàsetter
cung cấp điều này. - Có một định nghĩa chặt chẽ hơn (theo mình thì nó cũng dễ hiểu và quen thuộc hơn với mọi người), theo đó thì đóng gói được thực hiện để ẩn dữ liệu, làm cho thành phần được đóng gói sẽ không thể bị truy cập, ngoại trừ việc thông qua các
getter
vàsetter
.
Khởi tạo getter và setter
1. Dưới dạng 1 phương thức
Vì
getter
và
setter
về cơ bản là những hàm để lấy ra hoặc thay đổi giá trị, có nhiều cách để khởi tạo vào sử dụng chúng. Ví dụ :
var obj = { foo: 'this is the value of foo', getFoo: function() { return this.foo; }, setFoo: function(val) { this.foo = val; } } console.log(obj.getFoo()); // "this is the value of foo" obj.setFoo('hello'); console.log(obj.getFoo()); // "hello"
Đây là cách đơn giản nhất để khởi tạo
getter
và
setter
. Có 1 thuộc tính
foo
và 2 phương thức :
-
setFoo
để gán giá trị cho thuộc tínhfoo
. -
getFoo
để trả về giá trị của thuộc tínhfoo
2. Với từ khóa
Có một cách tốt hơn và chúng ta cũng hay dùng hơn đó là sử dụng từ khóa
get
,
set
.
Để khởi tạo một
getter
thì bạn chỉ việc thêm từ khóa
get
vào ngay trước khai báo hàm để biến hàm đó thành
getter
; cũng tương tự với từ khóa
set
trước hàm
setter
. Cú pháp như sau :
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; } } // getter console.log(obj.foo); // "this is the value of foo" // setter obj.foo = 'hello'; console.log(obj.foo); // "hello"
Lưu ý là dữ liệu chỉ có thể lưu trữ dưới một cái tên thuộc tính (ở ví dụ trên là
fooVal
) .
Cách khai báo nào sẽ tốt hơn?
Trong 2 cách khai báo trên thì cách khai báo với từ khóa là tốt hơn, bạn có thể sử dụng toán tử gán để
set
dữ liệu và toán tử
.
để
get
dữ liệu. Cách này cũng sẽ rất quen thuộc với bạn.
Chống ghi đè
Nếu vẫn phải sử dụng cách khai báo thứ nhất, hãy thay đổi các thuộc tính
getter
và
setter
trở thành read-only, bằng cách khởi tạo chúng bằng
Object.defineProperties
.
Các thuộc tính được khai báo thông qua
Object.defineProperties
,
Object.defineProperty
và
Reflect.defineProperty
tự động được cấu hình
writable: false
.
Ví dụ :
/* Overwrite prevention */ var obj = { foo: 'this is the value of foo' }; Object.defineProperties(obj, { 'getFoo': { value: function () { return this.foo; } }, 'setFoo': { value: function (val) { this.foo = val; } } }); obj.getFoo = 66; // getFoo is not going to be overwritten! console.log(obj.getFoo()); // "this is the value of foo"
Hoạt động bên trong getter và setter
Một khi đã tạo ra
getter
và
setter
thì bạn có thể tiếp tục và thực hiện các thao tác trên dữ liệu trước khi thay đổi hoặc là trả về.
Trong ví dụ bên dưới, trong hàm
getter
, dữ liệu được gắn thêm đằng sau một chuỗi trước khi trả về. Trong
setter
thì thực hiện xác nhận dữ liệu có phải là
number
hay không trước khi gán giá trị.
var obj = { n: 67, get id() { return 'The ID is: ' + this.n; }, set id(val) { if (typeof val === 'number') this.n = val; } } console.log(obj.id); // "The ID is: 67" obj.id = 893; console.log(obj.id); // "The ID is: 893" obj.id = 'hello'; console.log(obj.id); // "The ID is: 893"
Bảo vệ dữ liệu với getter và setter
Chúng ta cùng chuyển sang tìm hiểu cách ẩn dữ liệu khi nhìn từ bên ngoài vào với sự giúp đỡ của
getter
và
setter
.
Dữ liệu với getter và setter không được bảo vệ
Việc chúng ta khai báo các
getter
và
setter
không đồng nghĩa là dữ liệu chỉ có thể truy cập và thay đổi thông qua các phương thức đó. Dưới đây là một ví dụ dữ liệu được thay đổi trực tiếp :
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; } } obj.fooVal = 'hello'; console.log(obj.foo); // "hello"
Ta thay đổi trực tiếp thuộc tính
fooVal
mà không thông qua
setter
, dữ liệu ban đầu chúng ta đặt trong
obj
đã mất .
Để tránh việc đó, ta cần bảo vệ dữ liệu của mình, có thể là thêm giới hạn phạm vi mà dữ liệu của bạn sẵn dùng. Cùng tìm hiều kỹ hơn về phần này nhé.
1. Phạm vi block
Bên trong block thì dữ liệu sẽ được định nghĩa bằng từ khóa
let
để hạn chế phạm vi dữ liệu.
Một
block
có thể được tạo ra bằng cách đặt mã của bạn bên trong cặp dấu
{}
(và khi dùng thế này thì nên có comment để mọi người hiểu)
/* BLOCK SCOPE, leave the braces alone! */ { let fooVal = 'this is the value of foo'; var obj = { get foo() { return fooVal; }, set foo(val) { fooVal = val } } } fooVal = 'hello'; // not going to affect the fooVal inside the block console.log(obj.foo); // "this is the value of foo"
Và bây giờ thì việc thay đổi hay là tạo mới
fooVal
bên ngoài block kia sẽ không ảnh hưởng gì đến
fooVal
được gọi trong
getter
và
setter
cả.
2. Phạm vi hàm
Một cách quen thuộc hơn để bảo về dữ liệu bằng phạm vi là dữ cho dữ liệu bên trong hàm và trả về một đối tượng với
getter
và
setter
trong hàm đó. Ví dụ :
function myobj(){ var fooVal = 'this is the value of foo'; return { get foo() { return fooVal; }, set foo(val) { fooVal = val } } } fooVal = 'hello'; // not going to affect our original fooVal var obj = myobj(); console.log(obj.foo); // "this is the value of foo"
Các đối tượng trả về bởi hàm
myobj()
được lưu trong
obj
, và sau đó
obj
được dùng để gọi
getter
,
setter
.
3. Không sử dụng scope
Chúng ta cũng có cách khác bảo vệ dữ liệu để tránh việc bị ghi đè mà không sử dụng
scope
. Logic ở đây là : làm thế nào bạn có thể thay đổi được một dữ liệu nếu bạn không biết những gì được gọi đến?
Hãy xem ví dụ dưới đây :
var obj = { s89274934764: 'this is the value of foo', get foo() { return this.s89274934764; }, set foo(val) { this.s89274934764 = val; } } console.log(obj.foo); // "this is the value of foo"
Với một tên biến như trên (hoặc là bạn cũng có thể dùng giá trị hoặc ký tự ngẫu nhiên) thì mục đích chính là để dữ cho dữ liệu vô hình nếu nhìn từ bên ngoài, để xem hoặc cập nhật thì bạn sẽ phải thông qua
getter
và
setter
.
Khi nào bạn nên sử dụng getter và setter
Nếu dữ liệu của các bạn có thể nhìn thấy từ bên ngoài là tốt, bạn vẫn cần sử dụng
getter
và
setter
, chỉ để đóng gói nó với code mà thực hiện một số hoạt động trên nó? Tôi trả lời là có.
Tham khảo:
https://viblo.asia/p/getter-va-setter-trong-javascript-3P0lPOX4Zox
https://www.hongkiat.com/blog/getters-setters-javascript/
Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.