Proxy trong ES6

Mấy ngày trước bạn mình giới thiệu rằng game EverWing có thể hack bằng cách thay đổi giá trị javascript của nó. Oops, mình cũng khá thích vọc vọc mấy trò hack cỏn con như vậy. Thể là mình thử và quả nhiên hack được. Nhưng cứ phải thay đổi giá trị của object khá phiền phức. Mình suy nghĩ xem có cách nào custom lại behavious của object khi giá trị của property của nó thay đổi hay không. Và yeah, Proxy làm được.

Nếu bạn thấy font trên trang này lỗi, hãy cài extension của 12bit để font đẹp và dễ đọc hơn:
Tải bản dành cho Chrome
Tải bản dành cho Firefox.

Vậy là mình code một đoạn code rất đơn giản, tạo Proxy, đặt tên proxy đè lên tên object, thế là mọi thay đổi hoặc truy xuất trên object đó đều phải thông qua proxy của mình và mình có thể return lại bất cứ giá trị nào mà mình muốn, vậy là bá đạo game này một cách đơn giản.

Nhưng Proxy còn nhiều ứng dụng hữu ích khác nữa, hãy cùng mình tìm hiểu về Proxy trong bài viết này và mình là người mới học, kiến thức của mình cũng chưa vững và vì vậy hãy giúp mình chỉnh sửa những phần sai và bổ sung những phần còn thiếu bằng cách comment ở dưới nhé. Cảm ơn các bạn.

Proxy là gì?

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
 — MDN

Tên Proxy làm chúng ta nhớ lại mấy cái proxy bên mạng, nó đóng vai trò trung gian giữa người request và người nhận request. Cũng tương tự như vậy, Proxy trong ES6 này cũng đóng vai trò trung gian giữa target object và nơi thực thi. Dùng để custom các behavious của các operator cơ bản.

Proxy gồm có ba phần:

  1. Target object: Object mà bạn áp dụng proxy lên nó, nó chứa các dữ liệu thật.
  2. Traps: Các phương thức tương ứng dùng để hồi đáp lại các “yêu cầu” thao tác dữ liệu/gọi hàm trên Target.
  3. Handler: Là object chứa các traps.

Cú pháp

let p = new Proxy(target, handler)

Hãy cho một ví dụ để có hứng thú tiếp tục:

Target object chỉ là một object bình thường, chúng ta không bàn về nó ở đây, chỉ có handler và trap là khá mới lạ, trong đó Handler cũng chỉ là một object để chứa traps mà thôi.

Traps

Traps là các phương thức của handler, và được gọi ra để xử lý các yêu cầu truy cập lên targer.

Chúng ta có khá nhiều trap, các trap này đều là tùy chọn, nếu bạn không định nghĩa trap nào, thì các yêu cầu sẽ được chuyển qua cho target xử lý.

Chúng ta có các trap như sau:

  1. handler.getPrototypeOf()
  2. handler.setPrototypeOf()
  3. handler.isExtensible()
  4. handler.preventExtensions()
  5. handler.getOwnPropertyDescriptor()
  6. handler.defineProperty()
  7. handler.has()
  8. handler.get()
  9. handler.set()
  10. handler.deleteProperty()
  11. handler.ownKeys()
  12. handler.apply()
  13. handler.construct()

Các trap này sẽ được gọi ra tương ứng với các thao tác mà bạn gọi lên proxy. Như ở ví dụ đầu bài, khi lấy giá trị của property thì phương thức get sẽ được gọi kèm theo hai tham số là target và tên của property.

Lý thuyết thì có chừng đó, bây giờ chúng ta hãy thử làm một vài ví dụ.

Object Validation

Giả sử bạn muốn validate thuộc tính age của một object:

Bạn thể trực tiếp sửa code và ấn run để test các kết quả khác.

Cái này hữu ích khi bạn viết một thư viện mà bạn đòi hỏi developer phải tuân thủ những quy định nào đó.

Reactive DOM

Nếu dùng Vuejs bạn sẽ thấy tính năng reactive của nó trên DOM rất thú vị. Ví dụ như bạn thay đổi giá trị của biến và nó tự động cập nhật lại DOM. Bạn cũng hoàn toàn có thể làm điều đó với Proxy.

Thú vị nhỉ. Nhưng để làm được như Vuejs thì còn một chặn đường quá xa. Tương lai Vuejs cũng sẽ sử dụng proxy thay vì sử dụng Object.defineProperty(), và bạn không cần dùng hàm $set khi muốn thêm một property chưa được định nghĩa trước.

Cache

Trong vuejs cũng có cơ chế để cache giá trị của các phương thức. Giả sử chúng ta cũng gặp trường hợp tương tự, chúng ta có một phương thức cần rất nhiều tính toán, chúng ta cần cache lại kết quả nếu như tham số của hai lần gọi là giống hệt nhau:

Khi thực thi, thì Real Compute! chỉ chạy hai lần. Hai dòng console cuối cùng có thể bị browser hoặc RunkIt merge vào nhau, nhưng thực tế là hàm tính toán thực sự đã không được gọi, mà giá trị trong cache đã được gọi ra.

Đây chỉ là ví dụ thôi, chứ không thực sự có thể ứng dụng được vào project của bạn, nếu muốn dùng nó thì có lẽ bạn sẽ cần tải tiến nhiều hơn.

API Object

Làm API object cũng rất thú vị. Giả sử như bạn có một kết nối realtime, bạn muốn các object sync với nhau mà không cần phải gọi đi gọi làm phương thức emit. Lúc này bạn cần làm một proxy, và code phần update trong trap set. Phần này chỉ là ý tưởng, các bạn tự hiện thực.

Lời kết

Như bạn thấy trong ví dụ của mình có sử dụng Reflect, nó và proxy hỗ tương. Nếu proxy là để custom behavious thì Reflect dùng để thực hiện default behavious của target. Chúng ta sẽ nói về nó ở những bài viết sau.

Còn bây giờ hay tìm cái gì đó vui vui làm với Proxy đi nào. Đọc xong làm liền thì dễ hiểu hơn.

Cảm ơn các bạn đã theo dõi, xin nhờ các bạn đóng góp thêm ý kiến hoặc những ví dụ thú vị hơn về proxy.

Like what you read? Give Duoc Nguyen a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.