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

图示

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

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属性,因为在包含全部栈信息方面,它是不可靠的

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

[Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性的更多相关文章

  1. [Effective JavaScript 笔记]第40条:避免继承标准类

    ECMAScript标准库里配备了许多重要的类,如Array,function,以及Date等.扩展这些类生成子类可以方便完成很多工作,但它们的定义具有很多特殊的行为,所以很难写出行为正确的类. Ar ...

  2. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  3. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  4. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  5. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  6. [Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域

    嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x) ...

  7. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  8. [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

  9. [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

随机推荐

  1. 初识Groovy

    Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python.Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代 ...

  2. Swift 玩转 3D Touch 之 Peek & Pop

    什么是3D Touch 3D Touch 是iOS9之后专为 iPhone6s 机型加入的新特性,这一新技术移植于 Mac Book 上的 ForceTouch 更准确地说应该是 ForceTouch ...

  3. Unity3D 的摄像机

    什么是摄像机 Unity3D中,摄像机是一个非常非常重要的组件. 他的作用就是:将你设计的场景投影到设备的屏幕上. 摄像机的属性 1 clear flags 确定屏幕的哪一部分将被清除. 每个摄像机在 ...

  4. [BZOJ2879][Noi2012]美食节(最小费用最大流动态加边)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2879 分析: 和bzoj1070一样,但这题的数据范围大了很多,如果直接建图就会TLE ...

  5. 完美实现开机启动虚拟WIFI,顺便实现目前的WP8系统使用VPN(7.1修)

    众所周知,windows7系统的机器若带有无线网卡(台式机可以买一个USB无线网卡,京东目前39元,TP-Link的),可以虚拟出wifi,供手机等移动设备使用. 虚拟的WIFI的命了和软件在网上都找 ...

  6. 第一次作业---安卓开发工具Android studio发展演变

    Android studio2013年由谷歌推出,用于安卓端的开发,我所使用的版本为2015年5月推出的1.3.2. 1.安装.配置.作为麻瓜的我,刚刚接触Android studio时在安装方面走了 ...

  7. HTTP协议强化理解

    一:第一波 1.  是什么? 答:是一种定义超文本在网络中如何进行传输的协议!   所有的WWW上的文件都必须遵循! 是基于TCP/IP. 传输路径:  客户端<——>服务端  (全双工) ...

  8. java多线程-BlockingQueue

    BlockingQueue简介 ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小.并且可以指定公平性与非公平性,默认情 ...

  9. FusionCharts的使用方法

    来源于:http://www.cnblogs.com/xuhongfei/archive/2013/04/12/3016882.html 今天统计价格变化规律的时候找到的一个很好的文档,很详细 一.简 ...

  10. SQLHelper---赵晓虎(简洁,全面)

    public static class SQLHelper { //获取连接字符串,,首先添加对configuration的引用 private static string connStr = Con ...