Bài viết này tham khảo từ bài viết Ultimate Guide to Getters and Setters in JavaScript 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
– 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 get và set, 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
– 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ởitạ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 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ó.
(nguồn viblo.asia)