许多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. 抽象类 VS 接口

    引言 接口和抽象类是面向对象编程(OOP, Object Oriented programming)中两个绕不开的概念,二者相似而又有所不同.接下来,我们来了解二者的概念并比较它们的异同. 什么是抽象 ...

  2. C++求一个十进制的二进制中1的个数

    int oneNumInBinary(int n){ ; while(n){ n = n&(n-); cnt++; } return cnt; }

  3. height和line-height有什么区别?

    <div style="height:120px;">是用来规定整个div的高度,文字还是默认会在顶端开始向下排列<div style="line-he ...

  4. 从C#到Objective-C,循序渐进学习苹果开发(6)--视图控制器的使用

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本篇主要开始介绍基于XCod ...

  5. RPC(Remote Procedure Call Protocol)——远程过程调用协议 学习总结

        首先了解什么叫RPC,为什么要RPC,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需 ...

  6. zhuang 定制iOS 7中的导航栏和状态栏

    近期,跟大多数开发者一样,我也正忙于对程序进行升级以适配iOS 7.最新的iOS 7外观上有大量的改动.从开发者的角度来看,导航栏和状态栏就发生了明显的变化.状态栏现在是半透明的了,这也就意味着导航栏 ...

  7. 用EXcel制作不同背景的图

    Excel 绘图区分区设置不同背景色 之 条形图 样图 在Excel图表中,如对绘图区设置背景色,一般只能对整个绘图区设置同一种颜色.图案或图片为背景.但有时希望能对不同的分区设置不同的颜色作为背景, ...

  8. java学习第9天

    形式参数和返回值的问题 (1)形式参数: 类名:需要该类的对象 抽象类名:需要该类的子类对象 接口名:需要该接口的实现类对象 (2)返回值类型: 类名:返回的是该类的对象 抽象类名:返回的是该类的子类 ...

  9. ZeroMQ(java)之I/O线程的实现与组件间的通信

    算是开始读ZeroMQ(java)的代码实现了吧,现在有了一个大体的了解,看起来实现是比较的干净的,抽象什么的不算复杂... 这里先来看看它的I/O线程的实现吧,顺带看看是如何实现组件的通信的.... ...

  10. Implicit Object in JSP

    Implicit Object Description request The HttpServletRequest object associated with the request. respo ...