艾伦说啊,学习javascript,必须要学会看源码,通过高手的源码,你可以从中吸取很多书本上难以看到的技巧。
看源码就好像喝鸡汤,所有的营养都在这汤里了。这汤就是源码,高手写的源码,就是最好的鸡汤。
这也是他短短两年时间,快速在前端界崭头露角,成为一名新星的原因。一般人,他不会说的,但是我觉得,好东西就要分享。
这样才能让前端界有更多新星出现,为推进整个前端的健康发展做出贡献。
他的这些源码经验,不知道会不会在他的新书里出现。如果有,算是提前爆料了。
下面就是我这些天,一边实践,一边总结的一些方法。
我以艾伦的一个模块类require.js为例。
require.js的源码:
/**************************************************************** * 支持AMD,CMD模块加载方式 * @by Aaron * github:https://github.com/JsAaron/aaronRequire * blog:http://www.cnblogs.com/aaronjs/ *****************************************************************/ ;(function(r) { if (typeof module === "object" && typeof require === "function") { module.exports.require = r.require; module.exports.define = r.define; } else { require = r.require; define = r.define; } })(function() { var objproto = Object.prototype, objtoString = objproto.toString, arrproto = Array.prototype, nativeForEach = arrproto.forEach, modules = {}, pushStack = {}; function each(obj, callback, context) { if (obj == null) return; //如果支持本地forEach方法,并且是函数 if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(callback, context); } else if (obj.length === +obj.length) { //for循环迭代 for (var i = 0, l = obj.length; i < l; i++) { if (callback.call(context, obj[i], i, obj) === breaker) return; } } }; function isFunction(it) { return objtoString.call(it) === '[object Function]'; } function isArray(it) { return objtoString.call(it) === '[object Array]'; } //解析依赖关系 function parseDeps(module) { var deps = module['deps'], temp = []; each(deps, function(id, index) { temp.push(build(modules[id])) }) return temp; } function build(module) { var depsList,existMod, factory = module.factory, id = module.id; if (existMod = pushStack[id]) { //去重复执行 return existMod; } //接口点,将数据或方法定义在其上则将其暴露给外部调用。 module.exports = {}; //去重 delete module.factory; if (module.deps) { //依赖数组列表 depsList = parseDeps(module); module.exports = factory.apply(module, depsList); } else { // exports 支持直接 return 或 modulejs.exports 方式 module.exports = factory(require, module.exports, module) || module.exports; } pushStack[id] = module.exports; return module.exports; } //解析require模块 function makeRequire(ids, callback) { var r = ids.length, shim = []; while (r--) { shim.unshift(build(modules[ids[r]])); } if (callback) { callback.apply(null, shim); } else { shim = null; } } return { //引入模块 require: function(id, callback) { //数组形式 //require(['domReady', 'App'], function(domReady, app) {}); if (isArray(id)) { if (id.length > 1) { return makeRequire(id, callback); } id = id[0]; } if (!modules[id]) { throw "module " + id + " not found"; } if (callback) { var module = build(modules[id]); callback(module) return module; } else { if (modules[id].factory) { return build(modules[id]); } return modules[id].exports; } }, //定义模块 define: function(id, deps, factory) { //模块名,依赖列表,模块本身 if (modules[id]) { throw "module " + id + " 模块已存在!"; } //存在依赖导入 if (arguments.length === 3) { modules[id] = { id : id, deps : deps, factory : factory }; } else { modules[id] = { id : id, factory : deps }; } } } }());
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>changsha,Aaron</title> <!-- 引用自Aaron私有框架Xut --> <script type="text/javascript" src="require.js"></script> </head> <body> </body> <script type="text/javascript"> //定义模块a define('a',function(){ function a(){ console.log('a is fine') //your code } return a }); //定义模块b define('b',function(){ function b(){ console.log('b is fine') //your code } return b }); //定义模块c,依赖模块a 和 b define('c',['a','b'],function(a,b){ function c(){ a(); b(); console.log('c is fine') //your code } return c }); //引入模块c // require('c',function(c){ // c() // }) // //引入模块a b c // require(['a','b','c'],function(c){ // // c() // }) // 引入模块a,并执行 require('a')() </script> </html>
我推荐用谷歌浏览器调式,很方便。
学源码的第一条法则,是从函数调用处开始看。
我的demo是从define('a')开始的。这是最简单的一种情况。
打开浏览器看下
谷歌的控制台很棒,我常用有,Console用来输出日志,Resources用来查看数据
elements查看结构,样式。Sourcese用来断点调式。
今天想重点记录一下这个断点调式。
从哪里开始断,这个直接影响到调式的效果。自己摸索着体会吧。
按F11看光标经过的地方,停留在变量名上,会显示出变量存放的值。这比打一堆的console.log或者alert要方便很多
而且也不用看这个函数在哪里定义的,按F11,自动带你去。你只要在脑子里预先猜想一下,你的心中的流程和实际的是否一
样。在一些具体的方法体内,初步调式的时候,你大可跳过。先理清流程,整出大至的思路。然后看实现的细节就比较有体会了。
在明白这些实现之后,在自己重新默写一遍,看能不能实现。最后试着简化,用自己的代码去实现它。
这一路下来,你会收很多收获的。