许多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. 如何采用easyui tree编写简单角色权限代码

    首先每个管理员得对应一个角色: 而角色可以操作多个栏目,这种情况下我们可以采用tree多选的方式: 在页面上js代码: $('#Permission').dialog({ title: '栏目权限', ...

  2. Nginx 配置支持C++

    1.在auto/make脚本里添加新的编译器和链接器: #LINK = $LINK                         #原25行附近注释掉原链接器CXX=g++             ...

  3. JMeter Webservice测试计划

    一.新建测试计划:HelloWorld测试计划 二.添加线程组 编辑线程组属性: 三.添加 SOAP/ XML-RPC 请求元素 四.填入url地址及请求报文 此处的请求报文,是用soapUI生成的, ...

  4. TP-LINK telnet远程 重启路由器

    突然断网,以前房东的路由器管理页面可以打开,今天突然间就打不开了.ping了下,可以ping通,于是就想起了房东的路由器是TP-LINK的 可以 telnet登陆的.每次,断网,我都会重启房东的路由器 ...

  5. linux定时执行任务crontab命令用法

    linux系统的定时任务是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另外, 由于使用者自己也可以设置计划任务,所 ...

  6. SQL Server代理(1/12):配置和概况

    SQL Server代理是所有实时数据库的核心.代理有很多不明显的用法,因此系统的知识,对于开发人员还是DBA都是有用的.这系列文章会通俗介绍它的很多用法. SQL Server代理是SQL Serv ...

  7. Activity中使用Intent实现页面跳转与参数的传递(转)

    新建一个FirstAvtivity.java package com.zhuguangwei; import android.app.Activity; import android.content. ...

  8. 两步验证Authy时间同步问题

    Authy是我常用的软件之一,通常用于Google的两步验证,或者是其他基于Google两步验证的原理的衍生程序.比如Namesilo.印象笔记等均有使用. 先说说什么是两步验证. 两步验证 两步验证 ...

  9. 软删除脏数据job笔记

    某次处理一个case,发现线上库里有很多数据有问题.于是决定写一个job来将有问题的数据软删除掉.涉及到的两条SQL语句如下: <select id="loadTSKTVBillDai ...

  10. Hive_初步见解,安装部署与测试

    一.hive是什么东东 1. 个人理解 hive就是一个基于hdfs运行于MapReduce上的一个java项目, 这个项目封装了jdbc,根据hdfs编写了处理数据库的DDL/DML,自带的 二进制 ...