许多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. session和cookie的前后的操作

    1. // sign outexports.signout = function (req, res, next) { req.session.destroy(); res.clearCookie(c ...

  2. Socket网络编程--FTP客户端(1)(Windows)

    已经好久没有写过博客进行分享了.具体原因,在以后说. 这几天在了解FTP协议,准备任务是写一个FTP客户端程序.直接上干货了. 0.了解FTP作用 就是一个提供一个文件的共享协议. 1.了解FTP协议 ...

  3. am,pm时间转换

    写在前面 最近遇到的一个问题,在英文操作系统上,获取到的时间是带am或者pm的.但是数据库是datetime类型,存储的时候竟然都变成0000-00-00 00:00:00.但是在中文操作系统上又是正 ...

  4. WCF 入门(29)

    前言 最近工作比较忙,加了会班就不想再写东西了,就想洗洗睡. 但是这个视频真的不能断,不能像过去一样写了几集就停了. 现在公司在做一个MVC框架的项目,话说已经一年没有写MVC了,重新上手的感觉还可以 ...

  5. Unknown column '' in 'field list'解决方案

    很多人在用php+MySQL做网站往数据库插入数据时发现如下错误: 注册失败!Unknown column '1a' in 'field list' 结果发现用数字提交是没有问题的,其他如char型就 ...

  6. 每天一个linux命令(54):sftp命令

    sftp 是一个交互式文件传输程式.它类似于 ftp, 但它进行加密传输,比FTP有更高的安全性.下边就简单介绍一下如何远程连接主机,进行文件的上传和下载,以及一些相关操作. 举例,如远程主机的 IP ...

  7. The big deffrence between ($(du * )) and $(du *)

    Infolist=($(du *))echo "Get the list one $Infolist"This has formed a array after quating t ...

  8. 【CodeForces 625C】K-special Tables

    题意 把1到n*n填在n*n的格子里.要求每一行都是递增的,使第k列的和最大. 分析 第k列前的格子1 2 .. 按要求填到满格,然后第k列及后面的格子,都从左到右填递增1的数. 第k列的和再加起来, ...

  9. Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError

    SLF4J: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackO ...

  10. POJ2186 Popular Cows

    Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= ...