HomeLập trìnhJavaScriptLập trình meta...

Lập trình meta trong JavaScript là gì? Bằng tiếng Anh, xin vui lòng.


JavaScript có nhiều tính năng hữu ích mà hầu hết các nhà phát triển đều biết. Đồng thời, có một số viên ngọc ẩn có thể giải quyết các vấn đề thực sự khó khăn nếu bạn biết về chúng.

Lập trình meta trong JavaScript là một trong những khái niệm mà nhiều người trong chúng ta có thể không quen thuộc. Trong bài viết này, chúng ta sẽ tìm hiểu về Siêu lập trình và nó hữu ích như thế nào đối với chúng ta.

Với ES6 (ECMAScript 2015), chúng tôi có hỗ trợ cho ReflectProxy các đối tượng cho phép chúng ta thực hiện Siêu lập trình một cách dễ dàng. Trong bài viết này, chúng ta sẽ học cách sử dụng chúng với các ví dụ.

Metaprogramming không có gì ít hơn so với ma thuật trong lập trình! Làm thế nào về việc viết một chương trình đọc, sửa đổi, phân tích và thậm chí tạo ra một chương trình? Không phải điều đó nghe có vẻ phù thủy và mạnh mẽ sao?

elwIjsjlSeV2c9VBF07ZDHmurJ5_NdeIJ0bDOSNpNai644OhE90gDbGlyOnL4xea5D7S6s9M17V3w4h3zgpr8Q9sn3Ke8BuzPJySs4JI6J0v0jvgX6eSdalnFdULzTWh85IjQMFGOAYX-ymmA
Siêu lập trình là ma thuật

Wikipedia mô tả Siêu lập trình như thế này:

Metaprogramming là một kỹ thuật lập trình trong đó các chương trình máy tính có khả năng coi các chương trình khác là dữ liệu của chúng. Điều này có nghĩa là một chương trình có thể được thiết kế để đọc, tạo, phân tích hoặc chuyển đổi các chương trình khác và thậm chí tự sửa đổi chính nó trong khi chạy.

Nói một cách đơn giản, Siêu lập trình liên quan đến việc viết mã có thể

  • Tạo mã
  • Thao tác cấu trúc ngôn ngữ trong thời gian chạy. Hiện tượng này được gọi là Reflective Metaprogramming hoặc Reflection.

Reflection là một nhánh của Siêu lập trình. Reflection có ba nhánh con:

  1. nội quan: Mã có thể tự kiểm tra. Nó được sử dụng để truy cập các thuộc tính bên trong để chúng tôi có thể lấy thông tin cấp thấp trong mã của mình.
  2. Tự sửa đổi: Như tên gợi ý, mã có thể tự sửa đổi.
  3. cầu thay: Nghĩa đen của sự can thiệp là hành động thay mặt cho người khác. Trong siêu lập trình, sự can thiệp thực hiện chính xác như vậy bằng cách sử dụng các khái niệm như, bao bọc, bẫy, chặn.

ES6 cung cấp cho chúng tôi Reflect đối tượng (còn gọi là Reflect API) để đạt được Introspection. Các Proxy đối tượng của ES6 giúp chúng tôi với Intercession. Chúng ta sẽ không nói quá nhiều về Self-Modification vì chúng tôi muốn tránh xa nó càng nhiều càng tốt.

Đợi một chút! Nói rõ hơn, Lập trình meta không được giới thiệu trong ES6. Thay vào đó, nó đã có sẵn trong ngôn ngữ ngay từ đầu. ES6 đã làm cho nó dễ sử dụng hơn rất nhiều.

Bạn có nhớ eval? Chúng ta hãy xem nó đã được sử dụng như thế nào:

const blog = {
    name: 'freeCodeCamp'
}
console.log('Before eval:', blog);

const key = 'author';
const value="Tapas";
testEval = () => eval(`blog.${key} = '${value}'`);

// Call the function
testEval();

console.log('After eval magic:', blog);

Như bạn có thể nhận thấy, eval đã giúp tạo mã bổ sung. Trong trường hợp này, đối tượng blog đã được sửa đổi với một thuộc tính bổ sung tại thời điểm thực hiện.

Before eval: {name: freeCodeCamp}
After eval magic: {name: "freeCodeCamp", author: "Tapas"}

nội quan

Trước khi đưa vào Reflect object trong ES6, chúng tôi vẫn có thể xem xét nội tâm. Dưới đây là một ví dụ về cách đọc cấu trúc của chương trình:

var users = {
    'Tom': 32,
    'Bill': 50,
    'Sam': 65
};

Object.keys(users).forEach(name => {
    const age = users[name];
    console.log(`User ${name} is ${age} years old!`);
});

Ở đây chúng ta đang đọc users cấu trúc đối tượng và ghi lại khóa-giá trị trong một câu.

User Tom is 32 years old!
User Bill is 50 years old!
User Sam is 65 years old!

Tự sửa đổi

Hãy lấy một đối tượng blog có phương thức tự sửa đổi:

var blog = {
    name: 'freeCodeCamp',
    modifySelf: function(key, value) {blog[key] = value}
}

Các blog đối tượng có thể tự sửa đổi bằng cách thực hiện điều này:

blog.modifySelf('author', 'Tapas');

cầu thay

Intercession trong siêu lập trình có nghĩa là hành động hoặc thay đổi mọi thứ thay mặt cho ai đó hoặc cái gì khác. Trước ES6 Object.defineProperty() phương pháp có thể thay đổi ngữ nghĩa của một đối tượng:

var sun = {};

Object.defineProperty(sun, 'rises', {
    value: true,
    configurable: false,
    writable: false,
    enumerable: false
});

console.log('sun rises', sun.rises);
sun.rises = false;
console.log('sun rises', sun.rises);

Đầu ra:

sun rises true
sun rises true

Như bạn có thể thấy, các sun đối tượng đã được tạo như một đối tượng bình thường. Sau đó, ngữ nghĩa đã được thay đổi để nó không thể ghi được.

Đọc thêm  Kiểm tra loại JavaScript – Cách kiểm tra loại trong JS với typeof()

Bây giờ chúng ta hãy bắt đầu tìm hiểu ReflectProxy đồ vật với công dụng tương ứng của chúng.

Trong ES6, Reflect là một tính năng mới Global Object (như Toán học) cung cấp một số chức năng tiện ích. Một số chức năng này có thể thực hiện chính xác điều tương tự như các phương thức từ Object hoặc Function.

Tất cả các hàm này đều là các hàm Introspection, nơi bạn có thể truy vấn một số chi tiết nội bộ về chương trình trong thời gian chạy.

Dưới đây là danh sách các phương pháp có sẵn từ Reflect vật.

// Reflect object methods

Reflect.apply()
Reflect.construct()
Reflect.get()
Reflect.has()
Reflect.ownKeys()
Reflect.set()
Reflect.setPrototypeOf()
Reflect.defineProperty()
Reflect.deleteProperty()
Reflect.getOwnPropertyDescriptor()
Reflect.getPrototypeOf()
Reflect.isExtensible()

Nhưng chờ đã, đây là một câu hỏi: Tại sao chúng ta cần một đối tượng API mới khi những đối tượng này có thể đã tồn tại hoặc có thể được thêm vào Object hoặc Function?

Bối rối? Hãy cố gắng tìm ra điều này.

Tất cả trong một không gian tên

JavaScript đã hỗ trợ phản chiếu đối tượng. Nhưng các API này không được tổ chức dưới một không gian tên. Kể từ ES6, họ hiện đang ở dưới Reflect.

Tất cả các phương thức của đối tượng Reflect đều có bản chất tĩnh. Điều đó có nghĩa là bạn không phải khởi tạo đối tượng Reflect bằng cách sử dụng new từ khóa.

Đơn giản để sử dụng

Các introspection phương pháp của Object ném một ngoại lệ khi họ không hoàn thành thao tác. Đây là một gánh nặng bổ sung cho người tiêu dùng (lập trình viên) để xử lý ngoại lệ đó trong mã.

Bạn có thể thích xử lý nó như một boolean(true | false) thay vì sử dụng xử lý ngoại lệ. Đối tượng Reflect giúp bạn làm điều đó.

Đây là một ví dụ với Object.defineProperty:

 try {
        Object.defineProperty(obj, name, desc);
    } catch (e) {
        // Handle the exception
    }

Và với API phản ánh:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
 // failure (and far better)
}

Ấn tượng về chức năng First-Class

Chúng ta có thể tìm thấy sự tồn tại của một thuộc tính cho một đối tượng như (prop in obj). Nếu chúng ta cần sử dụng nó nhiều lần trong mã của mình, chúng ta phải tạo một hàm bằng cách gói mã này.

Trong ES6, Reflect API giải quyết vấn đề này bằng cách giới thiệu một chức năng hạng nhất, Reflect.has(obj, prop).

Hãy xem một ví dụ khác: Xóa thuộc tính đối tượng.

const obj = { bar: true, baz: false};

// We define this function
function deleteProperty(object, key) {
    delete object[key];
}
deleteProperty(obj, 'bar');

Với API phản ánh:

// With Reflect API
Reflect.deleteProperty(obj, 'bar');

Một cách đáng tin cậy hơn để sử dụng phương thức apply()

Các apply() phương thức trong ES5 giúp gọi một hàm với ngữ cảnh của một this giá trị. Chúng ta cũng có thể truyền các đối số dưới dạng một mảng.

Function.prototype.apply.call(func, obj, arr);
// or
func.apply(obj, arr);

Điều này kém tin cậy hơn vì func có thể là một đối tượng đã xác định riêng của mình apply phương pháp.

Đọc thêm  Toán tử logic JavaScript

Trong ES6, chúng tôi có một cách giải quyết vấn đề này đáng tin cậy và thanh lịch hơn:

Reflect.apply(func, obj, arr);

Trong trường hợp này, chúng tôi sẽ nhận được một TypeError nếu func không gọi được.

Giúp các loại suy tư khác

chúng tôi sẽ thấy điều này có nghĩa là gì khi chúng ta tìm hiểu về Proxy vật. Các phương thức Reflect API có thể được sử dụng với Proxy trong nhiều trường hợp sử dụng.

ES6 Proxy đối tượng giúp trong intercession.

Như tên cho thấy, một proxy đối tượng giúp hành động thay mặt cho một cái gì đó. Nó thực hiện điều này bằng cách ảo hóa một đối tượng khác. Ảo hóa đối tượng cung cấp các hành vi tùy chỉnh cho đối tượng đó.

Ví dụ: sử dụng đối tượng proxy, chúng ta có thể ảo hóa việc tra cứu thuộc tính đối tượng, gọi hàm, v.v. Chúng ta sẽ thấy một số trong số này chi tiết hơn dưới đây.

Dưới đây là một vài thuật ngữ hữu ích bạn cần nhớ và sử dụng:

  • Các target: Một đối tượng mà proxy cung cấp các hành vi tùy chỉnh.
  • Các handler: Nó là một đối tượng có chứa bẫy.
  • Các trap: Trap là một phương thức cung cấp quyền truy cập vào các thuộc tính của đối tượng đích. Điều này đạt được bằng cách sử dụng các phương pháp Reflect API. Mỗi phương thức bẫy được ánh xạ với các phương thức từ Reflect API.

Bạn có thể tưởng tượng nó một cái gì đó như thế này:

dòng chảy-1

Một người xử lý với một trap chức năng nên được xác định. Sau đó, chúng ta cần tạo một đối tượng Proxy bằng cách sử dụng trình xử lý và đối tượng đích. Đối tượng Proxy sẽ có tất cả các thay đổi với các hành vi tùy chỉnh được áp dụng.

Hoàn toàn ổn nếu bạn chưa hiểu rõ từ mô tả ở trên. Chúng ta sẽ nắm được nó thông qua mã và các ví dụ trong một phút.

Cú pháp tạo đối tượng Proxy như sau:

let proxy = new Proxy(target, handler);

Có nhiều bẫy proxy (chức năng xử lý) có sẵn để truy cập và tùy chỉnh đối tượng mục tiêu. Đây là danh sách của họ.

handler.apply()
handler.construct()
handler.get()
handler.has()
handler.ownKeys()
handler.set()
handler.setPrototypeOf()
handler.getPrototypeOf()
handler.defineProperty()
handler.deleteProperty()
handler.getOwnPropertyDescriptor()
handler.preventExtensions()
handler.isExtensible()

Lưu ý rằng mỗi cái bẫy có một ánh xạ với Reflect các phương thức của đối tượng. Điều này có nghĩa là bạn có thể sử dụng ReflectProxy cùng nhau trong nhiều trường hợp sử dụng.

Cách nhận các giá trị thuộc tính đối tượng không khả dụng

Hãy xem xét một ví dụ về một employee đối tượng và cố gắng in một số thuộc tính của nó:

const employee = {
    firstName: 'Tapas',
    lastName: 'Adhikary'
};

console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.org);
console.log(employee.fullName);

Đầu ra dự kiến ​​như sau:

Tapas
Adhikary
undefined
undefined

Bây giờ, hãy sử dụng đối tượng Proxy để thêm một số hành vi tùy chỉnh vào employee vật.

Bước 1: Tạo Trình xử lý sử dụng bẫy get

Chúng tôi sẽ sử dụng một cái bẫy gọi là get cho phép chúng tôi có được một giá trị thuộc tính. Đây là xử lý của chúng tôi:

let handler = {
    get: function(target, fieldName) {        

        if(fieldName === 'fullName' ) {
            return `${target.firstName} ${target.lastName}`;
        }

        return fieldName in target ?
            target[fieldName] :
                `No such property as, '${fieldName}'!`

    }
};

Trình xử lý trên giúp tạo giá trị cho fullName tài sản. Nó cũng thêm thông báo lỗi tốt hơn khi thiếu thuộc tính đối tượng.

Đọc thêm  Hàm thuần túy trong JavaScript là gì?

Bước 2: Tạo đối tượng proxy

Như chúng ta có mục tiêu employee đối tượng và trình xử lý, chúng ta sẽ có thể tạo một đối tượng Proxy như thế này:

let proxy = new Proxy(employee, handler);

Bước 3: Truy cập thuộc tính trên đối tượng Proxy

Bây giờ chúng ta có thể truy cập các thuộc tính của đối tượng employee bằng cách sử dụng đối tượng proxy, như sau:

console.log(proxy.firstName);
console.log(proxy.lastName);
console.log(proxy.org);
console.log(proxy.fullName);

Đầu ra sẽ là:

Tapas
Adhikary
No such property as, 'org'!
Tapas Adhikary

Lưu ý cách chúng tôi đã thay đổi mọi thứ một cách kỳ diệu cho employee vật!

Proxy để xác thực các giá trị

Hãy tạo một đối tượng proxy để xác thực một giá trị số nguyên.

Bước 1: Tạo trình xử lý sử dụng bẫy đã đặt

Trình xử lý trông như thế này:

const validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if(!Number.isInteger(value)) {
                throw new TypeError('Age is always an Integer, Please Correct it!');
            }
            if(value < 0) {
                throw new TypeError('This is insane, a negative age?');
            }
        }
    }
};

Bước 2: Tạo đối tượng proxy

Tạo một đối tượng proxy như thế này:

let proxy = new Proxy(employee, validator);

Bước 3: Gán một giá trị không nguyên cho một thuộc tính, chẳng hạn như tuổi

Hãy thử làm điều này:

proxy.age="I am testing a blunder"; // string value

Đầu ra sẽ như thế này:

TypeError: Age is always an Integer, Please Correct it!
    at Object.set (E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:28:23)
    at Object.<anonymous> (E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:40:7)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3

Tương tự, hãy thử làm điều này:

p.age = -1; // will result in error

Cách sử dụng Proxy và Reflect cùng nhau

Dưới đây là một ví dụ về trình xử lý mà chúng tôi sử dụng các phương thức từ API Reflect:

const employee = {
    firstName: 'Tapas',
    lastName: 'Adhikary'
};

let logHandler = {
    get: function(target, fieldName) {        
        console.log("Log: ", target[fieldName]);
        
        // Use the get method of the Reflect object
        return Reflect.get(target, fieldName);
    }
};

let func = () => {
    let p = new Proxy(employee, logHandler);
    p.firstName;
    p.lastName;
};

func();

Một vài trường hợp sử dụng Proxy khác

Có một số trường hợp sử dụng khác mà khái niệm này có thể được sử dụng.

  • Để bảo vệ TÔI trường của một đối tượng khỏi bị xóa (bẫy: deleteProperty)
  • Để theo dõi Quyền truy cập thuộc tính (bẫy: nhận, đặt)
  • Đối với ràng buộc dữ liệu (bẫy: đặt)
  • Với tham chiếu hủy bỏ
  • để thao tác in hành vi của nhà điều hành

… và nhiều thứ khác nữa.

Trong khi khái niệm về Metaprogramming mang lại cho chúng ta rất nhiều sức mạnh, đôi khi sự kỳ diệu của nó có thể đi sai hướng.

black_magic
Hãy cẩn thận với mặt khác của phép thuật

Hãy cẩn thận:

  • Quá nhiều magic! Hãy chắc chắn rằng bạn hiểu nó trước khi bạn áp dụng nó.
  • Hiệu suất khả thi đạt được khi bạn biến điều không thể thành có thể
  • Có thể được coi là gỡ lỗi truy cập.

Để tóm tắt,

  • ReflectProxy là những bổ sung tuyệt vời trong JavaScript để trợ giúp cho Lập trình siêu dữ liệu.
  • Rất nhiều tình huống phức tạp có thể được xử lý với sự giúp đỡ của họ.
  • Hãy nhận biết những nhược điểm là tốt.
  • ES6 Symbols cũng có thể được sử dụng với các lớp và đối tượng hiện có của bạn để thay đổi hành vi của chúng.

Tôi hy vọng bạn tìm thấy bài viết này sâu sắc. Tất cả mã nguồn được sử dụng trong bài viết này có thể được tìm thấy trong kho lưu trữ GitHub của tôi.

Hãy chia sẻ bài viết để những người khác cũng có thể đọc được. Bạn có thể @ tôi trên Twitter (@tapasadhikary) với các bình luận, hoặc thoải mái theo dõi tôi.





Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status