摘要:前言整理中一些相似的關(guān)鍵字方法概念。于是我們修改上面的函數(shù)來驗證它們的區(qū)別小明擼代碼擼代碼小紅小黑小剛小紅小黑擼代碼小李小謝小剛小李小謝擼代碼那么與有什么區(qū)別呢與和不同的是,綁定后不會立即執(zhí)行。通常用來處理一些并發(fā)的異步操作。
前言
整理 javascript 中一些相似的關(guān)鍵字、方法、概念。
1. var、function、let、const 命令的區(qū)別使用var聲明的變量,其作用域為該語句所在的函數(shù)內(nèi),且存在變量提升現(xiàn)象
使用let聲明的變量,其作用域為該語句所在的代碼塊內(nèi),不存在變量提升
使用const聲明的是常量,在后面出現(xiàn)的代碼中不能再修改該常量的棧內(nèi)存在的值和地址
使用function聲明的函數(shù),其作用域為該語句所在的函數(shù)內(nèi),且存在函數(shù)提升現(xiàn)象
var
//a. 變量提升 console.log(a) // => undefined var a = 123 //b. 作用域 function f() { var a = 123 console.log(a) // => 123 } console.log(a) // => a is not defined for (var i = 0; i < 10; i ++) {} console.log(i) // => 10
let
//a. 變量不提升 console.log(a) // => a is not defined let a = 123 //b. 作用域為所在代碼塊內(nèi) for (let i = 0; i < 10; i ++) {} console.log(i) // => i is not defined
const
//a. 不能修改的是棧內(nèi)存在的值和地址 const a = 10 a = 20 // => Assignment to constant variable // 但是以下的賦值確是合法的 const a = { b: 20 } a.b = 30 console.log(a.b) // => 30
function
//a. 函數(shù)提升 fn() // => 123 function fn() { return 123 } //b. 作用域 function fn() { function fn1 () { return 123456 } fn1() // => 123456 } fn1() // => fn1 is not defined
經(jīng)典面試題
var a = 1 function fn() { if (!a) { var a = 123 } console.log(a) } fn() ?
// 如何依次打印出0 - 9
for (var i = 0; i < 10; i++) { setTimeout(function(){ console.log(i) }) }
function Foo() { getName = function(){ console.log("1"); }; return this; } Foo.getName = function() { console.log("2"); }; Foo.prototype.getName = function(){ console.log("3"); }; var getName = function() { console.log("4"); } function getName(){ console.log("5"); } Foo.getName(); ? getName(); ? Foo().getName(); ? getName(); ? new Foo.getName(); ? new Foo().getName(); ?
答案:
第一題
//我們把它執(zhí)行順序整理下 var a = 1 function fn() { var a = nudefined if (!a) { var a = 123 } console.log(a) } //所以 答案很明顯 就是 123
第2題
for (var i = 0; i < 10; i++) { print(i) } function print(i) { // 把每個變量i值傳進來,變成只可當前作用域訪問的局部變量 setTimeout(function(){ console.log(i) }) } // 或者自執(zhí)行函數(shù)簡寫 for (var i = 0; i < 10; i++) { (function(i){ setTimeout(function(){ console.log(i) }) })(i) }
第3題
// 我們整理下它的執(zhí)行順序 var getName = undefined function Foo() { getName = function(){ console.log("1"); }; return this; } function getName(){ console.log("5"); } Foo.getName = function() { console.log("2"); }; Foo.prototype.getName = function(){ console.log("3"); }; getName = function() { console.log("4"); } Foo.getName(); // 2 /* 函數(shù)也是對象, Foo.getName 相當于給 Foo這個對象添加了一個靜態(tài)方法 getName,我們調(diào)用的其實是這個靜態(tài)方法,并不是調(diào)用的我們實例化的 getName */ getName(); // 4 /* 按照上面的執(zhí)行順序,其實這個就很好理解了,因為 `getName = function() { console.log("4"); }` 是最后一個賦值, 執(zhí)行的應(yīng)該是這個函數(shù) */ Foo().getName(); // 1 /* 這里為什么是 1 而不是我們想象的 3 呢? 問題就是出在 調(diào)用的是 Foo(); 并沒有使用 new 這個關(guān)鍵字,所以那時候返回的 this 指向的并不是 Foo, 而是 window; 至于為什么不用 new 返回的 this 不指向 Foo, 這個隨便去哪查一下就好, 就不在這介紹了 */ getName(); // 1 /* 這里為什么也是1 呢? 其實原因就是 上面我們調(diào)用了 `Foo().getName();` 這個方法引起的, 因為我們執(zhí)行了 Foo 函數(shù), 觸發(fā)了 getName = function(){ console.log("1"); } 這段代碼, 而且并沒有在Foo里面聲明 getName 變量, 于是就一直往上查找, 找到外部的 getName 變量 并賦值給它. 所以這里調(diào)用 getName() 方法時, 它的值已經(jīng)變成 getName = function(){ console.log("1"); } 了 */ new Foo.getName(); // 2 /*這個時候還是沒有實例化, 調(diào)用的還是它的靜態(tài)方法*/ new Foo().getName(); // 3 /*因為實例化了,所以調(diào)的是原型上的方法*/
我記得看到過幾個經(jīng)典的例子,找了半天沒找到, 暫時就這些吧.。
2. == 與 === 的區(qū)別
相同點:
它們兩個運算符都允許任意類型的的操作數(shù),如果操作數(shù)相等,返回true,否則返回false
不同點:
==:運算符稱作相等,用來檢測兩個操作數(shù)是否相等,這里的相等定義的非常寬松,可以允許進行類型轉(zhuǎn)換
===:用來檢測兩個操作數(shù)是否嚴格相等,不會進行類型轉(zhuǎn)換
== 轉(zhuǎn)換規(guī)則
首先看雙等號前后有沒有NaN,如果存在NaN,一律返回false。
再看雙等號前后有沒有布爾,有布爾就將布爾轉(zhuǎn)換為數(shù)字。(false是0,true是1)
接著看雙等號前后有沒有字符串, 有三種情況:
a. 對方是對象,對象使用toString()或者valueOf()進行轉(zhuǎn)換;
b. 對方是數(shù)字,字符串轉(zhuǎn)數(shù)字;
c. 對方是字符串,直接比較;
d. 其他返回false
如果是數(shù)字,對方是對象,對象取valueOf()或者toString()進行比較, 其他一律返回false
null, undefined不會進行類型轉(zhuǎn)換, 但它們倆相等
// 不同類型,相同值 var a = 1 var b = "1" console.log(a == b) // => true console.log(a === b) // => false // 對象和字符串 console.log([1,2,3] == "1,2,3") // => true 因為 [1,2,3]調(diào)用了 toString()方法進行轉(zhuǎn)換 // 對象和布爾 console.log([] == true) // => false []轉(zhuǎn)換為字符串"",然后轉(zhuǎn)換為數(shù)字0, true 轉(zhuǎn)換成1 // 對象和數(shù)字 console.log(["1"] == 1) // => true []轉(zhuǎn)換為字符串"1" console.log(2 == {valueOf: function(){return 2}}) // => true 調(diào)用了 valueOf()方法進行轉(zhuǎn)換 // null, undefined 不會進行類型轉(zhuǎn)換, 但它們倆相等 console.log(null == 1) // => false console.log(null == 0) // => false console.log(undefined == 1) // => false console.log(undefined == 0) // => false console.log(null == false) // => false console.log(undefined == false) // => false console.log(null == undefined) // => true console.log(null === undefined) // => false // NaN 跟任何東西都不相等(包括自己) console.log(NaN == NaN) // => false console.log(NaN === NaN) // => false
下面幾張圖表示這些 == === 的關(guān)系
==
===
所有對象繼承了這兩個轉(zhuǎn)換方法
toString: 返回一個反映這個對象的字符串
valueOf: 返回它相應(yīng)的原始值
toString
var arr = [1,2,3] var obj = { a: 1, b: 2 } console.log(arr.toString()) // => 1,2,3 console.log(obj.toString()) // => [object Object] // 那我們修改一下它原型上的 toString 方法呢 Array.prototype.toString = function(){ return 123 } Object.prototype.toString = function(){ return 456 } console.log(arr.toString()) // => 123 console.log(obj.toString()) // => 456 // 我們看下其余類型轉(zhuǎn)換出來的結(jié)果, 基本都是轉(zhuǎn)換成了字符串 console.log((new Date).toString()) // => Mon Feb 05 2018 17:45:47 GMT+0800 (中國標準時間) console.log(/d+/g.toString()) // => "/d+/g" console.log((new RegExp("asdad", "ig")).toString()) // => "/asdad/gi" console.log(true.toString()) // => "true" console.log(false.toString()) // => "false" console.log(function(){console.log(1)}.toString()) // => "function (){console.log(1)}" console.log(Math.random().toString()) // => "0.2609205380591437"
valueOf
var arr = [1,2,3] var obj = { a: 1, b: 2 } console.log(arr.valueOf()) // => [1, 2, 3] console.log(obj.valueOf()) // => {a: 1, b: 2} // 證明valueOf返回的是自身的原始值 // 同樣我們修改下 valueOf 方法 Array.prototype.valueOf = function(){ return 123 } Object.prototype.valueOf = function(){ return 456 } console.log(arr.valueOf()) // => 123 console.log(obj.valueOf()) // => 456 // valueOf轉(zhuǎn)化出來的基本都是原始值,復(fù)雜數(shù)據(jù)類型Object返回都是本身,除了Date 返回的是時間戳 console.log((new Date).valueOf()) // => 1517824550394 //返回的并不是字符串的世界時間了,而是時間戳 console.log(/d+/g.valueOf()) // => 456 當我們不設(shè)置時valueOf時,正常返回的正則表式本身:/d+/g,只是我們設(shè)置了 Object.prototype.valueOf 所以返回的時:456 console.log(Math.valueOf()) // => 456 同上 console.log(function(){console.log(1)}.valueOf()) // => 456 同上
toString 和 valueOf 實例
var a = { toString: function() { console.log("你調(diào)用了a的toString函數(shù)") return 8 } } console.log( ++a) // 你調(diào)用了a的toString函數(shù) // 9 // 當你設(shè)置了 toString 方法, 沒有設(shè)置 valueOf 方法時,會調(diào)用toString方法,無視valueOf方法
var a = { num: 10, toString: function() { console.log("你調(diào)用了a的toString函數(shù)") return 8 }, valueOf: function() { console.log("你調(diào)用了a的valueOf函數(shù)") return this.num } } console.log( ++a) // 你調(diào)用了a的valueOf函數(shù) // 11 // 而當你兩者都設(shè)置了的時候,會優(yōu)先取valueOf方法, 不會執(zhí)行toString方法4. || 和 && 的區(qū)別
如果以 “||” 和 “&&” 做條件判斷的話
“||” 只要其中有一個為 true 那么就滿足條件
“&&” 必須要所有條件都為 true 才能滿足條件
var a = true,b = false, c = true, d = false var str = "none" if (b || d || a) { str = "現(xiàn)在是 ||" } console.log(str) // => "現(xiàn)在是 ||" ,因為其中a為true所有滿足條件 var str = "none" if (b || d ) { str = "現(xiàn)在是 ||" } console.log(str) // => "none" ,因為b,d都是false, 不滿足條件 var str = "none" if (a && c && d) { str = "現(xiàn)在是 &&" } console.log(str) // => "none" ,因為d是false, 其中有一個false就不滿足條件 var str = "none" if (a && c) { str = "現(xiàn)在是 &&" } console.log(str) // => "現(xiàn)在是 &&" ,因為b,d都是true, 滿足條件
短路原理:
||(或):
1.只要“||”前面是true,結(jié)果會返回“||”前面的值
2.如果“||”前面是false,結(jié)果都會“||”返回后面的值
var a = true,b = false, c = true, d = false var str = "none" if (b || d || a) { str = "現(xiàn)在是 ||" } console.log(str) // => "現(xiàn)在是 ||" ,因為其中a為true所有滿足條件 var str = "none" if (b || d ) { str = "現(xiàn)在是 ||" } console.log(str) // => "none" ,因為b,d都是false, 不滿足條件 var str = "none" if (a && c && d) { str = "現(xiàn)在是 &&" } console.log(str) // => "none" ,因為d是false, 其中有一個false就不滿足條件 var str = "none" if (a && c) { str = "現(xiàn)在是 &&" } console.log(str) // => "現(xiàn)在是 &&" ,因為b,d都是true, 滿足條件
&&(與):
1.只要“&&”前面是false,無論“&&”后面是true還是false,結(jié)果都將返“&&”前面的值
2.只要“&&”前面是true,無論“&&”后面是true還是false,結(jié)果都將返“&&”后面的值
var a = false, b = true console.log(a && b) // => false 只要“&&”前面是false,無論“&&”后面是true還是false,結(jié)果都將返“&&”前面的值 console.log(b && a) // => false 只要“&&”前面是true,無論“&&”后面是true還是false,結(jié)果都將返“&&”后面的值5. call/bind/apply 的區(qū)別
var name = "小剛" var person = { name: "小明", fn: function() { console.log(this.name + "擼代碼") } } person.fn() // => 小明擼代碼 // 如何把它變成 “小剛擼代碼” 呢? // 我們可以用 call/bind/apply 分別來實現(xiàn) person.fn.call(window) // => 小剛擼代碼 person.fn.apply(window) // => 小剛擼代碼 person.fn.bind(window)() // => 小剛擼代碼
顯而易見,call 和 apply 更加類似,bind與兩者形式不同
那 call 和 apply 的區(qū)別在哪呢?
obj.call(thisObj, arg1, arg2, ...) obj.apply(thisObj, [arg1, arg2, ...]) // 通過上面的參數(shù)我們可以看出, 它們之間的區(qū)別是apply接受的是數(shù)組參數(shù),call接受的是連續(xù)參數(shù)。 // 于是我們修改上面的函數(shù)來驗證它們的區(qū)別 var person = { name: "小明", fn: function(a,b) { if ({}.toString.call(a).slice(8, -1) === "Array") { console.log(this.name+","+a.toString()+"擼代碼") }else{ console.log(this.name+","+a+","+b+"擼代碼") } } } person.fn.call(this, "小紅", "小黑" ) // => 小剛,小紅,小黑擼代碼 person.fn.apply(this, ["小李", "小謝"]) // => 小剛,小李,小謝擼代碼
那么bind 與call,apply有什么區(qū)別呢 ?
與call和apply不同的是,bind綁定后不會立即執(zhí)行。它只會將該函數(shù)的 this 指向確定好,然后返回該函數(shù)
var name = "小紅" var obj = { name: "小明", fn: function(){ console.log("我是"+this.name) } } setTimeout(obj.fn, 1000) // => 我是小紅 // 我們可以用bind方法打印出 "我是小明" setTimeout(obj.fn.bind(obj), 1000) // => 我是小明 // 這個地方就不能用 call 或 apply 了, 不然我們把函數(shù)剛一方去就執(zhí)行了 // 注意: bind()函數(shù)是在 ECMA-262 第五版才被加入 // 所以 你想兼容低版本的話 ,得需要自己實現(xiàn) bind 函數(shù) Function.prototype.bind = function (oThis) { if (typeof this !== "function") { throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply( this instanceof fNOP && oThis ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)) ); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; };6. callback 、 promise 、 async/await
這三個東西牽涉到的可能就是我們最常見到的 “同步”、“異步”、“任務(wù)隊列”、“事件循環(huán)” 這幾個概念了
例:
var data; $.ajax({ ... success: function(data) { data = data } }) console.log(data)
當我們從服務(wù)器獲取到數(shù)據(jù)的時候,為什么打印出來的是undefined ?
解決這個問題之前我們先來了解javascript的運行環(huán)境
JavaScript是單線程語言,JS中所有的任務(wù)可以分為兩種:同步任務(wù)和異步任務(wù)。
同步任務(wù):
意思是我必須做完第一件事,才能做第二件事,按照順序一件一件往下執(zhí)行(在主線程上)
異步任務(wù):
假如我第一件事需要花費 10s, 但是我第二件事急著要做, 于是我們就把第一件事告訴主線程,然后主線程暫停先放到某個地方, 等把第二件事完成之后,再去那個地方執(zhí)行第一件事,第一件事也就可以理解為異步任務(wù)
任務(wù)隊列(task queue):
任務(wù)隊列是干嘛的呢; 上面我們說了異步任務(wù)的情況, 我們把第一件放到某個地方, 那某個地方是什么地方呢,就是 “任務(wù)隊列” 這個東西。里面乘放的是所有異步任務(wù)。
Event Loop(事件循環(huán))
當主線程上面所有同步任務(wù)執(zhí)行完之后,主線程就會向任務(wù)隊列中讀取異步任務(wù)(隊列方法:先進先出)
而且是一直重復(fù)向任務(wù)隊列中,即使沒有任務(wù)。它也會一直去輪詢。
只不過在任務(wù)列表里面沒有任務(wù)的時候, 主線程只需要稍微過一遍就行, 一旦遇到任務(wù)隊列里面有任務(wù)的時候,就會去執(zhí)行它
也就是說在我們打開網(wǎng)頁的時候,JS引擎會一直執(zhí)行事件循環(huán),直到網(wǎng)頁關(guān)閉
如圖所示
由此,上面為什么會產(chǎn)生 undefined的原因了, 因為ajax 是異步任務(wù),而我們console.log(data)是同步任務(wù),所以先執(zhí)行的同步任務(wù),才會去執(zhí)行 ajax
說了這么多,我們來看下 為什么我們很需要 從 callback => promise => async/await
因為很多時候我們需要把一個異步任務(wù)的返回值,傳遞給下一個函數(shù),而且有時候是連續(xù)的n個
callback
// 只有一個callback的時候 function fn(callback) { setTimeout(function(){ callback && callback() }, 1000) } fn(function(){ console.log(1) }) // 一旦我們多幾個呢? function fn(a){ // 傳入a 返回a1 function fn1(a1){ function fn2(a2){ function fn3(a3){ console.log(a3) .... } } } } // 當項目一復(fù)雜,這滋味。。。
Promise
什么是promise?
Promise是異步編程的一種解決方案,同時也是ES6的內(nèi)置對象,它有三種狀態(tài):
pending: 進行中
resolved: 已完成
rejected:已失敗
Promise方法
Promise.prototype.then() 接收兩個函數(shù),一個是處理成功后的函數(shù),一個是處理錯誤結(jié)果的函數(shù)。可以進行鏈式調(diào)用
Promise.prototype.catch() 捕獲異步操作時出現(xiàn)的異常, 一般我們用來代替.then方法的第二個參數(shù)
Promise.resolve() 接受一個參數(shù)值,可以是普通的值, 會返回到對應(yīng)的Promise的then方法上
Promise.reject() 接受一個參數(shù)值,可以是普通的值, 會返回到對應(yīng)的Promise的catch方法上或著then方法的第二個參數(shù)上
Promise.all() 接收一個參數(shù),它必須是可以迭代的,比如數(shù)組。通常用來處理一些并發(fā)的異步操作。成功調(diào)用后返回一個數(shù)組,數(shù)組的值是有序的,即按照傳入?yún)?shù)的數(shù)組的值操作后返回的結(jié)果
Promise.race() 接收一個可以迭代的參數(shù),比如數(shù)組。但是只要其中有一個執(zhí)行了,就算執(zhí)行完了,不管是成功還是失敗。
基本用法
let promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(1) }, 1000) }) promise.then( res => { console.log(res)// 一秒之后打印1 })
我們把上面的回調(diào)地獄轉(zhuǎn)換下
const fn = a => { return Promise.resolve(a) } const fn1 = a => { return Promise.resolve(a) } const fn2 = a => { // return Promise.resolve(a) return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(a) },1000) }) } const fn3 = a => { // return Promise.resolve(a) return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(a) },1000) }) } fn(123) .then(fn1) .then(fn2) .then(fn3) .then( res => { console.log(res) // => 123 })
這樣就簡單明了多了, 我們就不需要一層一層嵌套callback了,可以通過鏈式調(diào)用來解決callback的問題
然而,僅僅這樣還是覺得不夠好
因為這種面條式調(diào)用還是讓人很不爽,而且 then 方法里面雖然是按先后順序來的,但是其本身還是異步的
看下面這段代碼
const promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) console.log(111) promise.then( res => { console.log(res) }) console.log(333)
打印結(jié)果依然還是 111 => 333 => 222, 并不是我們想象的 111 => 222 => 333
依然不適合單線程的思維模式。所以下一個解決方案 又出現(xiàn)了
async/await
這是ES7的語法,當然,在現(xiàn)在這種工程化的時代,基本babel編譯之后也都是能在項目中引用的
基本用法跟規(guī)則
async 表示這是一個async函數(shù),
await只能用在這個函數(shù)里面。后面應(yīng)該跟著是 Promise 對象, 不跟的話也沒關(guān)系, 但是await就不會在這里等待了
await 表示在這里等待promise返回結(jié)果
例:
const fn = () => { return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) } (async function(){ console.log(111) let data = await fn() console.log(data) console.log(333) })() // 是不是返回 111 => 222 => 333 了呢 // 我們來試下返回別的東西, 不返回 promise const fn = () => { setTimeout(function(){ console.log(222) }, 1000) } (async function(){ console.log(111) let data = await fn() console.log(data) console.log(333) })() // 打印結(jié)果: 111 => undefined => 333 => 222 // 當我們不是在await 關(guān)鍵字后面返回的不是 promise 對象時, 它就不會在原地等待 promise執(zhí)行完再執(zhí)行, 而是向正常的JS一樣執(zhí)行,把異步任務(wù)跳過去
await 關(guān)鍵字必須包裹在 async 函數(shù)里面,而且async 函數(shù)必須是它的父函數(shù)
const fn = () => { let promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) } // 這樣是不行的,會報錯,因為的await關(guān)鍵字的父函數(shù)不是 async 函數(shù) const grand = async () => { return function parent() { let data = await fn() } } // 這樣才行,因為await 的父函數(shù) 是一個 async 函數(shù) const grand = () => { return async function parent() { let data = await fn() } }7. 柯里化 與 反柯里化
柯里化
函數(shù)柯里化就是對高階函數(shù)的降階處理。
柯里化簡單的說,就是把 n 個參數(shù)的函數(shù),變成只接受一個參數(shù)的 n 個函數(shù)
function(arg1,arg2)變成function(arg1)(arg2)
function(arg1,arg2,arg3)變成function(arg1)(arg2)(arg3)
function(arg1,arg2,arg3,arg4)變成function(arg1)(arg2)(arg3)(arg4)
柯里化有什么作用
參數(shù)復(fù)用;
提前返回;
延遲計算/運行
例:
//求和 function add (a, b, c) { return a + b + c } add(1,2,3)
如果我只改變 c 的值,在求和
add(1,2,4) 是不是得多出重新計算 a + b 的部分
我們是不是可以提前返回a+b的值, 然后只傳入 c 的值進行計算就行了
修改一下方法
function add (a, b) { return function (c) { return a + b + c } } var sum = add(1, 2) sum(3) sum(4)
在此基礎(chǔ)上我們在做下修改
function add (a) { return function (b) { return function (c) { return a + b + c } } }
這樣我們是不是可以隨時復(fù)用某個參數(shù),并且控制在某個階段提前返回
還有一個經(jīng)典的例子
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };
我們每次調(diào)用事件時,都需要判斷兼容問題, 但我們運用柯里化的方式就只要判斷一次就行了
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();
還有一個作用就是延遲計算
小明每天都會花一部分錢吃飯
小明想知道它5天之后總共會花費多少錢
var total = 0 var fn = function(num) { total += num } fn(50) fn(70) fn(60) fn(100) fn(80)
這樣我們便能算出它總共花了都少錢
但是小明又突然想知道 如果他每天花費的的錢翻一倍 會產(chǎn)生多少錢
于是我們是不是得改下 上面的 函數(shù)
var fn = function(num) { total += num*2 } fn(50) fn(70) fn(60) fn(100) fn(80)
那我們是不是有什么辦法,先把這些數(shù) 存起來,到最后在進行計算
我們接著來封裝
var curry = function(fn) { var args = [] return function() { if (arguments.length === 0) { return fn.apply(null, args) }else{ args = args.concat([].slice.call(arguments)) return curry.call(null, fn, args) } } } var curryFn = function() { var args = [].slice.call(arguments), total = 0 for (var i = 0; i < args.length; i++) { total += args[i] } return total } var fn = curry(curryFn) fn(50) fn(70) fn(60) fn(100) fn(80) fn() //不傳參數(shù)的時候進行計算
這樣我們只有最后的時候才進行計算。
而且只需要修改 curryFn 里面的計算方法就行
我們整理下上面的方法封裝完整的柯里化函數(shù)
var curry = function (fn, length) { length = length || fn.length; var sub_curry = function (f) { var args = [].slice.call(arguments, 1); return function () { return f.apply(null, args.concat([].slice.call(arguments))) } } return function () { var args = [].slice.call(arguments); if (length > args.length) { var newArgs = [fn].concat(args); return curry(sub_curry.apply(null,newArgs), length - args.length) }else{ fn.apply(null,arguments) } } }
// 1. var fn = curry( function(a,b,c){ console.log(a, b, c) }) fn("a")("b")("c") // 2. fn1 = curry(function(){ console.log(arguments) }, 3) fn1("a")("b")("c")
反柯里化
反柯里化的作用在與擴大函數(shù)的適用性,使本來作為特定對象所擁有的功能的函數(shù)可以被任意對象所用.
被任意對象使用? 是不是想到了用call, apply 設(shè)置this指向
通過 call/apply 被任意對象所用
var obj = { a: 1, fn: function (b) { return this.a + b } } obj.fn(2) // 3 var obj1 = {a:4} obj.fn.call(obj1, 2) // 6
反柯里化版本
var uncurrying= function (fn) { return function () { var context=[].shift.call(arguments); return fn.apply(context,arguments); } } // const uncurrying = fn => (...args) => Function.prototype.call.apply(fn,args) // 簡潔版 var f = function (b) { return this.a + b } var uncurry = uncurrying(f) var obj = {a:1}, obj1 = {a:4} uncurry(obj, 2) // 3 uncurry(obj1, 2) // 3
相信大家已經(jīng)看出區(qū)別了,這丫的就相當于一個外部的call方法
總結(jié)上面很多只是自己的部分理解,不一定準確。如果有不同理解,謝謝指出。
博客地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107323.html
摘要:在說真正的內(nèi)容之前,咱們還要先來說說與之間的那些事兒。的核心庫只關(guān)注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。高效核心庫文件壓縮之后只有,遠比的壓縮版文件小得多。這么說還是會比較抽象,接下來咱們用代碼來進一步解釋和之間的關(guān)系。 書接上文,上一回咱們說到了如今的前端江湖早已是框架三分天下的格局。接下來,咱們就要說到主角 Vue 了。在說真正的 Vue 內(nèi)容之前,咱們還要先來說...
閱讀 2980·2023-04-26 02:29
閱讀 592·2019-08-30 15:54
閱讀 1668·2019-08-29 13:13
閱讀 605·2019-08-28 17:51
閱讀 2730·2019-08-26 13:58
閱讀 1537·2019-08-26 13:27
閱讀 2826·2019-08-26 11:39
閱讀 3453·2019-08-26 10:46