• JavaScript:遍历原型链,调用栈,作用域链(转)


    JavaScript:遍历原型链,调用栈,作用域链

     

    在JavaScript中,有三种常见的链式结构:原型链(Prototype Chain),调用栈(Call Stack),作用域链(Scope Chain).本文并不准备讲这些概念的基础知识,而是要给出如何遍历这三种链结构的方法,从而加深理解.

    遍历原型链

    在JavaScript中,任何对象都有自己的原型链.原型链是由一系列对象加上最后的null组成的.如果还没掌握相关基础知识,可以看看我在MDN上翻译的继承与原型链一文.遍历函数如下:

    function getPrototypeChain(obj) {
        var protoChain = [];
        while (obj = Object.getPrototypeOf(obj)) {
            protoChain.push(obj);
        }
        protoChain.push(null);
        return protoChain;
    }

    尝试执行一下

    >getPrototypeChain(new String(""))
    [String, Object, null]                     //依次是String.prototype,Object.prototype,null
      
    >getPrototypeChain(function(){})
    [function Empty() {}, Object, null]        //依次是Function.prototype,Object.prototype,null

    这个函数是在我以前写的一篇文章JavaScript:我对原型链的理解中给出的.

    遍历调用栈

    在JavaScript中,调用栈就是一系列的函数,表明当前函数是由哪些上层函数调用的.遍历函数如下:

    function getCallStack() {
        var stack = [];
        var fun = getCallStack;
        while (fun = fun.caller) {
            stack.push(fun)
        }
        return stack
    }
    

    该函数用到了非标准的caller属性,不过主流浏览器都支持它.尝试执行一下:

    function a() {
        b()
    }
    
    function b() {
        c()
    }
    
    function c() {
        alert(getCallStack().map(function (fun) {
            return fun.name  //使用了非标准的name属性
        }))  
    }
    
    a()  //弹出c,b,a
    b()  //弹出c,b

    在调试工具中,我们可以直接使用console.trace()来打印出调用栈.在递归调用中,如果调用栈的长度过长,引擎就会抛出异常"too much recursion".到底多长是上限,不同的引擎不同的操作系统环境这个值是不同的.可以使用下面这个函数表达式获取到这个上限值:

    > (function(i){try{(function m(){++i&&m()}())}catch(e){return i}})(0)
    50761

    遍历作用域链

    作用域链是由一系列执行上下文(Execution context)中的活动对象(Activation object)加最后的全局对象组成的.活动对象是一个抽象实体(Abstract Entity),它是由引擎内部来管理的,并不能通过JavaScript来访问.看不到,摸不着,所以这些知识就很难理解.

    不过在Mozilla的引擎中,有一个魔法属性__parent__可以获取到函数执行时的活动对象.只是在SpiderMonkey中,该属性已经被删除了(Firefox 4开始).不过在Mozilla的另外一个JavaScript引擎Rhino(Java编写)上,还可以使用这个特殊属性.遍历代码如下:

    function getScopeChain(fun) {
        var scopeChain = [];
        while (fun = fun.__parent__) {
            scopeChain.push(fun);
    }
    return scopeChain;
    }

    尝试执行一下:

    var a = 0;
    (function fun1() {
        var a = 1;
        (function fun2() {
            var a = 2;
            (function fun3() {
                var a = 3;
                getScopeChain(function () {}).map(function (obj) {
                    print("-----------------------------")
                    for(var i in obj){
                        print(i + ":" + (obj[i].name?obj[i].name:obj[i]))
                    }
                })
            })()
        })()
    })()
    
    -----------------------------           //函数fun3
    arguments:[object Arguments]        
    a:3
    fun3:fun3
    -----------------------------           //函数fun2
    arguments:[object Arguments]
    a:2
    fun2:fun2
    -----------------------------           //函数fun1
    arguments:[object Arguments]
    a:1
    fun1:fun1
    -----------------------------           //全局上下文
    a:0
    getScopeChain:getScopeChain
  • 相关阅读:
    分布式文件系统--->fastDFS
    varnish4.0缓存代理配置
    varnish4.0缓存代理配置
    varnish4.0缓存代理配置
    SDN 是什么
    SDN 是什么
    SDN 是什么
    Solidworks如何保存为网页可以浏览的3D格式
    github 的 配置SSH
    当一个实例被创建,__init__()就会被自动调用
  • 原文地址:https://www.cnblogs.com/sandianbaozi/p/2799529.html
Copyright © 2020-2023  润新知