如何从源码中学习javascript
艾伦说啊,学习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,自动带你去。你只要在脑子里预先猜想一下,你的心中的流程和实际的是否一
样。在一些具体的方法体内,初步调式的时候,你大可跳过。先理清流程,整出大至的思路。然后看实现的细节就比较有体会了。
在明白这些实现之后,在自己重新默写一遍,看能不能实现。最后试着简化,用自己的代码去实现它。
这一路下来,你会收很多收获的。
如何从源码中学习javascript的更多相关文章
- Jquery源码中的Javascript基础知识(三)
这篇主要说一下在源码中jquery对象是怎样设计实现的,下面是相关代码的简化版本: (function( window, undefined ) { // code 定义变量 jQuery = fun ...
- Jquery源码中的Javascript基础知识(一)
jquery源码中涉及了大量原生js中的知识和概念,文章是我在学习两者的过程中进行的整理和总结,有不对的地方欢迎大家指正. 本文使用的jq版本为2.0.3,附上压缩和未压缩版本地址: http://a ...
- MVVM架构~knockoutjs系列之从Knockout.Validation.js源码中学习它的用法
返回目录 说在前 有时,我们在使用一个插件时,在网上即找不到它的相关API,这时,我们会很抓狂的,与其抓狂,还不如踏下心来,分析一下它的源码,事实上,对于JS这种开发语言来说,它开发的插件的使用方法都 ...
- Jquery源码中的Javascript基础知识(二)
接上一篇,jquery源码的这种写法叫做匿名函数自执行 (function( window, undefined ) { // code })( window ); 函数定义了两个参数window和u ...
- 快来!我从源码中学习到了一招Dubbo的骚操作!
荒腔走板 大家好,我是 why,欢迎来到我连续周更优质原创文章的第 55 篇. 老规矩,先来一个简短的荒腔走板,给冰冷的技术文注入一丝色彩. 魔幻的 2020 年的上半年过去了,很多人都在朋友圈和上半 ...
- Jquery源码中的Javascript基础知识(四)— jQuery.fn.init方法
$() 即调用了jQuery.fn.init方法 jQuery = function( selector, context ) { return new jQuery.fn.init( selecto ...
- Spring源码学习:第1步--在Spring源码中添加最简单的Demo代码
为了最大程度地贴近Spring源码并进行学习,一种比较直接的做法是:直接在Spring源码中加入Demo代码,并进行调试. 参照以前使用Spring的经验,Spring最简单的使用方法是:一个实体类. ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- 访何红辉:谈谈Android源码中的设计模式
最近Android 6.0版本的源代码开放下载,刚好分析Android源码的技术书籍<Android源码设计模式解析与实战>上市,我们邀请到它的作者何红辉,来谈谈Android源码中的设计 ...
随机推荐
- QGis、Gdal本地中文路径问题
编译qgis完整项目后,由于Gdal库的原因,中文路径下通过添加矢量数据中数据库中是没有OGR的Oracle数据库功能的: 最开始打算通过重新编译gadl库从内部支持中文的(有成功的麻烦也请告诉我), ...
- Windows下ADT环境搭建
1.JDK安装 下载JDK(点我下载),安装成功后在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量: JAVA_HOME值为C:\Program Files ...
- Java 用LinkdeList实现52张扑克牌
用LinkdeList实现52张扑克牌(不含大小王)的洗牌功能.提示:花色 ,和数字分别用数组存储. import java.util.LinkedList; import java.util.Ran ...
- 获取IP地址 & 伪装IP地址发送请求
//获取请求客户端IP地址 public final static String getIpAddress(HttpServletRequest request) throws IOExcepti ...
- 使用--gc-section编译选项减小程序体积
本周在给程序添加功能的时候,突然发现,我只是写了几个函数,还没调用,size就变大了.这肯定是不行的嘛,没用的函数就应该不链接进来,占用我宝贵的空间. 这种功能,讲道理编译器肯定要支持的,于是搜了一下 ...
- mysql 命令行还原备份数据库
通常数据库还原备份可以通过navicat等数据库管理工具进行,只需要简单的导出导入就行了,但遇到有索引外键的数据库,数据库管理工具运行.sql文件会报错,这时候可以尝试命令行导入,亲测可以成功 MyS ...
- centos 格式化分区
#格式化U盘,成fat32 fdisk -l #获取U盘设备信息 #Disk /dev/sdc: 16.0 GB, 16025387008 bytes, 31299584 sectors#Units ...
- IOS 真机调试
真机调试的步骤: 1.注册成为苹果开发者(99$) 2.登陆苹果开发者主页 https://developer.apple.com/membercenter/index.action 3.点击 Cer ...
- 安装mysql
查看已安装的mysql,并删除它们 rpm -qa|grep -i mysql rpm -e --nodeps filename 如果重装mysql,查找安装mysql产生的文件,并删除它们 find ...
- C#获取本机可用端口
当我们要创建一个Tcp/UDP Server connection ,我们需要一个范围在1000到65535之间的端口 .但是本机一个端口只能一个程序监听,所以我们进行本地监听的时候需要检测端口是否被 ...