国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

你要看看這些有趣的函數(shù)方法嗎?

Shihira / 1711人閱讀

前言

這是underscore.js源碼分析的第六篇,如果你對(duì)這個(gè)系列感興趣,歡迎點(diǎn)擊

underscore-analysis/ watch一下,隨時(shí)可以看到動(dòng)態(tài)更新。

下劃線中有非常多很有趣的方法,可以用比較巧妙的方式解決我們?nèi)粘I钪杏龅降膯?wèn)題,比如_.after,_.before_.defer...等,也許你已經(jīng)用過(guò)他們了,今天我們來(lái)深入源碼,一探究竟,他們到底是怎么實(shí)現(xiàn)的。

指定調(diào)用次數(shù)(after, before)

把這兩個(gè)方法放在前面也是因?yàn)樗麄儌z能夠解決我們工作中至少以下兩個(gè)問(wèn)題

如果你要等多個(gè)異步請(qǐng)求完成之后才去執(zhí)行某個(gè)操作fn,那么你可以用_.after,而不必寫多層異步回調(diào)地獄去實(shí)現(xiàn)需求

有一些應(yīng)用可能需要進(jìn)行初始化操作而且僅需要一次初始化就可以,一般的做法是在入口處對(duì)某個(gè)變量進(jìn)行判斷,如果為真那么認(rèn)為已經(jīng)初始化過(guò)了直接return掉,如果為假那么進(jìn)行參數(shù)的初始化工作,并在完成初始化之后設(shè)置該變量為真,那么下次進(jìn)入的時(shí)候便不必重復(fù)初始化了。

對(duì)于問(wèn)題1

let async1 = (cb) => {
  setTimeout(() => {
    console.log("異步任務(wù)1結(jié)束了")
    cb()
  }, 1000)
}

let async2 = (cb) => {
  setTimeout(() => {
    console.log("異步任務(wù)2結(jié)束了")
    cb()
  }, 2000)
}

let fn = () => {
  console.log("我是兩個(gè)任務(wù)都結(jié)束了才進(jìn)行的任務(wù)")
}

如果要在任務(wù)1,和任務(wù)2都結(jié)束了才進(jìn)行fn任務(wù),我們一般的寫法是啥?
可能會(huì)下面這樣寫

async1(() => {
  async2(fn)
})

這樣確實(shí)可以保證任務(wù)fn是在前面兩個(gè)異步任務(wù)都結(jié)束之后才進(jìn)行,但是相信你是不太喜歡回調(diào)的寫法的,這里舉的異步任務(wù)只有兩個(gè),如果多了起來(lái),恐怕就要蛋疼了。別疼,用下劃線的after函數(shù)可以解救你。

fn = _.after(2, fn)

async1(fn)
async2(fn)

運(yùn)行截圖

有木有很爽,不用寫成回調(diào)地獄的形式了。那么接下來(lái)我們看看源碼是怎么實(shí)現(xiàn)的。

after源碼實(shí)現(xiàn)

_.after = function(times, func) {
  return function() {
    // 只有返回的函數(shù)被調(diào)用times次之后才執(zhí)行func操作
    if (--times < 1) {
      return func.apply(this, arguments);
    }
  };
};

源碼簡(jiǎn)單到要死啊,但是就是這么神奇,妥妥地解決了我們的問(wèn)題1。

對(duì)于問(wèn)題2

let app = {
  init (name, sex) {
    if (this.initialized) {
      return
    }
    // 進(jìn)行參數(shù)的初始化工作
    this.name = name
    this.sex = sex
    // 初始化完成,設(shè)置標(biāo)志
    this.initialized = true
  },
  showInfo () {
    console.log(this.name, this.sex)
  }
}

// 傳參數(shù)進(jìn)行應(yīng)用的初始化

app.init("qianlonog", "boy")
app.init("xiaohuihui", "girl")
app.showInfo() // qianlonog boy 注意這里打印出來(lái)的是第一次傳入的參數(shù)

一般需要且只進(jìn)行一次參數(shù)初始化工作的時(shí)候,我們可能會(huì)像上面那樣做。但是其實(shí)如果用下劃線中的before方法我們還可以這樣做。

let app = {
  init: _.before(2, function (name, sex) {
    // 進(jìn)行參數(shù)的初始化工作
    this.name = name
    this.sex = sex
  }) ,
  showInfo () {
    console.log(this.name, this.sex)
  }
}

// 傳參數(shù)進(jìn)行應(yīng)用的初始化

app.init("qianlonog", "boy")
app.init("xiaohuihui", "girl")
app.showInfo() // qianlonog boy 注意這里打印出來(lái)的是第一次傳入的參數(shù)

好玩吧,讓我們看看_.before是怎么實(shí)現(xiàn)的。

// 創(chuàng)建一個(gè)函數(shù),這個(gè)函數(shù)調(diào)用次數(shù)不超過(guò)times次
// 如果次數(shù) >= times 則最后一次調(diào)用函數(shù)的返回值將被記住并一直返回該值

_.before = function(times, func) {
  var memo;
  return function() {
    // 返回的函數(shù)每次調(diào)用都times減1
    if (--times > 0) { 
      // 調(diào)用func,并傳入外面?zhèn)鬟M(jìn)來(lái)的參數(shù)
      // 需要注意的是,后一次調(diào)用的返回值會(huì)覆蓋前一次
      memo = func.apply(this, arguments);
    }
    // 當(dāng)調(diào)用次數(shù)夠了,就將func銷毀設(shè)置為null
    if (times <= 1) func = null;
    return memo;
  };
};
讓函數(shù)具有記憶的功能

在程序中我們經(jīng)常會(huì)要進(jìn)行一些計(jì)算的操作,當(dāng)遇到比較耗時(shí)的操作時(shí)候,如果有一種機(jī)制,對(duì)于同樣的輸入,一定得到相同的輸出,并且對(duì)于同樣的輸入,后續(xù)的計(jì)算直接從緩存中讀取,不再需要將計(jì)算程序運(yùn)行那就非常贊了。

舉例

let calculate = (num, num2) => {
  let result = 0
  let start = Date.now()
  for (let i = 0; i< 10000000; i++) { // 這里只是模擬耗時(shí)的操作
    result += num
  }

  for (let i = 0; i< 10000000; i++) { // 這里只是模擬耗時(shí)的操作
    result += num2
  }
  let end = Date.now()
  console.log(end - start)
  return result
}

calculate(1, 2) // 30000000
// log 得到235
calculate(1, 2) // 30000000
// log 得到249

對(duì)于上面這個(gè)calculate函數(shù),同樣的輸入1, 2,兩次調(diào)用的輸出都是一樣的,并且兩次都走了兩個(gè)耗時(shí)的循環(huán),看看下劃線中的memoize函數(shù),如何為我們省去第二次的耗時(shí)操作,直接給出300000的返回值

let calculate = _.memoize((num, num2) => {
  let start = Date.now()
  let result = 0
  for (let i = 0; i< 10000000; i++) { // 這里只是模擬耗時(shí)的操作
    result += num
  }

  for (let i = 0; i< 10000000; i++) { // 這里只是模擬耗時(shí)的操作
    result += num2
  }
  let end = Date.now()
  console.log(end - start)
  return result
}, function () {
  return [].join.call(arguments, "@") // 這里是為了給同樣的輸入指定唯一的緩存key
})

calculate(1, 2) // 30000000
// log 得到 238
calculate(1, 2) // 30000000
// log 啥也沒(méi)有打印出,因?yàn)橹苯訌木彺嬷凶x取了

源碼實(shí)現(xiàn)

 _.memoize = function(func, hasher) {
  var memoize = function(key) {
    var cache = memoize.cache;
    // 注意hasher,如果傳了hasher,就用hasher()執(zhí)行的結(jié)果作為緩存func()執(zhí)行的結(jié)果的key
    var address = "" + (hasher ? hasher.apply(this, arguments) : key); 
    // 如果沒(méi)有在cache中查找到對(duì)應(yīng)的key就去計(jì)算一次,并緩存下來(lái)
    if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); 
    // 返回結(jié)果
    return cache[address];
  };
  memoize.cache = {};
  return memoize; // 返回一個(gè)具有cache靜態(tài)屬性的函數(shù)
};

相信你已經(jīng)看懂了源碼實(shí)現(xiàn),是不是很簡(jiǎn)單,但是又很實(shí)用有趣。

來(lái)一下延時(shí)(_.delay和_.defer)

下劃線中在原生延遲函數(shù)setTimeout的基礎(chǔ)上做了一些改造,產(chǎn)生以上兩個(gè)函數(shù)

_.delay(function, wait, *arguments)

就是延遲wait時(shí)間去執(zhí)行functionfunction需要的參數(shù)由*arguments提供

使用舉例

var log = _.bind(console.log, console)
_.delay(log, 1000, "hello qianlongo")
// 1秒后打印出 hello qianlongo

源碼實(shí)現(xiàn)

_.delay = function(func, wait) {
  // 讀取第三個(gè)參數(shù)開始的其他參數(shù)
  var args = slice.call(arguments, 2);
  return setTimeout(function(){
    // 執(zhí)行func并將參數(shù)傳入,注意apply的第一個(gè)參數(shù)是null護(hù)著undefined的時(shí)候,func內(nèi)部的this指的是全局的window或者global
    return func.apply(null, args); 
  }, wait);
};

不過(guò)有點(diǎn)需要注意的是_.delay(function, wait, *arguments)`function中的this指的是window或者global`

_.defer(function, *arguments)

延遲調(diào)用function直到當(dāng)前調(diào)用棧清空為止,類似使用延時(shí)為0的setTimeout方法。對(duì)于執(zhí)行開銷大的計(jì)算和無(wú)阻塞UI線程的HTML渲染時(shí)候非常有用。 如果傳遞arguments參數(shù),當(dāng)函數(shù)function執(zhí)行時(shí), arguments 會(huì)作為參數(shù)傳入

源碼實(shí)現(xiàn)

_.defer = _.partial(_.delay, _, 1);

所以主要還是看_.partial是個(gè)啥

可以預(yù)指定參數(shù)的函數(shù)_.partial

局部應(yīng)用一個(gè)函數(shù)填充在任意個(gè)數(shù)的 參數(shù),不改變其動(dòng)態(tài)this值。和bind方法很相近。你可以在你的參數(shù)列表中傳遞_來(lái)指定一個(gè)參數(shù) ,不應(yīng)該被預(yù)先填充(underscore中文網(wǎng)翻譯)

使用舉例

let fn = (num1, num2, num3, num4) => {
  let str = `num1=${num1}`
  str += `num2=${num2}`
  str += `num3=${num3}`
  str += `num4=${num4}`
  return str
}

fn = _.partial(fn, 1, _, 3, _)
fn(2,4)// num1=1num2=2num3=3num4=4

可以看到,我們傳入了_(這里指的是下劃線本身)進(jìn)行占位,后續(xù)再講2和4填充到對(duì)應(yīng)的位置去了。

源碼具體怎么實(shí)現(xiàn)的呢?

_.partial = function(func) {
  // 獲取除了傳進(jìn)回調(diào)函數(shù)之外的其他預(yù)參數(shù)
  var boundArgs = slice.call(arguments, 1); 
  var bound = function() {
    var position = 0, length = boundArgs.length;
    // 先創(chuàng)建一個(gè)和boundArgs長(zhǎng)度等長(zhǎng)的空數(shù)組
    var args = Array(length); 
    // 處理占位元素_
    for (var i = 0; i < length; i++) { 
      // 如果發(fā)現(xiàn)boundArgs中有_的占位元素,就依次用arguments中的元素進(jìn)行替換boundArgs
      args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; 
    }
    // 把a(bǔ)uguments中的其他元素添加到boundArgs中
    while (position < arguments.length) args.push(arguments[position++]); 
    // 最后執(zhí)行executeBound,接下來(lái)看看executeBound是什么
    return executeBound(func, bound, this, this, args);
  };
  return bound;
};

在上一篇文章如何寫一個(gè)實(shí)用的bind?
有詳細(xì)講解,這里我們?cè)倩仡櫼幌?br>executeBound

var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
  // 如果調(diào)用方式不是new func的形式就直接調(diào)用sourceFunc,并且給到對(duì)應(yīng)的參數(shù)即可
  if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); 
   // 處理new調(diào)用的形式
  var self = baseCreate(sourceFunc.prototype);
  var result = sourceFunc.apply(self, args);
  if (_.isObject(result)) return result;
  return self;
};

先看一下這些參數(shù)都?代表什么含義

sourceFunc:原函數(shù),待綁定函數(shù)

boundFunc: 綁定后函數(shù)

context:綁定后函數(shù)this指向的上下文

callingContext:綁定后函數(shù)的執(zhí)行上下文,通常就是 this

args:綁定后的函數(shù)執(zhí)行所需參數(shù)

這里其實(shí)就是執(zhí)行了這句,所以關(guān)鍵還是如果處理預(yù)參數(shù),和后續(xù)參數(shù)的邏輯

sourceFunc.apply(context, args);
管道式函數(shù)組合

你也許遇到過(guò)這種場(chǎng)景,任務(wù)A,任務(wù)B,任務(wù)C必須按照順序執(zhí)行,并且A的輸出作為B的輸入,B的輸出作為C的輸入,左后再得到結(jié)果。用一張圖表示如下

那么一般的做法是什么呢

let funcA = (str) => {
  return str += "-A"
}

let funcB = (str) => {
  return str += "-B"
}

let funcC = (str) => {
  return str += "-C"
}

funcC(funcB(funcA("hello")))
// "hello-A-B-C"

用下劃線中的compose方法怎么做呢

let fn = _.compose(funcC, funcB, funcA)
fn("hello")
// "hello-A-B-C"

看起來(lái)沒(méi)有一般的做法那樣,層層繞進(jìn)去了,而是以一種非常扁平的方式使用。

同樣我們看看源碼是怎么實(shí)現(xiàn)的。

_.compose源碼

_.compose = function() {
  var args = arguments;
  // 從最后一個(gè)參數(shù)開始處理
  var start = args.length - 1;
  return function() {
    var i = start;
    // 執(zhí)行最后一個(gè)函數(shù),并得到結(jié)果result
    var result = args[start].apply(this, arguments); 
    // 從后往前一個(gè)個(gè)調(diào)用傳進(jìn)來(lái)的函數(shù),并將上一次執(zhí)行的結(jié)果作為參數(shù)傳進(jìn)下一個(gè)函數(shù)
    while (i--) result = args[i].call(this, result); 
    // 最后將結(jié)果導(dǎo)出
    return result;
  };
};
給多個(gè)函數(shù)綁定同樣的上下文(_.bindAll(object, *methodNames))

將多個(gè)函數(shù)methodNames綁定上下文環(huán)境為object

? ? ?,好困,寫文章當(dāng)真好要時(shí)間和精力,到這里已經(jīng)快寫了3個(gè)小時(shí)了,夜深,好像躺下睡覺(jué)啊!!!啊啊啊,再等等快說(shuō)完了(希望不會(huì)誤人子弟)。

var buttonView = {
  label  : "underscore",
  onClick: function(){ alert("clicked: " + this.label); },
  onHover: function(){ console.log("hovering: " + this.label); }
};
_.bindAll(buttonView, "onClick", "onHover");

$("#underscore_button").bind("click", buttonView.onClick);

我們用官網(wǎng)給的例子說(shuō)一下,默認(rèn)的jQuery中$(selector).on(eventName, callback)callback中的this指的是當(dāng)前的元素本身,當(dāng)時(shí)經(jīng)過(guò)上面的處理,會(huì)彈出underscore

_.bindAll源碼實(shí)現(xiàn)

 _.bindAll = function(obj) {
  var i, length = arguments.length, key;
  // 必須要指定需要綁定到obj的函數(shù)參數(shù)
  if (length <= 1) throw new Error("bindAll must be passed function names");
  // 從第一個(gè)實(shí)參開始處理,這些便是需要綁定this作用域到obj的函數(shù)
  for (i = 1; i < length; i++) { 
    key = arguments[i];
    // 調(diào)用內(nèi)部的bind方法進(jìn)行this綁定
    obj[key] = _.bind(obj[key], obj); 
  }
  return obj;
};

內(nèi)部使用了_.bind進(jìn)行綁定,如果你對(duì)_.bind原生是如何實(shí)現(xiàn)的可以看這里如何寫一個(gè)實(shí)用的bind?

拾遺

最后關(guān)于underscore.js中function篇章還有兩個(gè)函數(shù)說(shuō)一下,另外節(jié)流函數(shù)throttle以及debounce_會(huì)另外多帶帶寫一篇文章介紹,歡迎前往underscore-analysis/ watch一下,隨時(shí)可以看到動(dòng)態(tài)更新。

_.wrap(function, wrapper)

將第一個(gè)函數(shù) function 封裝到函數(shù) wrapper 里面, 并把函數(shù) function 作為第一個(gè)參數(shù)傳給 wrapper. 這樣可以讓 wrapper 在 function 運(yùn)行之前和之后 執(zhí)行代碼, 調(diào)整參數(shù)然后附有條件地執(zhí)行.

直接看源碼實(shí)現(xiàn)吧

_.wrap = function(func, wrapper) {
    return _.partial(wrapper, func);
  };

還記得前面說(shuō)的partial吧,他會(huì)返回一個(gè)函數(shù),內(nèi)部會(huì)執(zhí)行wrapper,并且func會(huì)作為wrapper的一個(gè)參數(shù)被傳入。

_.negate(predicate)

將predicate函數(shù)執(zhí)行的結(jié)果取反。

使用舉例

let fn = () => {
  return true
}

_.negate(fn)() // false

看起來(lái)好像沒(méi)什么軟用,但是。。。。

let arr = [1, 2, 3, 4, 5, 6]

let findEven = (num) => {
  return num % 2 === 0
}

arr.filter(findEven) // [2, 4, 6]

如果要找到奇數(shù)呢?

let arr = [1, 2, 3, 4, 5, 6]

let findEven = (num) => {
  return num % 2 === 0
}

arr.filter(_.negate(findEven)) // [1, 3, 5]

源碼實(shí)現(xiàn)

_.negate = function(predicate) {
  return function() {
    return !predicate.apply(this, arguments);
  };
};

源碼很簡(jiǎn)單,就是把你傳進(jìn)來(lái)的predicate函數(shù)執(zhí)行的結(jié)果取反一下,但是應(yīng)用還是蠻多的。

結(jié)尾

這幾個(gè)是underscore庫(kù)中function相關(guān)的api,大部分已經(jīng)說(shuō)完了,如果對(duì)你有一點(diǎn)點(diǎn)幫助。

點(diǎn)一個(gè)小星星吧???

點(diǎn)一個(gè)小星星吧???

點(diǎn)一個(gè)小星星吧???

good night ?

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/50805.html

相關(guān)文章

  • 你要看看這些有趣函數(shù)方法

    前言 這是underscore.js源碼分析的第六篇,如果你對(duì)這個(gè)系列感興趣,歡迎點(diǎn)擊 underscore-analysis/ watch一下,隨時(shí)可以看到動(dòng)態(tài)更新。 下劃線中有非常多很有趣的方法,可以用比較巧妙的方式解決我們?nèi)粘I钪杏龅降膯?wèn)題,比如_.after,_.before,_.defer...等,也許你已經(jīng)用過(guò)他們了,今天我們來(lái)深入源碼,一探究竟,他們到底是怎么實(shí)現(xiàn)的。 showIm...

    melody_lql 評(píng)論0 收藏0
  • [翻]ECMAScript 6 特性速覽

    摘要:類總所周知,不像其他面向?qū)ο笳Z(yǔ)言那樣支持類,但是可以通過(guò)函數(shù)和原型來(lái)模擬類。如果你學(xué)習(xí)過(guò)或者其他面向?qū)ο笳Z(yǔ)言的話,你會(huì)覺(jué)得很熟悉。結(jié)論下一個(gè)版本的會(huì)帶來(lái)一個(gè)更加簡(jiǎn)單更加友好的語(yǔ)法來(lái)幫助那些從面向?qū)ο笳Z(yǔ)言轉(zhuǎn)過(guò)來(lái)的開發(fā)者的學(xué)習(xí)。 原文地址:http://www.frontendjournal.com/javascript-es6-learn-important-features-in-a-...

    CoderStudy 評(píng)論0 收藏0
  • 前端練級(jí)攻略(第一部分)

    摘要:第一部分介紹了如何使用和開發(fā)接口。由于系統(tǒng)變得越來(lái)越復(fù)雜,人們提出了稱為預(yù)處理器和后處理器的工具來(lái)管理復(fù)雜性。當(dāng)您第一次得知有預(yù)處理器和后處理器時(shí),你很有可能在任何地方已經(jīng)使用它們。我之前建議的文章,,也涵蓋了預(yù)處理器相關(guān)的知識(shí)。 我記得我剛開始學(xué)習(xí)前端開發(fā)的時(shí)候。我看到了很多文章及資料,被學(xué)習(xí)的資料壓得喘不過(guò)氣來(lái),甚至不知道從哪里開始。 本指南列出前端學(xué)習(xí)路線,并提供了平時(shí)收藏的一些...

    qpal 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Shihira

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<