REPL?! Đây Là Gì?
REPL là viết tắt của Read-Eval-Print Loop, và trong Clojure, đây là cách làm việc với mã. Nó tương tác, nhanh chóng và thực sự rất thú vị. Bạn viết mã, chạy ngay lập tức và kết quả sẽ xuất hiện ngay lập tức.
Tuy nhiên, REPL mặc định của NodeJS không thực sự tiện lợi cho lập trình hàng ngày. Chạy nó trong terminal không thuận tiện: bạn không thấy toàn bộ bức tranh, mã của bạn không được kiểm tra trước, và bạn liên tục chuyển đổi giữa trình soạn thảo và terminal, làm mất tập trung và ngữ cảnh.
Vì vậy, trong bài viết này, tôi sẽ chỉ cho bạn cách làm cho việc lập trình JavaScript trở nên tương tác và thú vị hơn - giống như cách mọi người lập trình trong Clojure.
Ví Dụ Thực Tế
Ví dụ, trong JavaScript, bạn có thể viết:
javascript
function add(a, b) { return a + b; }
Để kiểm tra nó, bạn thường phải thêm một số bài kiểm tra, hoặc làm điều gì đó như:
javascript
console.log(add(36, 6));
Sau đó chạy nó với:
bash
node index.js
Đó là rất nhiều bước cho một kết quả duy nhất.
Trong Clojure, mọi thứ trực tiếp hơn nhiều:
clojure
(defn add [a b] (+ a b))
(add 35 7) ;; => 42
Chu kỳ phản hồi là ngay lập tức. (Tất nhiên, chúng ta vẫn cần các bài kiểm tra đúng cho phần mềm thực tế, nhưng để khám phá và giải quyết vấn đề, REPL là rất hiệu quả.)
Công Thức
Vậy làm thế nào để mang điều đó vào JavaScript?
Tôi đã viết một tiện ích mở rộng cho plugin Conjure trong Neovim. Việc thiết lập rất đơn giản - chỉ cần làm theo tài liệu:
- Cài đặt Neovim
- Cài đặt Conjure
- Cài đặt NodeJS
- Cài đặt Treesitter và chạy
:TSInstall javascripttrong Neovim - Tạo một tệp ".js" mới và mở nó trong Neovim (ví dụ:
nvim repl-test.jstrong terminal của bạn). Conjure sẽ tự động gắn kết nếu mọi thứ được thiết lập đúng. Đó là tất cả - bạn đã sẵn sàng để bắt đầu đánh giá JavaScript ngay từ trình soạn thảo.
Tại sao phải làm vậy? Bởi vì một khi bạn đã có vòng lặp REPL này, bạn sẽ có ít lỗi hơn, bạn thực sự có thể cảm nhận được dòng chảy của mã của bạn, và bạn có thể điều chỉnh mọi thứ nhanh hơn rất nhiều.
Ví Dụ:
javascript
const mult = (a, b) => a * b;
mult(3, 4); // (out) 12
mult(5, 3); // (out) 15
mult(10, 8); // (out) 80
Bạn muốn thấy giá trị của một biểu thức nhanh chóng? Nhấn <localleader>ee (mặc định của Conjure). Cần khám phá một đối tượng? Chỉ cần đánh giá nó:
javascript
const obj = { a: 1, b: { c: 3 } }; // đánh giá này trước
obj.b.c; // (out) 3
Gỡ Lỗi Ngay Lập Tức
Kiểm tra các bước trung gian trở nên đơn giản:
javascript
[1, 2, 2, 3].reduce((acc, x) => {
console.log("acc", acc);
console.log("x", x);
console.log("val", acc[x]);
return { ...acc, [x]: ++acc[x] || 1 };
}, {});
Bạn sẽ thấy ngay lập tức từng bước trong bộ đệm log của Conjure. Không cần phải chạy lại tệp, không cần phải chuyển đổi giữa các terminal. Chỉ cần... dòng chảy.
Ví Dụ Nhanh Về Clojure
Các lập trình viên Clojure thường định nghĩa một hàm và ngay lập tức kiểm tra nó bên trong một khối (comment ...):
clojure
(defn name-extractor [m] (get m :name))
(comment
(name-extractor {:name "Kisha"}) ;; "Kisha"
(name-extractor {:age 20})) ;; nil
Nếu chúng ta muốn trả về :noname khi trường bị thiếu, chúng ta chỉ cần điều chỉnh hàm:
clojure
(defn name-extractor [m]
(get m :name :noname))
Và nếu chúng ta muốn xử lý chuỗi rỗng:
clojure
(defn name-extractor [{:keys [name]}]
(or (not-empty name) :noname))
Mỗi bước được thử nghiệm và hoàn thiện trong REPL.
Làm Tương Tự Trong JavaScript
javascript
function nameExtractor({ name }) {
return name || "noname";
}
nameExtractor({ name: "John" }); // 'John'
nameExtractor({ age: 15 }); // 'noname'
nameExtractor({ name: "" }); // 'noname'
Giống như trong Clojure, bạn có thể viết các trường hợp thử nghiệm nhỏ trong mã và điều chỉnh hàm từng bước.
Khám Phá RXJS
RXJS có thể cảm thấy trừu tượng lúc đầu, nhưng với REPL, bạn có thể thử nghiệm một cách tương tác và thấy kết quả ngay khi bạn đi:
javascript
import { BehaviorSubject } from "rxjs"
import { combineLatestWith, map, tap } from "rxjs/operators"
let o1 = new BehaviorSubject({ a: 1 })
let o2 = new BehaviorSubject({ b: 6 })
o1.pipe(combineLatestWith(o2),
tap(x => console.log("combined:", x)),
map(([{ a }, { b }]) => a + b),
tap(x => console.log("sum:", x))
).subscribe()
o1.next({ a: 2 })
o2.next({ b: 8 })
o1.unsubscribe()
o2.unsubscribe()
Bạn có thể điều chỉnh giá trị, chạy lại chúng và ngay lập tức quan sát cách các biến đổi hoạt động.
Các Phím Tắt Hữu Ích
<localleader>ef- đánh giá toàn bộ tệp<localleader>ts- chuyển đổi Stray Out Mode (để xem các đánh giá trì hoãn nhưsetTimeout)<localleader>cr- khởi động lại REPL (xóa tất cả các đánh giá trước đó)<localleader>E- đánh giá một khối văn bản được chọn trong chế độ hình ảnh (rất hữu ích cho các ví dụ JSDoc)
Những Khó Khăn Trong NodeJS (Được Xử Lý Bởi Conjure)
- Các hàm mũi tên không thể được định nghĩa lại trong REPL của NodeJS, nhưng Conjure sẽ chuyển đổi chúng thành các hàm thông thường.
importkhông được hỗ trợ trong REPL của NodeJS, nhưng Conjure sẽ chuyển nó thànhrequire.
Kết Luận
Những mẹo này có thể làm cho quy trình làm việc của bạn trong JavaScript nhanh hơn, tương tác hơn và gần gũi hơn với phong cách REPL của Clojure. Một chu kỳ phản hồi ngắn hơn không chỉ làm cho việc lập trình trở nên thú vị hơn, mà còn giúp bạn viết mã sạch hơn, đáng tin cậy hơn.