zoukankan      html  css  js  c++  java
  • [Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性

    许多js环境都提供检查调用栈的功能。调用栈是指当前正在执行的活动函数链。在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.caller。前者指向使用该arguments对象被调用的函数。后者指向调用该arguments对象被调用的函数的函数。许多环境支持arguments.callee,但它除了允许匿名函数递归地引用自身之外,没有更多的用途了。(高3中认为使用arguments.callee可以解除函数体内的代码和函数名之间的耦合,看来也不是完全没有用的)

    图示

    下面是一个简单的图示,可以容易了解arguments的callee,caller,及函数的caller
    1465378571303

    var factorial=(function(n){
        return (n<=1)?1:(n*arguments.callee(n-1));
    })
    

    但这个也是特别的有用,可以使用函数名来引用函数自身

    function factorial(n){
        return (n<=1)?1:(n*factorial(n-1));
    }
    

    arguments.caller属性更为强大。它指向的是使用该arguments对象调用函数的函数。出于安全考虑,大多数环境已经移除了此特性,因此用它时要检测一下才行。许多JS环境也提供了一个相似的函数对象属性--非标准但普遍适应的caller属性。它指向函数最近的调用者。

    function revealCaller(){
        return revealCaller.caller;
    }
    function start(){
        return revealCaller();
    }
    start()===start;//true;

    可以利用该属性获取一个提供当前调用栈快照的数据结构。构建一个栈跟踪:

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

    使用示例

    function f1(){
        return getCallStack();
    }
    function f2(){
        return f1();
    }
    var trace=f2();//[f1(), f2()]

    脆弱性,当某个函数在调用栈中出现不止一次,那么栈检查逻辑将会陷入循环。

    function f(n){
        return n===0?getCallStack():f(n-1);
    }
    var trace=f(1);//

    问题出在哪?由于函数f递归地调用其自身,因此其caller属性会自动更新,指回到函数f。所以,函数getCallStack会陷入无限地查找函数f的循环之中。即使我们试图检测该循环,但在函数f调用其自身之前也没有关于哪个函数调用了它的信息。因为其他调用栈的信息已经丢失了。
    这些栈检查属性都是非标准的,在移植性或适用性上很受限制。在ES5的严格模式的函数中,它们是被禁止使用的。试图获取严格函数或arguments对象的caller或callee属性都将报错。

    function f(){
      'use strict';
      return f.caller;
    }
    f();//Uncaught TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context.(…)

    最好的策略是完全避免栈检查。如果检查栈的理由完全是为了测试,那么更为可靠的方式是使用交互式的调试器。

    提示

    • 避免使用非标准的arguments.caller和arguments.callee属性,它们不具备良好的移植性

    • 避免使用非标准的函数对象caller属性,因为在包含全部栈信息方面,它是不可靠的

    附录:这个没附录,放水一篇,因为这节实在没什么可写的。

  • 相关阅读:
    Entity Framework 学习中级篇2—存储过程(上)(转)
    PB TreeView 属性,事件详解(转)
    java面试宝典
    Entity Framework 学习初级篇5ObjectQuery查询及方法(转)
    关于安卓中国移动定制机GPS定位问题解决办法
    Asp.net中优化页面小技巧—让搜索引擎更容易找到你的页面!
    服务器SQl2000和SQl2005版本不同带来一系列问题解决方案
    Asp.net中小技巧—服务器端Web控件与客户端Html控件交互
    Asp.net中利用ExecuteNonQuery()执行存储过程返回1解决方案
    Asp.net中DropDownlist中无法触发后台事件解决方案
  • 原文地址:https://www.cnblogs.com/wengxuesong/p/5570821.html
Copyright © 2011-2022 走看看