Effective JavaScript Item 29 避免使用非规范的Stack Inspection属性

本系列作为Effective JavaScript的读书笔记。

由于历史原因,很多JavaScript执行环境中都提供了某些方式来查看函数调用栈。在一些环境中,arguments对象(关于该对象可以查看Item
22,23,24)上有两个额外的属性:

arguments.callee -
它引用了正在被调用的函数

arguments.caller -
它引用了调用当前函数的函数

关于arguments.callee的使用,可以参考下面的代码:

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

可见,在递归函数中,可以使用callee来得到当前正在被调用的函数。

但是,使用函数声明的方式也可以很方便的实现函数的递归调用,并且这种方式更加清晰:

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

而对于arguments.caller,它提供的功能就更加强大了,能保存了调用当前函数的函数的一个引用。因为它有安全隐患,所以很多JavaScript运行环境都将这个属性移除了。同时,有部分运行环境在函数对象上提供了一个caller属性来达到和arguments.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();
trace; // [f1, f2]

但是当一个函数在调用栈中出现不止一次时,就会发生问题了,比如下面的代码会产生一个死循环:

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

原因在于,当发生递归调用时,函数自身会被赋值给它的caller属性。因此getCallStack中的for循环的终止条件f永远不会为false:

for (var f = getCallStack.caller; f; f = f.caller) {
	stack.push(f);
}

正因为这种不稳定性和由此带来的安全性问题,在ES5的strict
mode中,使用caller或者callee属性都是被禁止的:

function f() {
	"use strict";
	return f.caller;
}
f(); // error: caller may not be accessed on strict functions

总结:

  1. 避免使用arguments对象上的callee和caller属性。
  2. 避免使用function对象上的caller属性。

时间: 2024-08-25 03:06:46

Effective JavaScript Item 29 避免使用非规范的Stack Inspection属性的相关文章

Effective JavaScript Item 39 绝不要重用父类型中的属性名

本系列作为Effective JavaScript的读书笔记. 如果需要向Item 38中的Actor对象添加一个ID信息: function Actor(scene, x, y) { this.scene = scene; this.x = x; this.y = y; this.id = ++Actor.nextID; scene.register(this); } Actor.nextID = 0; 同时,也需要向Actor的子类型Alien中添加ID信息: function Alien(

Effective C++ Item 29 为”异常安全”而努力是值得的

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏.这样的函数区分为三种 可能的保证: 基本型-->发生异常,程序处于某个合法状态 强烈型-->发生异常,程序处于原先状态 不抛异常型-->承诺绝不抛出殿堂 示例: class PrettyMenu{ public: //... void changeBackground(std::istream &imgSr

Effective JavaScript Item 23 永远不要修改arguments对象

本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:arguments.shift(),arguments.push()是错误的. 在Item 20和Item 21中,知道了函数对象上存在call和apply方法,那么是不是可以利用它们来让arguments也能够利用数组的方法呢: function callMethod(obj, method) { var shi

Effective JavaScript Item 28 不要依赖函数的toString方法

本系列作为Effective JavaScript的读书笔记. 在JavaScript中,函数对象上存在一个toString方法,它能够方便地将函数的源代码转换返回成一个字符串对象. (function(x) { return x + 1; }).toString(); // "function (x) {\n return x + 1;\n}" toString方法不仅仅会让一些黑客找到攻击的方法,而且该方法也存在严重的限制. 首先,toString方法的实现方式并没有被ECMASc

Effective JavaScript Item 27 使用闭包而不是字符串来封装代码

本系列作为Effective JavaScript的读书笔记. 对于代码封装,在JavaScript中有两种方式可以办到.第一种就是使用function,第二种则是利用eval()函数,传入到该函数的字符串参数可以是一段代码. 当对使用哪种方式犹豫不决时,使用function.因为使用字符串的一个重要缺点是,传入的字符串并不是一个闭包,而function则可以代表一个闭包.关于闭包的特点,在Item 11中进行了描述. 下面是一段使用字符串来封装代码的例子: function repeat(n,

Effective JavaScript Item 31 优先使用Object.getPrototypeOf,而不是__proto__

本系列作为Effective JavaScript的读书笔记. 在ES5中引入了Object.getPrototypeOf作为获取对象原型对象的标准API.可是在非常多运行环境中.也提供了一个特殊的__proto__属性来达到相同的目的. 由于并非全部的环境都提供了这个__proto__属性,且每一个环境的实现方式各不同样,因此一些结果可能不一致: // 在某些环境中 var empty = Object.create(null); // object with no prototype "__

Effective JavaScript Item 38 调用父类的构造函数在子类的构造函数

作为这一系列Effective JavaScript的读书笔记. 在一个游戏或者图形模拟的应用中.都会有场景(Scene)这一概念.在一个场景中会包括一个对象集合,这些对象被称为角色(Actor). 而每一个角色依据其类型会有一个图像用来表示,同一时候场景也须要保存一个底层图形展示对象的引用,被称为上下文(Context): function Scene(context, width, height, images) { this.context = context; this.width =

Effective JavaScript Item 38 在子类构造函数中调用父类构造函数

本系列作为Effective JavaScript的读书笔记. 在一个游戏或者图形模拟的应用中,都会有场景(Scene)这一概念.在一个场景中会包含一个对象集合,这些对象被称为角色(Actor).而每个角色根据其类型会有一个图像用来表示,同时场景也需要保存一个底层图形展示对象的引用,被称为上下文(Context): function Scene(context, width, height, images) { this.context = context; this.width = width

Effective JavaScript Item 46 优先使用数组而不是Object类型来表示有顺序的集合

本系列作为Effective JavaScript的读书笔记. ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 但是在使用for..in循环对Object中的属性进行遍历的时候,确实是需要依赖于某种顺序的.正因为ECMAScript没有对这个顺序进行明确地规范,所以每个JavaScript执行引擎都能够根据自身的特点进行实现,那么在不同的执行环境中就不能保证for..in循环的行为一致性了. 比如,以下代码在调用report方法时的结果就是不确定的