react做爲當前十分流行的前端框架,相信不少前端er都有蠢蠢欲動的學習它的想法。工欲善其事,必先利其器。這篇文章就簡單的給你們介紹一下如何我快速的搭建一個react
前端開發環境。主要針對於react
小白,大神不喜勿噴。
從標題能夠看出,這裏不會僅僅只介紹一下react
的開發環境如何搭建。我將這個系列分紅三篇介紹:前端
第一篇--快速搭建一個react
開發環境。react
第二篇--快速開發一個react
開發環境腳手架工具。有了這個工具,在任何地方都可以一鍵生成環境。git
第三篇--腳手架依賴的核心庫co
的源碼解析。github
該篇是這個系列文章的第三篇,主要是對co
的源碼進行分析講解。co
的源碼十分簡單,但實現的功能倒是十分的強大。不瞭解的同窗能夠經過co自行學習,也能夠經過我這篇源碼分析的文章進行更深刻的學習。segmentfault
co
源碼歸納co
源碼主要包含了兩部分:公共方法和私有方法。數組
一、公共方法promise
co
前端框架
co.wrap
app
二、私有方法框架
isObject
isGeneratorFunction
isGenerator
isPromise
objectToPromise
arrayToPromise
thunkToPromise
toPromise
源碼的閱讀順序建議先閱讀私有方法
的部分,而後在閱讀公共方法
的部分。各個部分的閱讀順序也按照上面列舉的順序進行閱讀。
co
源碼分析/** * slice() reference. */ var slice = Array.prototype.slice; /** * Expose `co`. */ module.exports = co['default'] = co.co = co; co.wrap = function (fn) { // 這個方法的主要做用就是將generator函數轉化成普通的函數調用 有點相似於thunk函數的轉化 /** * function* a(val) { * return val * } * * console.log(co(a,'pavoooo')) * console.log(co.wrap(a)('pavoooo')) 就能夠這樣調用了 * */ createPromise.__generatorFunction__ = fn; return createPromise; function createPromise() { return co.call(this, fn.apply(this, arguments)); } }; function co(gen) { var ctx = this; var args = slice.call(arguments, 1) // co的調用結果是一個promise對象 return new Promise(function(resolve, reject) { // 若是co的第一個參數是函數的話 就將第二個以及後續的參數傳遞給這個函數 // 並將gen的調用結果賦給gen /** * co(function(){ * console.log(arguments) * }, 1, 2, 3) * 不考慮下面轉化的狀況 這個函數運行以後 會打印出{ '0': 1, '1': 2, '2': 3 } * 同時gen的值就是undefined */ if (typeof gen === 'function') gen = gen.apply(ctx, args); // 這個條件判斷的就是 若是gen調用以後的返回值是undefined或者不是一個generator函數 直接將promise的狀態轉化成resolved // 同時將返回值做爲resolved的狀態值釋放 也就是說co函數的參數應該是一個generator函數 if (!gen || typeof gen.next !== 'function') return resolve(gen); // 調用onFulfilled函數--遞歸的調用generator函數的next方法 onFulfilled(); function onFulfilled(res) { var ret; try { // 這個語句有兩重做用 // 1、接收上一個yield返回的值 // 2、將調用以後的遍歷器賦值給ret並傳遞到next函數中以判斷gen調用是否結束 ret = gen.next(res); } catch (e) { return reject(e); } // 遞歸的調用next 也是是遞歸的執行gen函數的yield語句 next(ret); } function onRejected(err) { var ret; try { // 這個函數主要是當yield後的語句不符合規定的類型的時候 向外拋出一個錯誤 ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } function next(ret) { // 若是generator函數運行結束 直接釋放結果 這個結果就是gen函數中return的結果 這就能夠在外部經過then方法接收 if (ret.done) return resolve(ret.value); // 不然將遍歷器對應的value轉化成promise var value = toPromise.call(ctx, ret.value); // 若是可以成功的轉化成promise 調用then方法 將值釋放出來 並將其做爲onFulfilled函數的參數 而在onFulfilled函數內部 又經過 // gen.next()接收 這樣 就能夠把每次gen.next().value保存在gen函數內部的變量 if (value && isPromise(value)) return value.then(onFulfilled, onRejected); // 這裏表示傳遞給co函數的generator函數的yield後的語句必須是一個function, promise, generator, array, or object return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, ' + 'but the following object was passed: "' + String(ret.value) + '"')); } }); } function toPromise(obj) { // 這個函數實際上是對下面幾種將元素轉化成promise對象的幾個函數的集合 這樣作就不須要在各個函數中分別判斷值的類型而後 // 調用不一樣的方法 統一交給這個函數根據不一樣的值的類型調用不一樣的轉換函數 // obj是假值 if (!obj) return obj; // obj是promise if (isPromise(obj)) return obj; // obj是generation if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); // obj是thunk函數 if ('function' == typeof obj) return thunkToPromise.call(this, obj); // obj是數組 if (Array.isArray(obj)) return arrayToPromise.call(this, obj); // obj是對象 if (isObject(obj)) return objectToPromise.call(this, obj); // obj是普通類型的數據且爲真 如字符串 數字等 return obj; } function thunkToPromise(fn) { // thunk函數轉換成promise // js的thunk函數就是將多參數函數轉換成單參數函數的一種方式 // 約定俗成的也是第一個參數是error對象 後續的參數是函數的返回值 var ctx = this; return new Promise(function (resolve, reject) { fn.call(ctx, function (err, res) { // error不爲空 直接將promise轉化成rejected狀態 if (err) return reject(err); // 不然將函數轉化成resolve狀態 if (arguments.length > 2) res = slice.call(arguments, 1); resolve(res); }); }); } function arrayToPromise(obj) { // 數組轉化成promise-- // 先將數組中的各個元素轉化成promise 而後經過Promise.all進行包裝 轉化成一個新的promise實例並返回 return Promise.all(obj.map(toPromise, this)); } /** * 這個函數是將一個對象轉換成promise對象 從isPromise函數的內部可知 * 把對象轉換成promise對象的前提就是 這個對象必須具備then方法 也是是必須是一個thenable對象 */ function objectToPromise(obj){ // 經過obj的constructor 建立出一個新的對象 這個對象擁有obj全部繼承的屬性 這樣就能夠在這個對象上進行轉化 從而防止了更改源對象 var results = new obj.constructor(); //獲取到obj的全部非繼承屬性的鍵組成的數組 var keys = Object.keys(obj); // 定義一個promise容器 並傳遞給Promise.all這個方法 var promises = []; for (var i = 0; i < keys.length; i++) { var key = keys[i]; // 根據值的類型調用對應的轉換函數轉換成promise對象 var promise = toPromise.call(this, obj[key]); // 這個if條件中的第一個條件 是容錯處理 若是isPromise用的不少的狀況下 建議將這個容錯處理 // 放在isPromise函數中 轉換以後的值是promise 就調用then方法 取出promise對象中返回的值 // 而後其設置爲對應鍵的值 /** * 也就是說 若是一個對象是以下的形式: * var a = { * p: new Promise((resolve, reject) => { * resolve(2) * }) * } * * 通過defer函數的轉換以後a.p = 3 */ if (promise && isPromise(promise)) defer(promise, key); // 若是不是promise就直接返回對應的值 else results[key] = obj[key]; } // 經過Promise.all將多個promise實例轉化成一個新的實例 並返回 return Promise.all(promises).then(function () { return results; }); function defer(promise, key) { // predefine the key in the result results[key] = undefined; promises.push(promise.then(function (res) { results[key] = res; })); } } /** * 判斷obj是否是一個promise對象 * 根據promise A+的規範 * 一個合格的promise必須是一個thenable對象也就是其必須提供一個then方法來獲取值 * 因此咱們能夠經過判斷一個對象是否具備then方法來判斷是否是promise對象 但這不是絕對準確的方法 co內部經過Promise.all這個 * 方法對isPromise()返回true的對象進行了封裝 均可以將其轉化成promise對象 因此在使用的時候不須要過多的擔憂 */ function isPromise(obj) { return 'function' == typeof obj.then; } function isGenerator(obj) { return 'function' == typeof obj.next && 'function' == typeof obj.throw; } /** * 判斷參數obj是否是一個generator函數 */ function isGeneratorFunction(obj) { var constructor = obj.constructor; if (!constructor) return false; /** * 判斷是否是一個generator函數 * function* gen() {} ====> gen.constructor.name = 'GeneratorFunction' * 同時這個if條件也是對如下的這種狀況做的判斷 * function* gen() {} * var g = gen() ====> g.constructor.name = 'GeneratorFunction' * * displayName是一個非標準的屬性 用於返回函數顯示的名稱 不推薦使用 * */ if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; /** * obj是經過原生的generator函數操做得出 即 * obj = generator() * obj = Object.create(generator) * obj = Object.create(generator()) * * 上面if條件都會返回true * * 下面的這個isGenerator函數 筆者猜想 * 一是對原生generator函數調用以後返回的迭代器的判斷 * 而是對自定義的generator函數的判斷 * 好比這種形式的返回結果也是true * function A() {} * A.prototype.next = function() { * return { * value: 1, * done: false * } * } * Aa.prototype.throw = function() {} * * var a = new A() * console.log(isGeneratorFunction(a)) * */ return isGenerator(constructor.prototype); } /** * 用於判斷一個對象是否是純粹的js對象 * js中純粹的對象通常有三種建立方式 * var obj = {} * var obj = new Object * var obj = Object.create(Object.prototype) */ function isObject(val) { return Object == val.constructor; }
以上就是對co
源碼的大體分析,不理解的或者有異議的同窗歡迎留言討論。