摘要:簡介是一款成熟可靠的第三方開源庫,正如統(tǒng)一了不同瀏覽器之間的操作的差異,讓我們可以簡單地對進行操作,則提供了一套完善的函數(shù)式編程的接口,讓我們更方便地在中實現(xiàn)函數(shù)式編程。
1. 簡介
underscore 是一款成熟可靠的第三方開源庫,正如 jQuery 統(tǒng)一了不同瀏覽器之間的 DOM 操作的差異,讓我們可以簡單地對 DOM 進行操作,underscore 則提供了一套完善的函數(shù)式編程的接口,讓我們更方便地在 JavaScript 中實現(xiàn)函數(shù)式編程。
jQuery 在加載時,會把自身綁定到唯一的全局變量 $ 上,underscore 與其類似,會把自身綁定到唯一的全局變量 _ 上,這也是為啥它的名字叫 underscore 的原因。
在搭建 underscore 之前,讓我們先來了解一下什么是 “立即執(zhí)行函數(shù)(IIFE)”.
2. 立即執(zhí)行函數(shù)(IIFE)立即執(zhí)行函數(shù),顧名思義,就是定義好的匿名函數(shù)立即執(zhí)行,寫法如下:
(function(name) { console.log(name); })("suporka");
其作用是:通過定義一個匿名函數(shù),創(chuàng)建了一個新的函數(shù)作用域,相當于創(chuàng)建了一個“私有”的命名空間,該命名空間的變量和方法,不會破壞污染全局的命名空間。
// 函數(shù)外部拿不到內部的變量,因此不會造成變量污染,內部的變量在內部使用即可 (function() { var name = "suporka"; })(); console.log(name); // name is undefinded3. 全局變量 _ 的掛載
當我們在瀏覽器中使用 _.map([1,2,3], function(item){console.log(item)}) 時, _ 是掛載在 Window對象上的,如果我們想在 node 環(huán)境中使用呢 ?
(function() { // root 為掛載對象,為 self 或 global 或 this 或 {} var root = (typeof self == "object" && self.self === self && self) || (typeof global == "object" && global.global === global && global) || this || {}; // _ 應該是一個對象,對象內有屬性函數(shù) var _ = {}; root._ = _; _.VERSION = "1.9.1"; // 給我們的 underscore 一個版本號吧 })();4. 函數(shù)式風格 && 面向對象風格的雙重實現(xiàn)
首先我們實現(xiàn)一個倒裝字符串的方法
(function() { // root 為掛載對象,為 self 或 global 或 this 或 {} var root = (typeof self == "object" && self.self === self && self) || (typeof global == "object" && global.global === global && global) || this || {}; // _ 應該是一個對象,對象內有屬性函數(shù) var _ = {}; root._ = _; _.VERSION = "1.9.1"; // 給我們的 underscore 一個版本號吧 /** * 字符串倒裝 */ _.reverse = function(string) { return string .split("") .reverse() .join(""); }; })(); _.reverse("suporka"); // akropus
不錯,很快實現(xiàn),但是這種是函數(shù)式寫法,調用一個函數(shù)去實現(xiàn),如果我們要實現(xiàn)面向對象寫法呢?如 _("suporka").reverse()! underscore 是支持這種寫法的,仔細觀察 _("suporka") , 你會發(fā)現(xiàn),_ 是一個函數(shù)啊,和我們前面定義的 var _ = {}; 不一致,那么該怎么實現(xiàn)呢?
實例原型我們先測試一下:如果 _ 為函數(shù),我們需要保存其傳進來的參數(shù) obj . new _() 生成一個實例原型對象
function _(obj) { this._wrapped = obj; } _.reverse = function(string) { return string .split("") .reverse() .join(""); }; _.reverse("suporka"); // "akropus", 函數(shù)式調用沒問題 new _("suporka");
從圖中我們可以看出,實例原型對象的 __proto__ (原型)的 constructor 構造函數(shù)指回了原來的 _(obj) 函數(shù),要調用其 reverse() 方法只能 new _("suporka").constructor.reverse()多了一個層級,不符合我們原本的期望。那我們不如在_proto_ 屬性下增加一個和 reverse 一樣的函數(shù),這樣不就可以直接調用了嗎?
let us try it !
function _(obj) { this._wrapped = obj; } _.reverse = function(string) { return string .split("") .reverse() .join(""); }; _.reverse("suporka"); // "akropus", 函數(shù)式調用沒問題 _.prototype.reverse = function() { return this._wrapped .split("") .reverse() .join(""); }; new _("suporka").reverse(); // "akropus", 面向對象式調用沒問題5. 改造 _() function
new _("suporka").reverse() 有點累贅,去掉 new, 重寫 function _()
var _ = function(obj) { // 如果傳入的是實例后對象,返回它 if (obj instanceof _) return obj; // 如果還沒有實例化,new _(obj) if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; _("suporka").reverse(); // "akropus", 面向對象式調用沒問題6. 寫一個迭代函數(shù) map()
/** * 數(shù)組或對象遍歷方法,并返回修改后的對象或數(shù)組 * @param iteratee 回調函數(shù) * @param context 回調函數(shù)中this的指向 */ _.map = function(obj, iteratee, context) { var length = obj.length, results = Array(length); for (var index = 0; index < length; index++) { results[index] = iteratee.call(context, obj[index], index, obj); } return results; }; _.prototype.map = function(iteratee, context) { var length = this._wrapped.length, results = Array(length); for (var index = 0; index < length; index++) { results[index] = iteratee.call( context, this._wrapped[index], index, this._wrapped ); } return results; }; _([1, 2, 3]).map( function(item) { console.log(item + this.value); }, { value: 1 } ); // 2,3,4 _.map( [1, 2, 3], function(item) { console.log(item + this.value); }, { value: 1 } ); // 2,3,4
嗯嗯,真好,完美實現(xiàn)。到這里你會發(fā)現(xiàn)一個問題,每次我新增一個方法,都得在 prototype 上同時寫多一次這個相似函數(shù),你會發(fā)現(xiàn)兩者之間只是 obj 換成了 this._wrapped.有沒有辦法讓它自動生成呢?答案肯定是有!
7. 自動創(chuàng)建原型方法在這之前,我們需要先實現(xiàn)一個遍歷方法 each(),如下:
// 最大數(shù)值 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; // 判斷是否為數(shù)組 var isArrayLike = function(collection) { var length = collection.length; return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX; }; /** * 數(shù)組或對象遍歷方法 */ _.each = function(obj, callback) { var length, i = 0; if (isArrayLike(obj)) { // 數(shù)組 length = obj.length; for (; i < length; i++) { // 這里隱式的調用了一次 callback.call(obj[i], obj[i], i); if (callback.call(obj[i], obj[i], i) === false) { break; } } } else { // 對象 for (i in obj) { if (callback.call(obj[i], obj[i], i) === false) { break; } } } return obj; };
用 each() 來遍歷 _ 上掛載的所有方法函數(shù),并給 prototype 創(chuàng)建相應的方法函數(shù)。那么,在此之前,我們需要知道 _ 上掛載了哪些方法名,來寫個 functions() 實現(xiàn)它
/** * 判斷是否為 function */ _.isFunction = function(obj) { return typeof obj == "function" || false; }; /** * 獲取_的所有屬性函數(shù)名 */ _.functions = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); };
用 each()實現(xiàn)它:
var ArrayProto = Array.prototype; var push = ArrayProto.push; _.each(_.functions(_), function(name) { var func = _[name]; _.prototype[name] = function() { var args = [this._wrapped]; // args = [this._wrapped, arguments[0], arguments[1]...], 相當于用 this._wrapped 代替 obj 實現(xiàn) push.apply(args, arguments); return func.apply(_, args); }; });7. 當前最終代碼
(function() { // root 為掛載對象,為 self 或 global 或 this 或 {} var root = (typeof self == "object" && self.self === self && self) || (typeof global == "object" && global.global === global && global) || this || {}; var _ = function(obj) { // 如果傳入的是實例后對象,返回它 if (obj instanceof _) return obj; // 如果還沒有實例化,new _(obj) if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // 最大數(shù)值 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var ArrayProto = Array.prototype; var push = ArrayProto.push; // 判斷是否為數(shù)組 var isArrayLike = function(collection) { var length = collection.length; return ( typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX ); }; root._ = _; _.VERSION = "1.9.1"; // 給我們的 underscore 一個版本號吧 /** * 字符串倒裝 */ _.reverse = function(string) { return string .split("") .reverse() .join(""); }; /** * 判斷是否為 function */ _.isFunction = function(obj) { return typeof obj == "function" || false; }; /** * 獲取_的所有屬性函數(shù)名 */ _.functions = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; /** * 數(shù)組或對象遍歷方法,并返回修改后的對象或數(shù)組 * @param iteratee 回調函數(shù) * @param context 回調函數(shù)中this的指向 */ _.map = function(obj, iteratee, context) { var length = obj.length, results = Array(length); for (var index = 0; index < length; index++) { results[index] = iteratee.call(context, obj[index], index, obj); } return results; }; /** * 數(shù)組或對象遍歷方法 */ _.each = function(obj, callback) { var length, i = 0; if (isArrayLike(obj)) { // 數(shù)組 length = obj.length; for (; i < length; i++) { // 這里隱式的調用了一次 callback.call(obj[i], obj[i], i); if (callback.call(obj[i], obj[i], i) === false) { break; } } } else { // 對象 for (i in obj) { if (callback.call(obj[i], obj[i], i) === false) { break; } } } return obj; }; _.each(_.functions(_), function(name) { var func = _[name]; _.prototype[name] = function() { var args = [this._wrapped]; // args = [this._wrapped, arguments[0], arguments[1]...], 相當于用 this._wrapped 代替 obj 實現(xiàn) push.apply(args, arguments); return func.apply(_, args); }; }); })();未完待續(xù),靜待下篇
前端進階小書(advanced_front_end)
前端每日一題(daily-question)
webpack4 搭建 Vue 應用(createVue)
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103887.html
摘要:上篇文章講述了的基本結構搭建,本文繼續(xù)講鏈式調用與混入。獲得一個經(jīng)包裹后的實例標識當前實例支持鏈式調用小試牛刀返回的為一個實例對象,后面的方法判斷屬性是否為為的話再調用一次方法再返回原來實例即可。 showImg(https://segmentfault.com/img/bVbrSwH?w=1374&h=773); 上篇文章講述了 underscore 的基本結構搭建,本文繼續(xù)講鏈式調...
摘要:我采用原生編寫后臺,因為感覺增刪改查的功能很簡單,就懶得用框架了其實是不會。瀏覽模式它也有一個,用來切換文章列表和文章詳情,也就是和編輯模式它加載了作為工具欄,然后可以進行文章的撰寫與修改。 介紹 項目地址:https://github.com/jrainlau/MintloG (特別亂,參考就好-_-|||)showImg(https://segmentfault.com/img/b...
摘要:即將立秋的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。課多周刊機器人運營中心是如何玩轉起來的分享課多周刊是如何運營并堅持下來的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大...
摘要:即將立秋的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。課多周刊機器人運營中心是如何玩轉起來的分享課多周刊是如何運營并堅持下來的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大...
閱讀 3952·2021-11-18 13:21
閱讀 4759·2021-09-27 14:01
閱讀 3110·2019-08-30 15:53
閱讀 2388·2019-08-30 15:43
閱讀 1730·2019-08-30 13:10
閱讀 1508·2019-08-29 18:39
閱讀 887·2019-08-29 15:05
閱讀 3341·2019-08-29 14:14