Ứng dụng một phần là gì?


Dương Kim Điệp
1 năm trước
Hữu ích 5 Chia sẻ Viết bình luận 0
Đã xem 9923

Ứng dụng một phần là gì?

Ứng dụng một phần là một hàm trong đó một số hoặc tất cả các đối số được đóng gói bên trong, sẵn sàng hoạt động và nó chỉ chờ thêm một vài lần nữa trước khi hàm chính được gọi. Chúng giống như các hàm có đối số mặc định, nhưng là các hàm thuần túy với một lượng tham số cố định.

Giới thiệu

Bài viết và danh sách phát video đồng hành sau đây sẽ đề cập đến một ứng dụng một phần là gì và làm thế nào nó có thể được sử dụng cho một tùy chọn chức năng thuần túy hơn cho các đối số mặc định. Giả sử bạn biết chức năng thuần túy là gì. Chúng tôi sẽ bao gồm:

  • Đối số chức năng cơ bản.
  • Đối số mặc định và cách đặt hàng có thể làm cho chúng khó hơn / dễ sử dụng hơn.
  • Chức năng arity.
  • Chức năng currying với các bao đóng và hiển thị cách đảo ngược thứ tự tham số so với các đối số mặc định.
  • Xây dựng các ứng dụng một phần để hiển thị cách sử dụng các đối số mặc định thuần túy.
  • Tạo các ứng dụng một phần không có đối số.

Danh sách phát video

Trong trường hợp bạn muốn có một ví dụ tương tác, tôi sẽ đi qua từng phần.


Luận điểm cơ bản

Các chức năng dưới đây ping google.com :
const pingGoogle = () => fetch('http://google.com'). Nếu nó được giải quyết, Google sẽ ở đó và máy tính của bạn có thể nói chuyện với các mạng nội bộ. Nếu nó gây ra lỗi, máy tính của bạn có thể gặp sự cố không dây.

Bạn gọi (hoặc gọi) nó như thế nào pingGoogle(). Nếu bạn muốn nhật ký tốt hơn, bạn sẽ đi:

pingGoogle()
.then(() => console.log("Connected to the internet!"))
.catch( error => console.log("Not connected to the internet:", error))

Trong phát triển phần mềm, bạn thường kiểm tra máy chủ CỦA BẠN, mã sẽ tồn tại trong URL CỦA BẠN chứ không phải Google. Ví dụ, tôi tự hỏi nếu blog của tôi lên?

const pingJessesBlog = () =>
fetch('http://jessewarden.com')

Tuy nhiên, trong khi nhỏ, tất cả đều được mã hóa cứng. Mặc dù không phải là tôn giáo, nhưng nó vi phạm DRY: Đừng lặp lại chính mình. Làm thế nào chúng ta có thể làm cho chức năng tái sử dụng nhiều hơn? Thông số hoặc đối số!

const ping = url => fetch(url)

Rad, bây giờ chúng tôi có thể kiểm tra lại Google:

ping('http://google.com')

Hoặc blog của tôi:

ping('http://jessewarden.com')

Hoặc thậm chí xem nếu chúng tôi kết nối với internet đầu tiên, THÌ blog của tôi.

ping('http://google.com')
.then(() => ping('http://jessewarden.com'))
.catch(error => console.log("Something failed:", error))

Đối số mặc định và hàm thuần túy

Tuy nhiên, chúng tôi biết nếu chúng tôi gọi ping, chúng tôi sẽ thực hiện điều này trên google rất nhiều, vì vậy để ngăn bản thân không gõ quá nhiều, chúng ta hãy mặc định là đối với http: // google.com.

const ping = (url='http://google.com') => fetch(url)

Bây giờ, chúng tôi chỉ phải cung cấp tham số URL nếu chúng tôi muốn một cái gì đó ngoài Google:

ping()
.then(() => ping('http://jessewarden.com'))
.catch(error => console.log("Something failed:", error))

Lưu ý rằng hàm pingbây giờ được nướng sẵn với một đối số là một bản sao lưu. undefinedThay vào đó, nếu bạn không gửi cho tôi hoặc gửi cho tôi , tôi sẽ chỉ sử dụng 'http://google.com'. Thay vào đó, đây là thứ được gọi là đối số mặc định. Nếu bạn cung cấp một cái gì đó, tuyệt vời, nhưng nếu bạn không, vẫn tuyệt vời, chúng tôi đã trở lại.

Bây giờ, pingkhông phải là một chức năng thuần túy. Một hàm thuần túy là:

  1. Cùng một đầu vào, cùng một đầu ra.
  2. Không có tác dụng phụ.

Một phiên bản thuần túy hơn sẽ là nếu chúng tôi buộc nhà phát triển tuyên bố nơi họ sẽ nhận được sự phụ thuộc fetch:

const pingPure = (fetch, url='http://jessewarden.com') => fetch(url))

Tất cả những gì chúng tôi thay đổi là yêu cầu fetchnhư tham số đầu tiên, tham số thứ hai  urlvẫn là tùy chọn và đặt mặc định cho Google. Bây giờ fetchcó tác dụng phụ, như thực hiện cuộc gọi HTTP, nhưng miễn là chúng tôi xử lý .catchhoặc sử dụng cú pháp thử / bắt với async / await, nó đủ tốt.

ping(fetch)
.then(() => ping(fetch, 'http://jessewarden.com'))
.catch(error => console.log("Something failed:", error))

Thứ tự tham số chức năng

Tuy nhiên, đó là một nỗi đau ở mông. Như bạn có thể thấy thứ tự đối số thực sự có thể làm cho một hàm khó sử dụng và chỉ nghĩ về thứ tự đối số hoặc kiểm tra một hàm, sau đó lặp lại trên một phiên bản mới của nó với một thứ tự khác có thể giúp mọi việc dễ dàng hơn. Chúng ta hãy làm điều đó bằng cách đảo ngược thứ tự, VÀ làm cho fetchcũng có một mặc định.

const pingPure = (url='http://jessewarden.com', fetchModule=fetch) => fetchModule(url))

Bây giờ, thật dễ dàng để sử dụng lại, nhưng có thể tinh khiết hơn:

ping()
.then(() => ping('http://jessewarden.com'))
.catch(error => console.log("Something failed:", error))

Thật tuyệt, và nếu bạn đang ở trong các trình duyệt cũ hơn, hoặc thậm chí trong Node nhưng muốn tìm nạp, bạn có thể truy cập:

import fetch from 'cross-fetch' // polyfill for older browsers, or if you're in Node

ping(undefined, fetch)
.then(() => ping('http://jessewarden.com', fetch))
.catch(error => console.log("Something failed:", error))

Lưu ý chúng tôi vượt qua undefinedbằng tay. Trong các tham số chức năng JavaScript, undefinedcũng giống như điều mà không vượt qua bất cứ thứ gì.

Tinh khiết, linh hoạt, dễ sử dụng.

Những gì chúng ta đã học:

  • Đối số mặc định làm cho các hàm dễ sử dụng hơn bằng cách yêu cầu ít đối số hơn và cung cấp mặc định hợp lý.
  • Các nhà phát triển sử dụng chúng có thể gõ ít hơn.
  • Ít gõ hơn, nhưng quan trọng hơn, ít đọc hơn làm cho mã dễ hiểu hơn.
  • Các hàm thuần túy yêu cầu cùng một đầu vào và không có tác dụng phụ.
  • Mặc dù vậy, các hàm thuần túy, quan trọng hơn, phải lấy tất cả các biến mà chúng không tự tạo ra từ các đối số hàm, trong trường hợp này fetch.
  • Thứ tự tham số chức năng thực sự giúp một chức năng có thể sử dụng nhiều hơn và dễ đọc hơn.

Chức năng Arity

Chúng ta hãy ngắn gọn bao gồm chức năng arity. Bạn có thể tìm hiểu thêm ở đây hoặc xem một đoạn video ngắn ở đây .

Hàm Arity là hàm có bao nhiêu tham số / đối số. yo()có 0, dude(wat)có 1, v.v. Bạn có thể truy vấn này thông qua theFunction.length.

console.log(yo.length) // 0
console.log(dude.length) // 1

Tuy nhiên, tranh luận về điếc không được tính chống lại điều đó. Tồi tệ hơn, nếu bạn đặt chúng lên trước, về cơ bản nó sẽ nói, đó là Oh, nếu đối số đầu tiên có mặc định, tôi đoán toàn bộ hàm không yêu cầu bất kỳ đối số nào và do đó có Arity bằng 0..

Vì vậy, pingPureở trên của chúng tôi sẽ có một số 0.

Những gì chúng ta đã học:

  • Hàm Arity có nghĩa là hàm có bao nhiêu tham số.
  • Nếu bạn sử dụng các đối số mặc định, thì arity là vô nghĩa trong JavaScript, heh.

Cà ri

Bây giờ một câu hỏi cơ bản: Các đối số mặc định có ảnh hưởng đến độ tinh khiết của chức năng không?

Câu trả lời: Có.

Mặc dù những thứ như Chuỗi được sao chép bằng val, như 'http://google.com' của chúng tôi, nhưng Đối tượng / Mảng thì không.

const pingPure = (url='http://jessewarden.com', fetchModule=fetch) => fetchModule(url))

Lưu ý fetch; cái đó từ đâu tới? Một biến toàn cục hoặc đóng được xác định và nó chỉ biết rằng khi hàm được chạy (hoặc được xác định nếu bạn sử dụng khai báo hàm, như function pingPureso với các hàm mũi tên béo).

Vậy làm thế nào để chúng ta làm cho nó thuần túy, nhưng vẫn cho phép các đối số mặc định?

Currying, còn được gọi là cà ri chức năng. Currying đảm bảo tất cả các chức năng có một, và chỉ một, tranh luận. Nói cách khác, tất cả các hàm trả về các hàm cho đến khi đối số cuối cùng được thông qua; THAT là một trong những thực sự kích hoạt tất cả các công việc.

Bây giờ các chức năng trả về các chức năng không phải là một số điều mới, mà thôi. Ví dụ: bạn có thể gói mọi thứ trong các bao đóng trả về các hàm:

const ping = url => fetch(url)
const pingGoogle = () => {
    return ping('http://google.com')
}

Bây giờ bạn có một hàm trả về một hàm mà sẽ trả lại một lời hứa. Lưu ý cách chúng tôi nướng URL Google vào chính chức năng. Nó được đóng lại và được lưu trữ ở đó, sẵn sàng để sử dụng. Đó là cách cà ri có thể hoạt động trong JavaScript.

Bạn cũng có thể sử dụng nó để gỡ lỗi, như trong mô-đun gỡ lỗi Node: https://www.npmjs.com/package/debug:

var debug = require('debug')('ping')

const ping = () => {
    debug("Pinging Google...")
    return fetch('http://google.com')
    .then(result => {
        debug("Done!")
    })
    .catch(error => {
        debug("Oh, it went boom:", error)
    })
}

Trước khi các hệ thống mô-đun như Node, Require.js hoặc ES6 / Webpack chính thức hóa mọi thứ, mọi người sẽ xác định các mô-đun bằng cách sử dụng Biểu thức hàm được gọi ngay lập tức hoặc IIFE.

(function () {
    function Person(name) {
        this.name = name
    }
    Person.prototype.sayName = function() { 
        console.log("this.name:", this.name)
    }
    return Person
})()

Lưu ý cái cuối cùng ()thực sự gọi hàm, nhưng vì các biến là cục bộ, bạn không phải lo lắng về việc Personlớp của bạn vô tình phá hủy lớp khác, được xác định trước đó trên windowhoặc toàn cầu trong Node. jQuery từng có xung đột phiên bản mọi lúc khi mọi người bắt đầu sử dụng nó và mọi người sẽ đưa nó vào, và bạn chưa bao giờ có ý tưởng về phiên bản nào bạn đang sử dụng, vì lý do đó, IIFE đã sửa nó.

Tuy nhiên, không có chức năng nào trong số đó là thuần túy; họ không khai báo các phụ thuộc của chúng trong các đối số hàm; chúng là các bao đóng ma thuật (tức là mô-đun gỡ lỗi hoặc mô-đun tìm nạp).

Cách dễ nhất là sử dụng một thư viện như Ramda hoặc Lodash để lấy mã hiện có của bạn và cho nó vào, nhưng bây giờ tôi và những người khác làm theo cách thủ công:

const ping = url => fetch => fetch(url)

Điều này có nghĩa là gì, thay vì gọi nó như trước đây:
ping('http://google.com', fetch)

Bạn phải gọi nó là một cách hơi kỳ lạ:

ping('http://google.com')(fetch)

Đây là lý do tại sao Tiến sĩ Axel Rauschmayer nói rằng cà ri không phải là JavaScript thành ngữ . JavaScript thành ngữ gì , heh?

Lý do là tham số đầu tiên trả về một hàm và thứ hai gọi hàm trả về. Nói cách khác:

const allINeedIsFetch = ping('http://google.com')
console.log(allINeedIsFetch) // function
const result = allINeedIsFetch(fetch) // Promise

Để làm điều đó theo cách cũ:

function ping(url) { 
    return function(fetch) { 
        return fetch(url)
    } 
}

Vâng không, heh! Sử dụng Lodash / Ramda, bạn có thể chỉ cần:

const curry = require('lodash/fp/curry')

const ping = (url, fetch) => fetch(url)
const pingCurried = curry(ping)

Bây giờ, bạn có thể làm một trong hai cách và cả hai đều hoạt động:

pingCurried('http://google.com', fetch)
pingCurried('http://google.com')(fetch)

Những gì chúng ta đã học:

  • Chức năng currying là đảm bảo tất cả các chức năng chỉ có một đối số.
  • Các hàm sẽ trả về các hàm cho đến khi đối số cuối cùng được truyền, sau đó nó thực sự chạy.
  • Các thư viện như Lodash / Ramda cho phép bạn thực hiện các chức năng dấu phẩy thông thường hiện có và làm cho chúng bị cong, nhưng chúng vẫn hoạt động cả hai cách.

Tôi không có thời gian để nói về cách thức hoạt động của nó với các hàm đã có đối số mặc định, heh.

Ứng dụng một phần

Ứng dụng một phần là gì?

Một ứng dụng một phần là những gì một hàm curried trả về khi bạn không gọi đối số cuối cùng.

Nếu chúng ta sử dụng chức năng ping bị quấy rầy:

const ping = url => fetch => fetch(url)

Càng và gọi nó với cả hai đối số:

const result = ping('http://google.com')(fetch)
console.log(result) // Promise

Sọ nó chạy chức năng của chúng tôi và trả về a Promise.

Nhưng điều gì xảy ra nếu chúng ta gọi nó chỉ bằng một tham số, ví dụ: URL?

const wat = ping('http://google.com')
console.log(wat) // function

Đây watlà một chức năng. Nếu bạn đăng xuất nó ra trình duyệt int, nó thực sự trông như thế này:

fetch => fetch(url)

Rad, phải không? Nhưng hãy chờ đợi, nơi này urlđến từ đâu? Việc đóng cửa! Tương tự như chúng ta đã làm như trước đây:

const url = 'http://google.com'
const wat = fetch => fetch(url)

Tuy nhiên, không giống như ở trên, chúng ta không cần một biến; chức năng này bao vây với nó, và giữ cho nó an toàn, giữ cho nó tinh khiết.

Vì vậy, người ta có thể nói rằng bạn có một hàm được sử dụng một phần. Đó là từ được áp dụng từ Lý thuyết Danh mục nơi bạn áp dụng các hàm cho các đối số. Về cơ bản, điều đó có nghĩa là hàm này có hai đối số, bạn đã đưa cho chúng tôi một trong số chúng, vì vậy hãy lấy hàm này và khi bạn có sẵn tham số thứ hai, hãy đưa nó cho hàm đó tôi đã cho bạn, nó sẽ thực hiện công việc.

Và đó là cách bạn thực hiện các đối số mặc định thuần túy.

const pingGoogle = ping('http://google.com')

Bây giờ, bạn chỉ cần cung cấp fetch, và nó là tốt để đi!

pingGoogle(fetch)

Tĩnh trái, động phải

Tuy nhiên, chúng tôi luôn biết rằng chúng tôi sẽ sử dụng fetch, đó là loại ngu ngốc; phần động duy nhất thực sự là URL. Như bạn đã thấy ở trên bằng cách sử dụng các đối số mặc định, các công cụ động như URL thường đi về bên trái. Những thứ bạn biết là tĩnh, không thay đổi nhiều như fetchmô-đun mà hầu hết các trình duyệt sử dụng, là ở bên phải.

Đó là đảo ngược trong cà ri. Tĩnh trái, động phải, như vậy:

const ping = fetch => url => fetch(url)

Khi bạn làm điều đó, bây giờ bạn có thể làm điều đó chúng tôi đã làm ở trên:

const pingPartial = ping(fetch)

pingPartial('http://google.com')
.then(() => pingPartial('http://jessewarden.com'))
.then(() => console.log("Pinged blog!"))
.catch(error => console.log("Failed:", error))

Ứng dụng một phần không có đối số

Nhưng, làm thế nào để bạn tạo một ứng dụng một phần không có đối số? Thật khó, nhưng bây giờ, tôi sẽ chỉ cho bạn cách thức của Lodash; họ có một chức năng được gọi là partiallàm cho nó dễ dàng:

const partial = require('lodash/fp/partial')
const ping = (fetch, url) => fetch(url)

const pingPartial = partial(ping, [fetch])
const pingGoogle = partial(ping, [fetch, 'http://google.com'])
const pingJesse = partial(ping, [fetch, 'http://jessewarden.com'])

Để sử dụng:

pingGoogle()
.then(() => pingJesse())
.then(() => console.log("Pinged blog!"))
.catch(error => console.log("Failed:", error))

Những gì chúng ta đã học:

  • Các phần chức năng là những gì các hàm được trả lại nếu bạn không cung cấp cho chúng tất cả các đối số của chúng.
  • Chúng cho phép bạn sử dụng các đối số mặc định theo cách thuần túy.
  • Nếu bạn muốn một ứng dụng một phần không có đối số, hãy sử dụng thư viện như Lodash / Ramda.

Phần kết luận

Một ứng dụng một phần là một hàm thuần túy với một số hoặc tất cả các đối số của nó được lưu trữ bên trong dưới dạng một bao đóng. Nếu bạn đang sử dụng các đối số mặc định rất nhiều, đó là một cách thuần túy hơn để thực hiện điều đó. Các ứng dụng một phần được tạo ra bằng cách chỉ đưa ra một số đối số cần thiết cho hàm bị khóa; giá trị trả về sẽ là một ứng dụng một phần cho đến khi tất cả các đối số đã được đưa ra. Thứ tự tham số cho các đối số mặc định có xu hướng thiên về các tham số tĩnh đã biết, bên phải và không xác định, dữ liệu động ở bên trái. Chức năng curried thì ngược lại. Các lệnh tham số này làm cho các loại chức năng dễ sử dụng hơn.

Hữu ích 5 Chia sẻ Viết bình luận 0
Đã xem 9923