วิธีเริ่มต้นใช้งาน VUE3.0 อย่างรวดเร็ว: เข้าสู่
คำแนะนำที่เกี่ยวข้องกับการเรียนรู้: JavaScript Learning Tutorial
ฉันได้เห็นคำอธิบายมากมายเกี่ยวกับ Functional Programming แต่ส่วนใหญ่อยู่ในระดับทฤษฎี และบางส่วนมีไว้สำหรับภาษา Functional Programming เท่านั้น เช่น ฮาสเคลล์. จุดประสงค์ของบทความนี้คือเพื่อพูดถึงแนวปฏิบัติเฉพาะของ Functional Programming ในสายตาของฉัน เหตุผลที่ "อยู่ในสายตาของฉัน" หมายความว่าสิ่งที่ฉันพูดเป็นเพียงความคิดเห็นส่วนตัวของฉันเท่านั้น ซึ่งอาจขัดแย้งกับแนวคิดที่เข้มงวดบางประการ
บทความนี้จะละเว้นการแนะนำแนวคิดที่เป็นทางการจำนวนมาก และเน้นที่การแสดงโค้ดฟังก์ชันใน JavaScript อะไรคือความแตกต่างระหว่างโค้ดฟังก์ชันกับการเขียนทั่วไป โค้ดฟังก์ชันมีประโยชน์อะไรให้เราบ้าง และฟังก์ชันทั่วไปมีอะไรบ้าง
ฉันคิดว่าการเขียนโปรแกรมเชิงฟังก์ชันสามารถเข้าใจได้ว่าเป็นวิธีการเขียนโปรแกรมที่ใช้ฟังก์ชันเป็นตัวพาหลัก การใช้ฟังก์ชันในการแยกส่วนและนิพจน์ทั่วไปที่เป็นนามธรรมนั้น
ถูกนำมาเปรียบเทียบกับความจำเป็น ประเด็นหลักมีดังนี้:
ความหมายที่ชัดเจนยิ่งขึ้น, ความสามารถในการใช้ซ้ำที่สูงขึ้น, ความสามารถในการบำรุงรักษาที่สูงขึ้น, การจำกัดขอบเขตที่ดีขึ้น และผลข้างเคียงที่น้อยลง ตัวอย่างต่อไปนี้คือนิพจน์การทำงานเฉพาะของ
โค้ด Javascript
// แต่ละคำในอาร์เรย์ ตัวอักษรตัวแรก // การเขียนทั่วไป const arr = ['apple', 'pen', 'apple-pen']; const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); console.log(arr); // วิธีการเขียนเชิงฟังก์ชัน - ฟังก์ชั่น upperFirst(word) { กลับคำ[0].toUpperCase() + word.slice(1); } ฟังก์ชั่น wordToUpperCase (arr) { กลับ arr.map (บนสุด); } console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // วิธีการเขียนเชิงฟังก์ชัน 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1)))
เมื่อสถานการณ์มีความซับซ้อนมากขึ้น วิธีการเขียนนิพจน์จะพบกับปัญหาหลายประการ:
ความหมายไม่ชัดเจน ค่อยๆ กลายเป็นเรื่องยากที่จะรักษา การใช้ซ้ำได้ไม่ดี โค้ดจะ
ถูกสร้างขึ้นมากขึ้น และตัวแปรระดับกลางจำนวนมากจะถูกสร้างขึ้น การเขียนโปรแกรมฟังก์ชั่นสามารถแก้ไขปัญหาข้างต้นได้ดี ขั้นแรก อ้างถึงวิธีการเขียนเชิงฟังก์ชัน 1 ซึ่งใช้การห่อหุ้มฟังก์ชันเพื่อแยกย่อยฟังก์ชัน (รายละเอียดไม่ซ้ำกัน) ห่อหุ้มให้เป็นฟังก์ชันที่แตกต่างกัน จากนั้นใช้การเรียกแบบรวมเพื่อให้บรรลุวัตถุประสงค์ ทำให้สำนวนชัดเจนและง่ายต่อการบำรุงรักษา ใช้ซ้ำ และขยายเวลา ประการที่สอง การใช้ฟังก์ชันที่มีลำดับสูงกว่า Array.map จะแทนที่ for...of สำหรับการสำรวจอาร์เรย์ เพื่อลดตัวแปรระดับกลางและการดำเนินการ
ข้อแตกต่างที่สำคัญระหว่างการเขียนเชิงฟังก์ชัน 1 และการเขียนเชิงฟังก์ชัน 2 คือ คุณสามารถพิจารณาว่าฟังก์ชันนั้นสามารถนำมาใช้ซ้ำได้ในภายหลังหรือไม่ ถ้าไม่เช่นนั้น วิธีหลังจะดีกว่า
จากวิธีการเขียน Functional 2 ข้างต้น เราจะเห็นได้ว่าในกระบวนการเขียน Functional Code นั้น เป็นเรื่องง่ายที่จะทำให้เกิดการขยายในแนวนอน กล่าวคือ ทำให้เกิดการซ้อนหลายชั้น
รหัส Javascript
// คำนวณผลรวมของตัวเลข // วิธีการเขียนทั่วไป console.log(1 + 2 + 3 - 4) // ผลรวมฟังก์ชันการเขียนฟังก์ชัน (a, b) { กลับ + b; } ฟังก์ชั่นย่อย (a, b) { กลับ - ข; } console.log(sub(sum(sum(1, 2), 3), 4); ตัวอย่างนี้แสดงเฉพาะกรณีที่รุนแรงของส่วนขยายแนวนอน เนื่องจากจำนวนระดับที่ซ้อนกันของฟังก์ชันเพิ่มขึ้นอย่างต่อเนื่อง โค้ดจึงอ่านได้น้อยลง ประสิทธิภาพลดลงอย่างมากและทำให้เกิดข้อผิดพลาดได้ง่าย ในกรณีนี้ เราสามารถพิจารณาวิธีการเพิ่มประสิทธิภาพได้หลายวิธี เช่น การเพิ่มประสิทธิภาพลูกโซ่ต่อไปนี้ // เพิ่มประสิทธิภาพการเขียน (คุณอ่านถูกต้องแล้ว นี่คือการเขียนแบบลูกโซ่ของ lodash) โค้ด Javascript const utils = { ห่วงโซ่(ก) { นี้._temp = ก; คืนสิ่งนี้; - ผลรวม(ข) { นี้._temp += b; คืนสิ่งนี้; - ย่อย (ข) { นี้._temp -= b; คืนสิ่งนี้; - ค่า() { const _temp = นี่._temp; this._temp = ไม่ได้กำหนด; กลับ _temp; - console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
หลังจากเขียนใหม่ด้วยวิธีนี้ โครงสร้างโดยรวมจะชัดเจนขึ้น และแต่ละลิงก์ของ chain จะเป็นสิ่งที่ต้องทำก็สามารถแสดงได้อย่างง่ายดาย อีกตัวอย่างที่ดีของการเปรียบเทียบระหว่างการซ้อนฟังก์ชันและการผูกมัดคือฟังก์ชันการโทรกลับและรูปแบบสัญญา
รหัส Javascript
// ขอสองอินเทอร์เฟซตามลำดับ // ฟังก์ชันโทรกลับนำเข้า $ จาก 'jquery'; $.post('a/url/to/target', (rs) => { ถ้า(อาร์เอส){ $.post('a/url/to/another/target', (rs2) => { ถ้า(rs2){ $.post('a/url/to/third/target'); - - - // สัญญาคำขอนำเข้าจาก 'catta'; // catta เป็นเครื่องมือคำขอแบบน้ำหนักเบาที่รองรับการดึงข้อมูล, jsonp, ajax และไม่มีคำขออ้างอิง ('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target') : Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
เมื่อระดับการซ้อนของฟังก์ชันการโทรกลับและความซับซ้อนระดับเดียวเพิ่มขึ้น มันจะกลายเป็น มันเป็น บวมและบำรุงรักษายาก แต่โครงสร้างลูกโซ่ของ Promise ยังสามารถขยายในแนวตั้งได้เมื่อความซับซ้อนสูงและการแยกแบบลำดับชั้นมีความชัดเจนมาก
การปิดโมเดลการทำงานทั่วไป
ไว้
ในบล็อกโค้ดที่ไม่ได้เผยแพร่ เรียกว่าการปิด
แนวคิดของการปิดค่อนข้างเป็นนามธรรม
กระเป๋าพาเราไปได้ไหม?
ก่อนอื่นเรามาดูวิธีการสร้างการปิด:
โค้ด Javascript
// สร้างฟังก์ชันการปิด makeCounter() { ให้ k = 0; ฟังก์ชันส่งคืน () { กลับ ++k; - ตัวนับ const = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter บล็อกโค้ดของฟังก์ชันนี้อ้างอิงตัวแปรโลคัล k ในฟังก์ชันที่ส่งคืน ทำให้ตัวแปรโลคัลล้มเหลวหลังจาก ฟังก์ชันถูกดำเนินการ และจะถูกรีไซเคิลโดยระบบ จึงทำให้เกิดการปิด หน้าที่ของการปิดนี้คือ "รักษา" ตัวแปรภายในเครื่องเพื่อให้สามารถนำตัวแปรกลับมาใช้ใหม่ได้เมื่อมีการเรียกใช้ฟังก์ชันภายใน ซึ่งต่างจากตัวแปรส่วนกลางตรงที่ตัวแปรนี้สามารถอ้างอิงได้ภายในฟังก์ชันเท่านั้น
กล่าวอีกนัยหนึ่ง การปิดจริง ๆ แล้วจะสร้าง "ตัวแปรถาวร" บางตัวที่เป็นส่วนตัวของฟังก์ชัน
จากตัวอย่างนี้ เราสามารถสรุปได้ว่าเงื่อนไขสำหรับการสร้างการปิดคือ:
มีฟังก์ชันภายในและฟังก์ชันภายนอกอ้างอิงถึงตัวแปรเฉพาะที่ของฟังก์ชันภายนอก วัตถุประสงค์หลักของการปิดคือเพื่อกำหนด ฟังก์ชันบางอย่าง ตัวแปรถาวรแบบจำกัดโดเมน ตัวแปรเหล่านี้สามารถใช้สำหรับการแคชหรือการคำนวณระดับกลาง เป็นต้น
รหัส Javascript
// เครื่องมือแคชอย่างง่าย // ฟังก์ชั่นที่ไม่ระบุชื่อสร้างการปิด const cache = (function() { ร้านค้า const = {}; กลับ { รับ (คีย์) { ส่งคืนร้านค้า [คีย์]; - ชุด (คีย์, วาล) { ร้านค้า [คีย์] = วาล; - - cache.set('a', 1); cache.get('a'); // 1
ตัวอย่างข้างต้นคือการใช้เครื่องมือแคชอย่างง่าย ,จะไม่ถูกรีไซเคิล.
ข้อเสียของการปิด: ตัวแปรถาวรจะไม่ถูกปล่อยออกมาตามปกติและยังคงใช้พื้นที่หน่วยความจำ ซึ่งอาจทำให้หน่วยความจำเปลืองได้ง่าย ดังนั้น โดยทั่วไปจำเป็นต้องมีกลไกการล้างข้อมูลด้วยตนเองเพิ่มเติมบางอย่าง
ฟังก์ชันที่ยอมรับหรือส่งคืนฟังก์ชันเรียกว่าฟังก์ชันลำดับที่สูงกว่า
ฟังดูเหมือนเป็นคำที่เย็นชามาก แต่จริงๆ แล้วเรามักจะใช้มัน แต่เราแค่ไม่รู้ชื่อของมัน ภาษา JavaScript สนับสนุนฟังก์ชันที่มีลำดับสูงกว่าอยู่แล้ว เนื่องจากฟังก์ชัน JavaScript เป็นพลเมืองชั้นหนึ่ง และสามารถใช้เป็นทั้งพารามิเตอร์และเป็นค่าตอบแทนของฟังก์ชันอื่นได้
เรามักจะเห็นฟังก์ชันลำดับสูงๆ มากมายใน JavaScript เช่น Array.map, Array.reduce และ Array.filter
มา
ดูกันว่าเขาใช้
กล่าวอีกนัยหนึ่ง แต่ละรายการในชุดจะต้องผ่านการเปลี่ยนแปลงเดียวกันเพื่อสร้าง
แผนที่ชุดใหม่ เนื่องจากเป็นฟังก์ชันที่มีลำดับสูง จึงยอมรับพารามิเตอร์ฟังก์ชันเป็น
โค้ด Javascript
แบบลอจิคัลของการแมป// เพิ่มหนึ่งรายการให้กับแต่ละรายการใน อาร์เรย์เพื่อสร้างอาร์เรย์ใหม่ // วิธีการเขียนทั่วไป const arr = [1,2,3]; const rs = []; rs.push(++n); } console.log(rs) // map เขียนใหม่ const arr = [1,2,3]; const rs = arr.map(n => ++n);
วิธีการเขียนทั่วไปข้างต้น โดยใช้ for...of loop เพื่อสำรวจอาร์เรย์ การดำเนินการเพิ่มเติม และ มีความเสี่ยงในการเปลี่ยนแปลงอาร์เรย์ดั้งเดิม
แต่ฟังก์ชัน map สรุปการดำเนินการที่จำเป็น เพื่อให้เราจำเป็นต้องดูแลเกี่ยวกับการใช้งานฟังก์ชันของตรรกะการแมป ซึ่งจะช่วยลดจำนวนโค้ดและความเสี่ยงของด้านข้าง ผลกระทบ
ให้พารามิเตอร์บางอย่างของฟังก์ชันและสร้างฟังก์ชันใหม่ที่ยอมรับพารามิเตอร์อื่นๆ
คุณอาจไม่ได้ยินคำนี้บ่อยนัก แต่ใครก็ตามที่เคยใช้ undescore หรือ lodash เคยได้ยินคำนี้มาก่อน
มีฟังก์ชัน _.partial ที่มีมนต์ขลัง ซึ่งเป็น
โค้ด Javascript
แบบ Curried// รับเส้นทางสัมพัทธ์ของไฟล์เป้าหมายไปยังเส้นทางฐาน // วิธีเขียนทั่วไปคือ const BASE = '/path/to/base'; path.relative(BASE, '/some/path'); // _.parical เขียนใหม่ const BASE = '/path/to/base'; constrelativeFromBase = _.partial(path.relative, BASE); constrelativePath =relativeFromBase('/some/path');
ผ่าน _.partial เราได้รับฟังก์ชันใหม่relativeFromBase พารามิเตอร์ที่ตามมาจะถูกผนวกตามลำดับ
ในกรณีนี้ สิ่งที่เราต้องการทำให้สำเร็จจริงๆ คือการได้เส้นทางที่สัมพันธ์กับ BASE ในแต่ละครั้ง โดยไม่สัมพันธ์กับเส้นทางใดๆ Currying ช่วยให้เราสนใจเฉพาะพารามิเตอร์บางตัวของฟังก์ชันเท่านั้น ทำให้วัตถุประสงค์ของฟังก์ชันชัดเจนขึ้นและเรียกมันได้ง่ายขึ้น
รวมความสามารถของหลายฟังก์ชันเพื่อสร้างฟังก์ชันใหม่
คุณอาจเคยเห็นมันเป็นครั้งแรกใน lodash ซึ่งเป็นวิธีการเขียน (ปัจจุบันเรียกว่าโฟลว์)
โค้ด Javascript
// ทำให้แต่ละคำในอาร์เรย์เป็นตัวพิมพ์ใหญ่ ทำ Base64 //วิธีการเขียนทั่วไป (หนึ่งในนั้น) const arr = ['pen', 'apple', 'applypen']; rs.push(btoa(w.toUpperCase())); } console.log(rs); // _.flow เขียนใหม่ const arr = ['ปากกา', 'apple', 'applypen']; const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow ผสานความสามารถของฟังก์ชันการแปลงตัวพิมพ์ใหญ่และการแปลง Base64 เพื่อสร้างฟังก์ชันใหม่ สะดวกในการใช้เป็นฟังก์ชันพารามิเตอร์หรือนำกลับมาใช้ใหม่ในภายหลัง
จากมุมมองของฉันเอง ความเข้าใจของฉันเกี่ยวกับการเขียนโปรแกรมเชิงฟังก์ชัน JavaScript อาจแตกต่างจากแนวคิดดั้งเดิมหลายประการ ฉันไม่เพียงแต่คิดว่าฟังก์ชันที่มีลำดับสูงเท่านั้นที่นับว่าเป็นการเขียนโปรแกรมเชิงฟังก์ชัน ฟังก์ชันอื่นๆ เช่น การเรียกฟังก์ชันทั่วไป โครงสร้างลูกโซ่ ฯลฯ ฉันคิดว่าอยู่ในหมวดหมู่ของการเขียนโปรแกรมเชิงฟังก์ชัน ตราบใดที่ฟังก์ชันเหล่านั้นใช้ฟังก์ชันหลัก ผู้ให้บริการ
และฉันคิดว่าการเขียนโปรแกรมเชิงฟังก์ชันไม่จำเป็น และไม่ควรเป็นกฎหรือข้อกำหนดบังคับ เช่นเดียวกับแนวคิดเชิงวัตถุหรือแนวคิดอื่นๆ นี่ก็เป็นวิธีหนึ่งเช่นกัน ในกรณีส่วนใหญ่ เราควรจะผสมผสานหลายๆ อย่างเข้าด้วยกัน แทนที่จะจำกัดอยู่เพียงแนวคิดเท่านั้น
คำแนะนำที่เกี่ยวข้อง: บทช่วยสอน JavaScript
ข้างต้นคือการอภิปรายโดยละเอียดเกี่ยวกับการเขียนโปรแกรมการทำงานของ JavaScript สำหรับข้อมูลเพิ่มเติม โปรดใส่ใจกับบทความอื่น ๆ ที่เกี่ยวข้องบนเว็บไซต์ PHP ภาษาจีน