zoukankan      html  css  js  c++  java
  • 如何从源码中学习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,自动带你去。你只要在脑子里预先猜想一下,你的心中的流程和实际的是否一

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

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

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

  • 相关阅读:
    PAT 甲级 1051 Pop Sequence (25 分)(模拟栈,较简单)
    PAT 甲级 1050 String Subtraction (20 分) (简单送分,getline(cin,s)的使用)
    PAT 甲级 1049 Counting Ones (30 分)(找规律,较难,想到了一点但没有深入考虑嫌麻烦)***...
    PAT 甲级 1048 Find Coins (25 分)(较简单,开个数组记录一下即可)
    PAT 甲级 1047 Student List for Course (25 分)(cout超时,string scanf printf注意点,字符串哈希反哈希)...
    PAT 甲级 1046 Shortest Distance (20 分)(前缀和,想了一会儿)
    R语言实现金融数据的时间序列分析及建模
    ES6 | class类的基本语法总结
    less使用总结
    umi 的项目中如何修改 favicon
  • 原文地址:https://www.cnblogs.com/afrog/p/3829187.html
Copyright © 2011-2022 走看看