艾伦说啊,学习javascript,必须要学会看源码,通过高手的源码,你可以从中吸取很多书本上难以看到的技巧。

看源码就好像喝鸡汤,所有的营养都在这汤里了。这汤就是源码,高手写的源码,就是最好的鸡汤。

这也是他短短两年时间,快速在前端界崭头露角,成为一名新星的原因。一般人,他不会说的,但是我觉得,好东西就要分享。

这样才能让前端界有更多新星出现,为推进整个前端的健康发展做出贡献。

他的这些源码经验,不知道会不会在他的新书里出现。如果有,算是提前爆料了。

下面就是我这些天,一边实践,一边总结的一些方法。

我以艾伦的一个模块类require.js为例。

require.js的源码:

  1. /****************************************************************
  2. * 支持AMD,CMD模块加载方式
  3. * @by Aaron
  4. * github:https://github.com/JsAaron/aaronRequire
  5. * blog:http://www.cnblogs.com/aaronjs/
  6. *****************************************************************/
  7. ;(function(r) {
  8. if (typeof module === "object" && typeof require === "function") {
  9. module.exports.require = r.require;
  10. module.exports.define = r.define;
  11. } else {
  12. require = r.require;
  13. define = r.define;
  14. }
  15. })(function() {
  16. var objproto = Object.prototype,
  17. objtoString = objproto.toString,
  18. arrproto = Array.prototype,
  19. nativeForEach = arrproto.forEach,
  20. modules = {},
  21. pushStack = {};
  22.  
  23. function each(obj, callback, context) {
  24. if (obj == null) return;
  25. //如果支持本地forEach方法,并且是函数
  26. if (nativeForEach && obj.forEach === nativeForEach) {
  27. obj.forEach(callback, context);
  28. } else if (obj.length === +obj.length) {
  29. //for循环迭代
  30. for (var i = 0, l = obj.length; i < l; i++) {
  31. if (callback.call(context, obj[i], i, obj) === breaker) return;
  32. }
  33. }
  34. };
  35.  
  36. function isFunction(it) {
  37. return objtoString.call(it) === '[object Function]';
  38. }
  39.  
  40. function isArray(it) {
  41. return objtoString.call(it) === '[object Array]';
  42. }
  43.  
  44. //解析依赖关系
  45. function parseDeps(module) {
  46. var deps = module['deps'],
  47. temp = [];
  48. each(deps, function(id, index) {
  49. temp.push(build(modules[id]))
  50. })
  51.  
  52. return temp;
  53. }
  54.  
  55. function build(module) {
  56. var depsList,existMod,
  57. factory = module.factory,
  58. id = module.id;
  59.  
  60. if (existMod = pushStack[id]) { //去重复执行
  61. return existMod;
  62. }
  63.  
  64. //接口点,将数据或方法定义在其上则将其暴露给外部调用。
  65. module.exports = {};
  66.  
  67. //去重
  68. delete module.factory;
  69.  
  70. if (module.deps) {
  71. //依赖数组列表
  72. depsList = parseDeps(module);
  73. module.exports = factory.apply(module, depsList);
  74. } else {
  75. // exports 支持直接 return 或 modulejs.exports 方式
  76. module.exports = factory(require, module.exports, module) || module.exports;
  77. }
  78.  
  79. pushStack[id] = module.exports;
  80.  
  81. return module.exports;
  82. }
  83.  
  84. //解析require模块
  85. function makeRequire(ids, callback) {
  86. var r = ids.length,
  87. shim = [];
  88. while (r--) {
  89. shim.unshift(build(modules[ids[r]]));
  90. }
  91. if (callback) {
  92. callback.apply(null, shim);
  93. } else {
  94. shim = null;
  95. }
  96. }
  97.  
  98. return {
  99. //引入模块
  100. require: function(id, callback) {
  101. //数组形式
  102. //require(['domReady', 'App'], function(domReady, app) {});
  103. if (isArray(id)) {
  104. if (id.length > 1) {
  105. return makeRequire(id, callback);
  106. }
  107. id = id[0];
  108. }
  109.  
  110. if (!modules[id]) {
  111. throw "module " + id + " not found";
  112. }
  113.  
  114. if (callback) {
  115. var module = build(modules[id]);
  116. callback(module)
  117. return module;
  118. } else {
  119. if (modules[id].factory) {
  120. return build(modules[id]);
  121. }
  122. return modules[id].exports;
  123. }
  124. },
  125. //定义模块
  126. define: function(id, deps, factory) { //模块名,依赖列表,模块本身
  127. if (modules[id]) {
  128. throw "module " + id + " 模块已存在!";
  129. }
  130. //存在依赖导入
  131. if (arguments.length === 3) {
  132. modules[id] = {
  133. id : id,
  134. deps : deps,
  135. factory : factory
  136. };
  137. } else {
  138. modules[id] = {
  139. id : id,
  140. factory : deps
  141. };
  142. }
  143. }
  144. }
  145.  
  146. }());
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>changsha,Aaron</title>
  6.  
  7. <!-- 引用自Aaron私有框架Xut -->
  8. <script type="text/javascript" src="require.js"></script>
  9.  
  10. </head>
  11. <body>
  12.  
  13. </body>
  14. <script type="text/javascript">
  15. //定义模块a
  16. define('a',function(){
  17. function a(){
  18. console.log('a is fine')
  19. //your code
  20. }
  21. return a
  22. });
  23. //定义模块b
  24. define('b',function(){
  25. function b(){
  26. console.log('b is fine')
  27. //your code
  28. }
  29. return b
  30. });
  31.  
  32. //定义模块c,依赖模块a 和 b
  33. define('c',['a','b'],function(a,b){
  34. function c(){
  35. a();
  36. b();
  37. console.log('c is fine')
  38. //your code
  39. }
  40. return c
  41. });
  42.  
  43. //引入模块c
  44. // require('c',function(c){
  45. // c()
  46. // })
  47.  
  48. // //引入模块a b c
  49. // require(['a','b','c'],function(c){
  50. // // c()
  51. // })
  52.  
  53. // 引入模块a,并执行
  54. require('a')()
  55. </script>
  56. </html>

我推荐用谷歌浏览器调式,很方便。

学源码的第一条法则,是从函数调用处开始看。

我的demo是从define('a')开始的。这是最简单的一种情况。

打开浏览器看下

谷歌的控制台很棒,我常用有,Console用来输出日志,Resources用来查看数据

elements查看结构,样式。Sourcese用来断点调式。

今天想重点记录一下这个断点调式。

从哪里开始断,这个直接影响到调式的效果。自己摸索着体会吧。

按F11看光标经过的地方,停留在变量名上,会显示出变量存放的值。这比打一堆的console.log或者alert要方便很多

而且也不用看这个函数在哪里定义的,按F11,自动带你去。你只要在脑子里预先猜想一下,你的心中的流程和实际的是否一

样。在一些具体的方法体内,初步调式的时候,你大可跳过。先理清流程,整出大至的思路。然后看实现的细节就比较有体会了。

在明白这些实现之后,在自己重新默写一遍,看能不能实现。最后试着简化,用自己的代码去实现它。

这一路下来,你会收很多收获的。

如何从源码中学习javascript的更多相关文章

  1. Jquery源码中的Javascript基础知识(三)

    这篇主要说一下在源码中jquery对象是怎样设计实现的,下面是相关代码的简化版本: (function( window, undefined ) { // code 定义变量 jQuery = fun ...

  2. Jquery源码中的Javascript基础知识(一)

    jquery源码中涉及了大量原生js中的知识和概念,文章是我在学习两者的过程中进行的整理和总结,有不对的地方欢迎大家指正. 本文使用的jq版本为2.0.3,附上压缩和未压缩版本地址: http://a ...

  3. MVVM架构~knockoutjs系列之从Knockout.Validation.js源码中学习它的用法

    返回目录 说在前 有时,我们在使用一个插件时,在网上即找不到它的相关API,这时,我们会很抓狂的,与其抓狂,还不如踏下心来,分析一下它的源码,事实上,对于JS这种开发语言来说,它开发的插件的使用方法都 ...

  4. Jquery源码中的Javascript基础知识(二)

    接上一篇,jquery源码的这种写法叫做匿名函数自执行 (function( window, undefined ) { // code })( window ); 函数定义了两个参数window和u ...

  5. 快来!我从源码中学习到了一招Dubbo的骚操作!

    荒腔走板 大家好,我是 why,欢迎来到我连续周更优质原创文章的第 55 篇. 老规矩,先来一个简短的荒腔走板,给冰冷的技术文注入一丝色彩. 魔幻的 2020 年的上半年过去了,很多人都在朋友圈和上半 ...

  6. Jquery源码中的Javascript基础知识(四)— jQuery.fn.init方法

    $() 即调用了jQuery.fn.init方法 jQuery = function( selector, context ) { return new jQuery.fn.init( selecto ...

  7. Spring源码学习:第1步--在Spring源码中添加最简单的Demo代码

    为了最大程度地贴近Spring源码并进行学习,一种比较直接的做法是:直接在Spring源码中加入Demo代码,并进行调试. 参照以前使用Spring的经验,Spring最简单的使用方法是:一个实体类. ...

  8. Android 网络框架之Retrofit2使用详解及从源码中解析原理

    就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...

  9. 访何红辉:谈谈Android源码中的设计模式

    最近Android 6.0版本的源代码开放下载,刚好分析Android源码的技术书籍<Android源码设计模式解析与实战>上市,我们邀请到它的作者何红辉,来谈谈Android源码中的设计 ...

随机推荐

  1. QGis、Gdal本地中文路径问题

    编译qgis完整项目后,由于Gdal库的原因,中文路径下通过添加矢量数据中数据库中是没有OGR的Oracle数据库功能的: 最开始打算通过重新编译gadl库从内部支持中文的(有成功的麻烦也请告诉我), ...

  2. Windows下ADT环境搭建

    1.JDK安装 下载JDK(点我下载),安装成功后在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量: JAVA_HOME值为C:\Program Files ...

  3. Java 用LinkdeList实现52张扑克牌

    用LinkdeList实现52张扑克牌(不含大小王)的洗牌功能.提示:花色 ,和数字分别用数组存储. import java.util.LinkedList; import java.util.Ran ...

  4. 获取IP地址 & 伪装IP地址发送请求

    //获取请求客户端IP地址   public final static String getIpAddress(HttpServletRequest request) throws IOExcepti ...

  5. 使用--gc-section编译选项减小程序体积

    本周在给程序添加功能的时候,突然发现,我只是写了几个函数,还没调用,size就变大了.这肯定是不行的嘛,没用的函数就应该不链接进来,占用我宝贵的空间. 这种功能,讲道理编译器肯定要支持的,于是搜了一下 ...

  6. mysql 命令行还原备份数据库

    通常数据库还原备份可以通过navicat等数据库管理工具进行,只需要简单的导出导入就行了,但遇到有索引外键的数据库,数据库管理工具运行.sql文件会报错,这时候可以尝试命令行导入,亲测可以成功 MyS ...

  7. centos 格式化分区

    #格式化U盘,成fat32 fdisk -l #获取U盘设备信息 #Disk /dev/sdc: 16.0 GB, 16025387008 bytes, 31299584 sectors#Units ...

  8. IOS 真机调试

    真机调试的步骤: 1.注册成为苹果开发者(99$) 2.登陆苹果开发者主页 https://developer.apple.com/membercenter/index.action 3.点击 Cer ...

  9. 安装mysql

    查看已安装的mysql,并删除它们 rpm -qa|grep -i mysql rpm -e --nodeps filename 如果重装mysql,查找安装mysql产生的文件,并删除它们 find ...

  10. C#获取本机可用端口

    当我们要创建一个Tcp/UDP Server connection ,我们需要一个范围在1000到65535之间的端口 .但是本机一个端口只能一个程序监听,所以我们进行本地监听的时候需要检测端口是否被 ...