摘要:他指示了一個對象的屬性,返回的將用來獲得該屬性對應的值在上面的分析中,我們知道,當傳入的是一個函數時,還要經過一個叫的內置函數才能獲得最終的所以此處的必然是優化回調的作用了。
開篇說明
對的,讓你所見,又開始造輪子了。哈哈,造輪子我們是認真的~
源碼閱讀是必須的,Underscore是因為剛剛學習整理了一波函數式編程,加上自己曾經沒有太多閱讀源碼的經驗,先拿Underscore練練手,跟著前輩們走一走,學一學。也相同時能夠夯實js基礎,從源碼中學習到更多的編碼技巧
Underscore源碼閱讀大致按照官方文檔來編寫.盡量的說明每一個函數的寫法,希望自己可以從中可以收獲大神的編碼功力。
github:Personal_Blog
閱讀目錄窺探Underscore源碼系列-開篇
窺探Underscore源碼系列-工具
窺探Underscore源碼系列-集合
窺探Underscore源碼系列-數組
窺探Underscore源碼系列-函數
窺探Underscore源碼系列-對象
窺探Underscore源碼系列-感悟
Underscore源碼+注釋地址源碼閱讀 整體結構、變量介紹
(function(){}())
常規操作哈,跟jQuery一毛一樣,通過IIFE來包裹業務邏輯,目的簡單:1、避免全局污染。2、保護隱私
var root = typeof self == "object" && self.self === self && self || typeof global == "object" && global.global === global && global || this || {}; var previousUnderscore = root._;
通過global和self來判斷是node環境還是window環境,說白了,就是為了拿到全局變量。因為我們需要一個全局的變量_,所以為了防止沖突,我們這里拿到root后,先暫存下之前的root._
var ArrayProto = Array.prototype, ObjProto = Object.prototype; var SymbolProto = typeof Symbol !== "undefined" ? Symbol.prototype : null; var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeCreate = Object.create; var Ctor = function(){}; var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; if (typeof exports != "undefined" && !exports.nodeType) { if (typeof module != "undefined" && !module.nodeType && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } _.VERSION = "1.8.3";
由于Underscore本身依賴很多原生js的方法,所以這里為了避免原型鏈的查找性能消耗,Underscore通過局部變量來保存一些常用的對象和方法。既可以提升性能,減少對象成員訪問深度也可以減少代碼的冗長。
下面的Ctor和_ 是為了面向對象而準備的。
迭代var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; var builtinIteratee; var cb = function(value, context, argCount) { if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value) && !_.isArray(value)) return _.matcher(value); return _.property(value); }; _.iteratee = builtinIteratee = function(value, context) { return cb(value, context, Infinity); };
這里的迭代,我們需要理清楚一個概念,在Underscore中,我們需要改變那種命令式的編程方式,具體的可以看我之前寫的關于函數式編程的文章哈。
所以這里想說的就是關于遍歷迭代的東西。
var results = _.map([1,2,3],function(elem){ return elem*2; }); // => [2,4,6] _.map = _.collect = function (obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length); // 定長初始化數組 for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; };
我們傳遞給的 _.map 的第二個參數就是一個 iteratee,他可能是函數,對象,甚至是字符串。
value 為 null。則 iteratee 的行為只是返回當前迭代元素自身
value 為一個函數。那么通過內置函數 optimizeCb 對其進行優化
value 為一個對象。那么返回的 iteratee(_.matcher)的目的是想要知道當前被迭代元素是否匹配給定的這個對象
value 是字面量,如數字,字符串等。他指示了一個對象的屬性 key,返回的 iteratee(_.property)將用來獲得該屬性對應的值
optimizeCb()在上面的分析中,我們知道,當傳入的 value 是一個函數時,value 還要經過一個叫 optimizeCb 的內置函數才能獲得最終的 iteratee:
var cb = function (value, context, argCount) { // ... if (_.isFunction(value)) return optimizeCb(value, context, argCount); // ... };
所以此處的optimizeCb必然是優化回調的作用了。
// 優化回調的函數,遍歷 var optimizeCb = function(func, context, argCount) { // void 0 會返回真正的undefined 此處確保上下文的存在 if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { // argCount為0時候,迭代過程中,我們只需要這個value就可以了 return func.call(context, value); }; // The 2-parameter case has been omitted only because no current consumers // 3個參數(值,索引,被迭代集合對象). case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; // 4個參數(累加器(比如reducer需要的), 值, 索引, 被迭代集合對象) case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; };
整體的代碼非常清晰,待優化的回調函數func,上下文context以及迭代回調需要的參數個數。
上面的這個優化的回調涉及到不同地方使用的不同迭代。這里暫時 先放一放。等過了一遍源碼后,看到每一個用到迭代的地方,在回頭來看,就會明白很多。
rest參數在 ES6中,我們定義不定參方法的時候可以這么寫
let a = (b,...c)=>{ console.log(b,c); }
但是在此之前,Underscore實現了自己的reset,使用如下:
function a(a,b,c,d,e){ console.log(a,b,c,d,e) } let aa = restArgs(a);//let aa = restArgs(a,4) aa(1,2,3,4,5,6,7,8,8)
看下restArgs的實現:
var restArgs = function(func, startIndex) { //未傳則取形參個數減一 startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { // 多傳了幾個參數 //length為多傳了幾個參數 var length = Math.max(arguments.length - startIndex, 0), rest = Array(length), index = 0; for (; index < length; index++) { rest[index] = arguments[index + startIndex]; } //優化。注意rest參數總是最后一個參數, 否則會有歧義 switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } //撇去常用的startIndex,這里循環 //先拿到前面參數 var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } //拿到后面的數組 args[startIndex] = rest; return func.apply(this, args); }; };面向對象
關于面向對象,這里不做過多解釋了,可以參考我的另一篇文章:
javasript設計模式之面向對象。
我們直接看他的繼承實現吧
var Ctor = function(){}; // 定義了一個用于繼承的內部方法 var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; // nativeCreate = Object.create; if (nativeCreate) return nativeCreate(prototype); Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; };
es5 中,我們有一種創建對象的方式,Object.create 。
function Animal(name){ this.name = name; } Animal.prototype.eat = function(){ console.log(this.name,"鳥為食亡"); } var dog = Object.create(Animal.prototype); dog.name = "毛毛"; dog.eat();
ok,大概從上大家就看出來create的作用了。
baseCrate中,首先判斷是否為對象,否則退出。瀏覽器能力檢測是否具備Object.create方法,具備則用。否則采用寄生式繼承創建對象。需要注意的是,baseCreate僅僅支持原型繼承,而不能像Object.create那樣傳遞屬性列表。
結束語開篇簡單的介紹Collection Functions上面的代碼部分。在介紹Collection Function每個方法實現之前,我們將在下一篇看一下一些工具方法的編寫方式。
的確在造造輪子,只是更想自己擼一遍優秀代碼。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94645.html
摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個系列后,再跟著其他系列的文章接著學習。如何閱讀我在寫系列的時候,被問的最多的問題就是該怎么閱讀源碼我想簡單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個系列再見啦 前言 別名:《underscore 系列 8 篇正式完結!》 介紹 underscore 系列是我寫的第三個系列,前兩個系列分別是 JavaScript 深入系列、...
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發布第一篇文章,到月日發布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里...
摘要:節流保證在一定時間內,只能觸發一次。我們在嘗試一下去抖消抖,消除抖動,感覺這個更好聽有沒有什么現成的上的一次發現源碼的經歷以及對學術界拿來主義的思考函數節流和函數去抖應用場景辨析函數去抖的實現 開篇先提幾個問題? 1.做搜索框的時候你使用什么事件?change?blur?keyup?你想要的效果是什么? 2.scroll事件怎么就觸發?是滾一段距離觸發一次?還是滾一圈觸發一次?還是滾...
摘要:直接來看例子一目了然,第一個參數是對象,第二個參數可以是一系列的值,也可以是數組數組中含,也可以是迭代函數,我們根據值,或者迭代函數來過濾中的鍵值對,返回新的對象副本。 Why underscore 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計劃中。 閱讀一些著名框架類庫的源碼,就好像和一個個大師對話,你會學到很多。...
摘要:類似于,但更加健壯和完善。當為一個函數,正常處理。系列系列目錄地址。系列預計寫八篇左右,重點介紹中的代碼架構鏈式調用內部函數模板引擎等內容,旨在幫助大家閱讀源碼,以及寫出自己的。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。 前言 僅看 cb 和 optimizeCb 兩個函數的名字,你可能想不到這是用來做什么的,盡管你可能想到 cb 是 callback 的縮寫。 如果直接講...
閱讀 4619·2021-09-26 09:55
閱讀 1364·2019-12-27 12:16
閱讀 882·2019-08-30 15:56
閱讀 1900·2019-08-30 14:05
閱讀 986·2019-08-30 13:05
閱讀 1266·2019-08-30 10:59
閱讀 1440·2019-08-26 16:19
閱讀 1884·2019-08-26 13:47