摘要:在做了一些研究之后,我發(fā)現(xiàn)了函數(shù)式編程的概念,比如不變性和純函數(shù)。在這篇文章中,將通大量代碼示例來(lái)詳細(xì)介紹函數(shù)式編程和一些相關(guān)重要概念。該非純函數(shù)接收該值并重新分配,使其值增加。函數(shù)式編程不鼓勵(lì)可變性。純函數(shù)是穩(wěn)定的一致的和可預(yù)測(cè)的。
為了保證的可讀性,本文采用意譯而非直譯。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!
在長(zhǎng)時(shí)間學(xué)習(xí)和使用面向?qū)ο缶幊讨螅蹅兺艘徊絹?lái)考慮系統(tǒng)復(fù)雜性。
在做了一些研究之后,我發(fā)現(xiàn)了函數(shù)式編程的概念,比如不變性和純函數(shù)。這些概念使你能夠構(gòu)建無(wú)副作用的函數(shù),因此更容易維護(hù)具有其他優(yōu)點(diǎn)的系統(tǒng)。
在這篇文章中,將通大量代碼示例來(lái)詳細(xì)介紹函數(shù)式編程和一些相關(guān)重要概念。
什么是函數(shù)式編程函數(shù)式編程是一種編程范式,是一種構(gòu)建計(jì)算機(jī)程序結(jié)構(gòu)和元素的風(fēng)格,它把計(jì)算看作是對(duì)數(shù)學(xué)函數(shù)的評(píng)估,避免了狀態(tài)的變化和數(shù)據(jù)的可變。
純函數(shù)當(dāng)我們想要理解函數(shù)式編程時(shí),需要知道的第一個(gè)基本概念是純函數(shù),但純函數(shù)又是什么鬼?
咱們?cè)趺粗酪粋€(gè)函數(shù)是否是純函數(shù)?這里有一個(gè)非常嚴(yán)格的定義:
如果給定相同的參數(shù),則返回相同的結(jié)果(也稱為確定性)。
它不會(huì)引起任何副作用。
如果給定相同的參數(shù),則得到相同的結(jié)果如果給出相同的參數(shù),它返回相同的結(jié)果。 想象一下,我們想要實(shí)現(xiàn)一個(gè)計(jì)算圓的面積的函數(shù)。
不是純函數(shù)會(huì)這樣做,接收radius 作為參數(shù),然后計(jì)算radius * radius * PI:
let PI = 3.14; const calculateArea = (radius) => radius * radius * PI; calculateArea(10); // returns 314.0
為什么這是一個(gè)不純函數(shù)?原因很簡(jiǎn)單,因?yàn)樗褂昧艘粋€(gè)沒(méi)有作為參數(shù)傳遞給函數(shù)的全局對(duì)象。
現(xiàn)在,想象一些數(shù)學(xué)家認(rèn)為圓周率的值實(shí)際上是42并且修改了全局對(duì)象的值。
不純函數(shù)得到10 * 10 * 42 = 4200。對(duì)于相同的參數(shù)(radius = 10),我們得到了不同的結(jié)果。
修復(fù)它:
let PI = 3.14; const calculateArea = (radius, pi) => radius * radius * pi; calculateArea(10, PI); // returns 314.0
現(xiàn)在把 PI 的值作為參數(shù)傳遞給函數(shù),這樣就沒(méi)有外部對(duì)象引入。
對(duì)于參數(shù)radius = 10和PI = 3.14,始終都會(huì)得到相同的結(jié)果:314.0。
對(duì)于 radius = 10 和 PI = 42,總是得到相同的結(jié)果:4200
讀取文件下面函數(shù)讀取外部文件,它不是純函數(shù),文件的內(nèi)容隨時(shí)可能都不一樣。
const charactersCounter = (text) => `Character count: ${text.length}`; function analyzeFile(filename) { let fileContent = open(filename); return charactersCounter(fileContent); }隨機(jī)數(shù)生成
任何依賴于隨機(jī)數(shù)生成器的函數(shù)都不能是純函數(shù)。
function yearEndEvaluation() { if (Math.random() > 0.5) { return "You get a raise!"; } else { return "Better luck next year!"; } }無(wú)明顯副作用
純函數(shù)不會(huì)引起任何可觀察到的副作用??梢?jiàn)副作用的例子包括修改全局對(duì)象或通過(guò)引用傳遞的參數(shù)。
現(xiàn)在,咱們要實(shí)現(xiàn)一個(gè)函數(shù),該接收一個(gè)整數(shù)并返對(duì)該整數(shù)進(jìn)行加1操作且返回。
let counter = 1; function increaseCounter(value) { counter = value + 1; } increaseCounter(counter); console.log(counter); // 2
該非純函數(shù)接收該值并重新分配counter,使其值增加1。
函數(shù)式編程不鼓勵(lì)可變性。我們修改全局對(duì)象,但是要怎么做才能讓它變得純函數(shù)呢?只需返回增加1的值。
let counter = 1; const increaseCounter = (value) => value + 1; increaseCounter(counter); // 2 console.log(counter); // 1
純函數(shù)increaseCounter返回2,但是counter值仍然是相同的。函數(shù)返回遞增的值,而不改變變量的值。
如果我們遵循這兩條簡(jiǎn)單的規(guī)則,就會(huì)更容易理解我們的程序。現(xiàn)在每個(gè)函數(shù)都是孤立的,不能影響系統(tǒng)的其他部分。
純函數(shù)是穩(wěn)定的、一致的和可預(yù)測(cè)的。給定相同的參數(shù),純函數(shù)總是返回相同的結(jié)果。
咱們不需要考慮相同參數(shù)有不同結(jié)果的情況,因?yàn)樗肋h(yuǎn)不會(huì)發(fā)生。
純函數(shù)的好處純函數(shù)代碼肯定更容易測(cè)試,不需要 mock 任何東西,因此,我們可以使用不同的上下文對(duì)純函數(shù)進(jìn)行單元測(cè)試:
給定一個(gè)參數(shù) A,期望函數(shù)返回值 B
給定一個(gè)參數(shù)C,期望函數(shù)返回值D
一個(gè)簡(jiǎn)單的例子是接收一組數(shù)字,并對(duì)每個(gè)數(shù)進(jìn)行加 1 這種沙雕的操作。
let list = [1, 2, 3, 4, 5]; const incrementNumbers = (list) => list.map(number => number + 1);
接收numbers數(shù)組,使用map遞增每個(gè)數(shù)字,并返回一個(gè)新的遞增數(shù)字列表。
incrementNumbers(list); // [2, 3, 4, 5, 6]
對(duì)于輸入[1,2,3,4,5],預(yù)期輸出是[2,3,4,5,6]。
不可變性盡管時(shí)間變或者不變,純函數(shù)大佬都是不變的。
當(dāng)數(shù)據(jù)是不可變的時(shí),它的狀態(tài)在創(chuàng)建后不能更改。
咱們不能更改不可變對(duì)象,如果非要來(lái)硬的,剛需要深拷貝一個(gè)副本,然后操作這個(gè)副本。
在JS中,我們通常使用for循環(huán),for的每次遍歷 i是個(gè)可變變量。
var values = [1, 2, 3, 4, 5]; var sumOfValues = 0; for (var i = 0; i < values.length; i++) { sumOfValues += values[i]; } sumOfValues // 15
對(duì)于每次遍歷,都在更改i和sumOfValue狀態(tài),但是我們?nèi)绾卧诒闅v中處理可變性呢? 答案就是使用遞歸。
let list = [1, 2, 3, 4, 5]; let accumulator = 0; function sum(list, accumulator) { if (list.length == 0) { return accumulator; } return sum(list.slice(1), accumulator + list[0]); } sum(list, accumulator); // 15 list; // [1, 2, 3, 4, 5] accumulator; // 0
上面代碼有個(gè) sum 函數(shù),它接收一個(gè)數(shù)值向量。函數(shù)調(diào)用自身,直到 list為空退出遞歸。對(duì)于每次“遍歷”,我們將把值添加到總accumulator中。
使用遞歸,咱們保持變量不變。不會(huì)更改list和accumulator變量。它保持相同的值。
觀察:我們可以使用reduce來(lái)實(shí)現(xiàn)這個(gè)功能。這個(gè)在接下的高階函數(shù)內(nèi)容中討論。
構(gòu)建對(duì)象的最終狀態(tài)也很常見(jiàn)。假設(shè)我們有一個(gè)字符串,想把這個(gè)字符串轉(zhuǎn)換成url slug。
在Ruby的面向?qū)ο缶幊讨?,咱們可以?chuàng)建一個(gè)類 UrlSlugify,這個(gè)類有一個(gè)slugify方法來(lái)將字符串輸入轉(zhuǎn)換為url slug。
class UrlSlugify attr_reader :text def initialize(text) @text = text end def slugify! text.downcase! text.strip! text.gsub!(" ", "-") end end UrlSlugify.new(" I will be a url slug ").slugify! # "i-will-be-a-url-slug"
上面使用的有命令式編程方式,首先用小寫字母表示我們想在每個(gè)slugify進(jìn)程中做什么,然后刪除無(wú)用的空格,最后用連字符替換剩余的空格。
這種方式在整個(gè)過(guò)程中改變了輸入狀態(tài),顯然不符合純函數(shù)的概念。
這邊可以通過(guò)函數(shù)組合或函數(shù)鏈來(lái)來(lái)優(yōu)化。換句話說(shuō),函數(shù)的結(jié)果將用作下一個(gè)函數(shù)的輸入,而不修改原始輸入字符串。
const string = " I will be a url slug "; const slugify = string => string .toLowerCase() .trim() .split(" ") .join("-"); slugify(string); // i-will-be-a-url-slug
上述代碼主要做了這幾件事:
toLowerCase:將字符串轉(zhuǎn)換為所有小寫字母。
trim:刪除字符串兩端的空白。
split和join:用給定字符串中的替換替換所有匹配實(shí)例
引用透明性接著實(shí)現(xiàn)一個(gè)square 函數(shù):
const square = (n) => n * n;
給定相同的輸入,這個(gè)純函數(shù)總是有相同的輸出。
square(2); // 4 square(2); // 4 square(2); // 4 // ...
將2作為square函數(shù)的參數(shù)傳遞始終會(huì)返回4。這樣咱們可以把square(2)換成4,我們的函數(shù)就是引用透明的。
基本上,如果一個(gè)函數(shù)對(duì)于相同的輸入始終產(chǎn)生相同的結(jié)果,那么它可以看作透明的。
有了這個(gè)概念,咱們可以做的一件很酷的事情就是記住這個(gè)函數(shù)。假設(shè)有這樣的函數(shù)
const sum = (a, b) => a + b;
用這些參數(shù)來(lái)調(diào)用它
sum(3, sum(5, 8));
sum(5, 8) 總等于13,所以可以做些騷操作:
sum(3, 13);
這個(gè)表達(dá)式總是得到16,咱們可以用一個(gè)數(shù)值常數(shù)替換整個(gè)表達(dá)式,并把它記下來(lái)。
函數(shù)是 JS 中的一級(jí)公民函數(shù)作為 JS 中的一級(jí)公民,很風(fēng)騷,函數(shù)也可以被看作成值并用作數(shù)據(jù)使用。
從常量和變量中引用它。
將其作為參數(shù)傳遞給其他函數(shù)。
作為其他函數(shù)的結(jié)果返回它。
其思想是將函數(shù)視為值,并將函數(shù)作為數(shù)據(jù)傳遞。通過(guò)這種方式,我們可以組合不同的函數(shù)來(lái)創(chuàng)建具有新行為的新函數(shù)。
假如我們有一個(gè)函數(shù),它對(duì)兩個(gè)值求和,然后將值加倍,如下所示:
const doubleSum = (a, b) => (a + b) * 2;
對(duì)應(yīng)兩個(gè)值求差,然后將值加倍:
const doubleSubtraction = (a, b) => (a - b) * 2;
這些函數(shù)具有相似的邏輯,但區(qū)別在于運(yùn)算符的功能。 如果我們可以將函數(shù)視為值并將它們作為參數(shù)傳遞,我們可以構(gòu)建一個(gè)接收運(yùn)算符函數(shù)并在函數(shù)內(nèi)部使用它的函數(shù)。
const sum = (a, b) => a + b; const subtraction = (a, b) => a - b; const doubleOperator = (f, a, b) => f(a, b) * 2; doubleOperator(sum, 3, 1); // 8 doubleOperator(subtraction, 3, 1); // 4
f參數(shù)并用它來(lái)處理a和b, 這里傳遞了sum函數(shù)和subtraction并使用doubleOperator函數(shù)進(jìn)行組合并創(chuàng)建新行為。
高階函數(shù)當(dāng)我們討論高階函數(shù)時(shí),通常包括以下幾點(diǎn):
將一個(gè)或多個(gè)函數(shù)作為參數(shù)
返回一個(gè)函數(shù)作為結(jié)果
上面實(shí)現(xiàn)的doubleOperator函數(shù)是一個(gè)高階函數(shù),因?yàn)樗鼘⒁粋€(gè)運(yùn)算符函數(shù)作為參數(shù)并使用它。
我們經(jīng)常用的filter、map和reduce都是高階函數(shù),Look see see。
Filter對(duì)于給定的集合,我們希望根據(jù)屬性進(jìn)行篩選。filter函數(shù)期望一個(gè)true或false值來(lái)決定元素是否應(yīng)該包含在結(jié)果集合中。
如果回調(diào)表達(dá)式為真,過(guò)濾器函數(shù)將在結(jié)果集合中包含元素,否則,它不會(huì)。
一個(gè)簡(jiǎn)單的例子是,當(dāng)我們有一個(gè)整數(shù)集合,我們只想要偶數(shù)。
命令式使用命令式方式來(lái)獲取數(shù)組中所有的偶數(shù),通常會(huì)這樣做:
創(chuàng)建一個(gè)空數(shù)組evenNumbers
遍歷數(shù)組 numbers
將偶數(shù) push 到evenNumbers數(shù)組中
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = []; for (var i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) { evenNumbers.push(numbers[i]); } } console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]
我們還可以使用filter高階函數(shù)來(lái)接收偶函數(shù)并返回一個(gè)偶數(shù)列表:
const even = n => n % 2 == 0;
const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10]
我在 Hacker Rank FP 上解決的一個(gè)有趣問(wèn)題是Filter Array問(wèn)題。 問(wèn)題是過(guò)濾給定的整數(shù)數(shù)組,并僅輸出小于指定值X的那些值。
命令式做法通常是這樣的:
var filterArray = function(x, coll) { var resultArray = []; for (var i = 0; i < coll.length; i++) { if (coll[i] < x) { resultArray.push(coll[i]); } } return resultArray; } console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]聲明式方式
對(duì)于上面的總是,我們更想要一種更聲明性的方法來(lái)解決這個(gè)問(wèn)題,如下所示:
function smaller(number) { return number < this; } function filterArray(x, listOfNumbers) { return listOfNumbers.filter(smaller, x); } let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0]; filterArray(3, numbers); // [2, 1, 0]
在smaller的函數(shù)中使用 this,一開(kāi)始看起來(lái)有點(diǎn)奇怪,但是很容易理解。
filter函數(shù)中的第二個(gè)參數(shù)表示上面 this, 也就是 x 值。
我們也可以用map方法做到這一點(diǎn)。想象一下,有一組信息
let people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]
我們希望過(guò)濾 age 大于 21 歲的人,用 filter 方式
const olderThan21 = person => person.age > 21; const overAge = people => people.filter(olderThan21); overAge(people); // [{ name: "TK", age: 26 }, { name: "Kazumi", age: 30 }]map
map函數(shù)的主要思路是轉(zhuǎn)換集合。
map方法通過(guò)將函數(shù)應(yīng)用于其所有元素并根據(jù)返回的值構(gòu)建新集合來(lái)轉(zhuǎn)換集合。
假如我們不想過(guò)濾年齡大于 21 的人,我們想做的是顯示類似這樣的:TK is 26 years old.
使用命令式,我們通常會(huì)這樣做:
var people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; var peopleSentences = []; for (var i = 0; i < people.length; i++) { var sentence = people[i].name + " is " + people[i].age + " years old"; peopleSentences.push(sentence); } console.log(peopleSentences); // ["TK is 26 years old", "Kaio is 10 years old", "Kazumi is 30 years old"]
聲明式會(huì)這樣做:
const makeSentence = (person) => `${person.name} is ${person.age} years old`; const peopleSentences = (people) => people.map(makeSentence); peopleSentences(people); // ["TK is 26 years old", "Kaio is 10 years old", "Kazumi is 30 years old"]
整個(gè)思想是將一個(gè)給定的數(shù)組轉(zhuǎn)換成一個(gè)新的數(shù)組。
另一個(gè)有趣的HackerRank問(wèn)題是更新列表問(wèn)題。我們想要用一個(gè)數(shù)組的絕對(duì)值來(lái)更新它的值。
例如,輸入[1,2,3,- 4,5]需要輸出為[1,2,3,4,5],-4的絕對(duì)值是4。
一個(gè)簡(jiǎn)單的解決方案是每個(gè)集合中值的就地更新,很危險(xiǎn)的作法
var values = [1, 2, 3, -4, 5]; for (var i = 0; i < values.length; i++) { values[i] = Math.abs(values[i]); } console.log(values); // [1, 2, 3, 4, 5]
我們使用Math.abs函數(shù)將值轉(zhuǎn)換為其絕對(duì)值并進(jìn)行就地更新。
這種方式不是最做解。
首先,前端我們學(xué)習(xí)了不變性,知道不可變性讓函數(shù)更加一致和可預(yù)測(cè),咱們的想法是建立一個(gè)具有所有絕對(duì)值的新集合。
其次,為什么不在這里使用map來(lái)“轉(zhuǎn)換”所有數(shù)據(jù)
我的第一個(gè)想法是測(cè)試Math.abs函數(shù)只處理一個(gè)值。
Math.abs(-1); // 1 Math.abs(1); // 1 Math.abs(-2); // 2 Math.abs(2); // 2
我們想把每個(gè)值轉(zhuǎn)換成一個(gè)正值(絕對(duì)值)。
現(xiàn)在知道如何對(duì)一個(gè)值執(zhí)行絕對(duì)值操作,可以使用此函數(shù)作為參數(shù)傳遞給map函數(shù)。
還記得高階函數(shù)可以接收函數(shù)作為參數(shù)并使用它嗎? 是的,map函數(shù)可以做到這一點(diǎn)
let values = [1, 2, 3, -4, 5]; const updateListMap = (values) => values.map(Math.abs); updateListMap(values); // [1, 2, 3, 4, 5]Reduce
reduce函數(shù)的思想是接收一個(gè)函數(shù)和一個(gè)集合,并返回通過(guò)組合這些項(xiàng)創(chuàng)建的值。
常見(jiàn)的的一個(gè)例子是獲取訂單的總金額。
假設(shè)你在一個(gè)購(gòu)物網(wǎng)站,已經(jīng)將產(chǎn)品1、產(chǎn)品2、產(chǎn)品3和產(chǎn)品4添加到購(gòu)物車(訂單)中?,F(xiàn)在,我們要計(jì)算購(gòu)物車的總數(shù)量:
以命令式的方式,就是便利訂單列表并將每個(gè)產(chǎn)品金額與總金額相加。
var orders = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; var totalAmount = 0; for (var i = 0; i < orders.length; i++) { totalAmount += orders[i].amount; } console.log(totalAmount); // 120
使用reduce,我們可以構(gòu)建一個(gè)函數(shù)來(lái)處理量計(jì)算sum并將其作為參數(shù)傳遞給reduce函數(shù)。
let shoppingCart = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount; const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0); getTotalAmount(shoppingCart); // 120
這里有shoppingCart,接收當(dāng)前currentTotalAmount的函數(shù)sumAmount,以及對(duì)它們求和的order對(duì)象。
咱們也可以使用map將shoppingCart轉(zhuǎn)換為一個(gè)amount集合,然后使用reduce函數(shù)和sumAmount函數(shù)。
const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;
function getTotalAmount(shoppingCart) { return shoppingCart .map(getAmount) .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120
getAmount接收product對(duì)象并只返回amount值,即[10,30,20,60],然后,reduce通過(guò)相加將所有項(xiàng)組合起來(lái)。
三個(gè)函數(shù)的示例看了每個(gè)高階函數(shù)的工作原理。這里為你展示一個(gè)示例,說(shuō)明如何在一個(gè)簡(jiǎn)單的示例中組合這三個(gè)函數(shù)。
說(shuō)到購(gòu)物車,假設(shè)我們的訂單中有這個(gè)產(chǎn)品列表
let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ]
假如相要想要購(gòu)物車?yán)镱愋蜑?books的總數(shù),通常會(huì)這樣做:
過(guò)濾 type 為 books的
使用map將購(gòu)物車轉(zhuǎn)換為amount集合。
用reduce將所有項(xiàng)加起來(lái)。
let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] const byBooks = (order) => order.type == "books"; const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart .filter(byBooks) .map(getAmount) .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 70
代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。
交流干貨系列文章匯總?cè)缦拢X(jué)得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛(ài)好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106465.html
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開(kāi)發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開(kāi)發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...
摘要:插件開(kāi)發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開(kāi)發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
閱讀 2003·2021-11-22 15:33
閱讀 3025·2021-11-18 10:02
閱讀 2640·2021-11-08 13:16
閱讀 1654·2021-10-09 09:57
閱讀 1397·2021-09-30 09:47
閱讀 2033·2019-08-29 13:05
閱讀 3096·2019-08-29 12:46
閱讀 1035·2019-08-29 12:19