Các hàm thuần túy là các khối xây dựng nguyên tử trong lập trình hàm. Chúng được yêu mến vì sự đơn giản và khả năng kiểm tra của chúng.
Bài đăng này bao gồm một danh sách kiểm tra nhanh để biết liệu một chức năng có thuần túy hay không.
danh sách kiểm tra
Một chức năng phải vượt qua hai bài kiểm tra để được coi là “thuần túy”:
- Đầu vào giống nhau luôn trả về cùng một kết quả đầu ra
- Không có tác dụng phụ
Hãy phóng to từng cái một.
1. Đầu vào giống nhau => Đầu ra giống nhau
So sánh điều này:
const add = (x, y) => x + y;
add(2, 4); // 6
Về điều này:
let x = 2;
const add = (y) => {
x += y;
};
add(4); // x === 6 (the first time)
Hàm thuần túy = Kết quả nhất quán
Ví dụ đầu tiên trả về một giá trị dựa trên các tham số đã cho, bất kể bạn gọi nó ở đâu/khi nào.
Nếu bạn vượt qua 2
và 4
bạn sẽ luôn nhận được 6
.
Không có gì khác ảnh hưởng đến đầu ra.
Hàm không tinh khiết = Kết quả không nhất quán
Ví dụ thứ hai không trả về gì cả. Điều đó dựa vào trạng thái chia sẻ để thực hiện công việc của mình bằng cách tăng một biến bên ngoài phạm vi của chính nó.
Mô hình này là nhiên liệu ác mộng của nhà phát triển.
trạng thái chia sẻ giới thiệu một sự phụ thuộc thời gian. Bạn nhận được các kết quả khác nhau tùy thuộc vào thời điểm bạn gọi hàm. Lần đầu tiên có kết quả 6
thời gian tới là 10
và như thế.
Phiên bản nào dễ suy luận hơn?
Cái nào ít có khả năng sinh ra bọ chỉ xảy ra trong những điều kiện nhất định?
Cái nào có nhiều khả năng thành công hơn trong môi trường đa luồng, nơi mà sự phụ thuộc vào thời gian có thể phá vỡ hệ thống?
Chắc chắn là cái đầu tiên.
2. Không có tác dụng phụ
Bản thân bài kiểm tra này là một danh sách kiểm tra. Một vài ví dụ về tác dụng phụ là
- Thay đổi đầu vào của bạn
console.log
- Cuộc gọi HTTP (AJAX/tìm nạp)
- Thay đổi hệ thống tập tin (fs)
- Truy vấn DOM
Về cơ bản, bất kỳ công việc nào mà một hàm thực hiện không liên quan đến việc tính toán đầu ra cuối cùng.
Đây là một chức năng không trong sạch với tác dụng phụ.
Không tệ lắm
const impureDouble = (x) => {
console.log('doubling', x);
return x * 2;
};
const result = impureDouble(4);
console.log({ result });
console.log
là tác dụng phụ ở đây, nhưng trong thực tế, nó sẽ không gây hại cho chúng ta. Chúng tôi vẫn sẽ nhận được các đầu ra giống nhau, với các đầu vào giống nhau.
Cái nàytuy nhiên, có thể gây ra sự cố.
Thay đổi một cách “không trong sạch” một đối tượng
const impureAssoc = (key, value, object) => {
object[key] = value;
};
const person = {
name: 'Bobo'
};
const result = impureAssoc('shoeSize', 400, person);
console.log({
person,
result
});
biến, person
đã bị thay đổi mãi mãi vì chức năng của chúng tôi đã giới thiệu một câu lệnh gán.
trạng thái chia sẻ có nghĩa là impureAssoc
tác động của nó không còn rõ ràng nữa. Hiểu được tác động của nó đối với một hệ thống giờ đây liên quan đến việc theo dõi mọi biến số mà nó từng chạm vào và biết lịch sử của chúng.
Trạng thái chia sẻ = phụ thuộc thời gian.
Chúng ta có thể thanh lọc impureAssoc
bằng cách đơn giản trả lại một đối tượng mới với các thuộc tính mong muốn của chúng tôi.
Thanh lọc nó lên
const pureAssoc = (key, value, object) => ({
...object,
[key]: value
});
const person = {
name: 'Bobo'
};
const result = pureAssoc('shoeSize', 400, person);
console.log({
person,
result
});
Hiện nay pureAssoc
trả về một kết quả có thể kiểm tra được và chúng tôi sẽ không bao giờ lo lắng nếu nó lặng lẽ thay đổi thứ gì đó ở nơi khác.
Bạn thậm chí có thể làm như sau và vẫn trong sạch:
Một cách tinh khiết khác
const pureAssoc = (key, value, object) => {
const newObject = { ...object };
newObject[key] = value;
return newObject;
};
const person = {
name: 'Bobo'
};
const result = pureAssoc('shoeSize', 400, person);
console.log({
person,
result
});
Thay đổi đầu vào của bạn có thể nguy hiểm, nhưng thay đổi một bản sao của nó thì không có vấn đề gì. Kết quả cuối cùng của chúng tôi vẫn là một chức năng có thể kiểm tra, dự đoán được, hoạt động bất kể bạn gọi nó ở đâu/khi nào.
Đột biến bị giới hạn trong phạm vi nhỏ đó và bạn vẫn đang trả về một giá trị.
Đối tượng nhân bản sâu
Đứng lên! Sử dụng toán tử trải rộng ...
tạo ra một Nông cạn bản sao của một đối tượng. Các bản sao nông không an toàn trước các đột biến lồng nhau.
Cảm ơn bạn Rodrigo Fernández Díaz đã khiến tôi chú ý đến điều này!
Đột biến lồng nhau không an toàn
const person = {
name: 'Bobo',
address: { street: 'Main Street', number: 123 }
};
const shallowPersonClone = { ...person };
shallowPersonClone.address.number = 456;
console.log({ person, shallowPersonClone });
Cả hai person
và shallowPersonClone
đã bị đột biến vì con cái của họ chia sẻ cùng một tài liệu tham khảo!
Đột biến lồng nhau an toàn
Để thay đổi các thuộc tính lồng nhau một cách an toàn, chúng ta cần một sâu dòng vô tính.
const person = {
name: 'Bobo',
address: { street: 'Main Street', number: 123 }
};
const deepPersonClone = JSON.parse(JSON.stringify(person));
deepPersonClone.address.number = 456;
console.log({ person, deepPersonClone });
Bây giờ bạn đã được đảm bảo an toàn vì chúng thực sự là hai thực thể riêng biệt!
Tóm lược
- Một hàm thuần túy nếu nó không có tác dụng phụ và trả về cùng một đầu ra, với cùng một đầu vào.
- Các tác dụng phụ bao gồm: thay đổi đầu vào, lệnh gọi HTTP, ghi vào đĩa, in ra màn hình.
- bạn có thể an toàn dòng vô tính, sau đó đột biến, đầu vào của bạn. Chỉ cần để nguyên bản gốc.
- Cú pháp lây lan (
…
cú pháp) là cách dễ nhất để nông cạn các đối tượng nhân bản. JSON.parse(JSON.stringify(object))
là cách dễ nhất để sâu sắc các đối tượng nhân bản. Một lần nữa xin cảm ơn Rodrigo Fernández Díaz!
Khóa học miễn phí của tôi
hướng dẫn này là từ khóa học hoàn toàn miễn phí của tôi trên Educative.io, Các mẫu lập trình hàm với RamdaJS!
Vui lòng xem xét việc lấy/chia sẻ nó nếu bạn thích nội dung này.
Nó có đầy đủ các bài học, đồ họa, bài tập và các mẫu mã có thể chạy được để dạy cho bạn phong cách lập trình chức năng cơ bản bằng cách sử dụng RamdaJS.
Cảm ơn vì đã đọc! Cho đến lần sau.