既然这种函数对象也有自己的特色,我们将它与FD和FE区分开来。其主要特点在于这种函数的[[Scope]]属性仅包含全局对象:
var x = 10; function foo() { var x = 20; var y = 30; var bar = new Function('alert(x); alert(y);'); bar(); // 10, "y" 未定义 }
我们看到,函数bar的[[Scope]]属性不包含foo上下文的Ao——变量”y”不能访问,变量”x”从全局对象中取得。顺便提醒一句,Function构造器既可使用new 关键字,也可以没有,这样说来,这些变体是等价的。
这些函数的其他特点与Equated Grammar Productions 和Joined Objects相关。作为优化建议(但是,实现上可以不使用优化),规范提供了这些机制。如,如果我们有一个100个元素的数组,在函数的一个循环中,执行可能使用Joined Objects 机制。结果是数组中的所有元素仅一个函数对象可以使用。
var a = []; for (var k = 0; k < 100; k++) { a[k] = function () {}; // 可能使用了joined objects }
但是通过函数构造器创建的函数不会被连接。
var a = []; for (var k = 0; k < 100; k++) { a[k] = Function(''); // 一直是100个不同的函数 }
另外一个与联合对象(joined objects)相关的例子:
function foo() { function bar(z) { return z * z; } return bar; } var x = foo(); var y = foo();
这里的实现,也有权利连接对象x和对象y(使用同一个对象),因为函数(包括它们的内部[[Scope]] 属性)在根本上是没有区别的。因此,通过函数构造器创建的函数总是需要更多的内存资源。
创建函数的算法
下面的伪码描述了函数创建的算法(与联合对象相关的步骤除外)。这些描述有助于你理解ECMAScript中函数对象的更多细节。这种算法适合所有的函数类型。
F = new NativeObject(); // 属性[[Class]]是"Function" F.[[Class]] = "Function" // 函数对象的原型是Function的原型 F.[[Prototype]] = Function.prototype // 医用到函数自身 // 调用表达式F的时候激活[[Call]] // 并且创建新的执行上下文 F.[[Call]] = < reference to function> // 在对象的普通构造器里编译 // [[Construct]] 通过new关键字激活 // 并且给新对象分配内存 // 然后调用F.[[Call]]初始化作为this传递的新创建的对象 F.[[Construct]] = internalConstructor // 当前执行上下文的作用域链 // 例如,创建F的上下文 F.[[Scope]] = activeContext.Scope // 如果函数通过new Function(...)来创建, // 那么 F.[[Scope]] = globalContext.Scope // 传入参数的个数 F.length = countParameters // F对象创建的原型 __objectPrototype = new Object(); __objectPrototype.constructor = F // {DontEnum}, 在循环里不可枚举x F.prototype = __objectPrototype return F
注意,F.[[Prototype]]是函数(构造器)的一个原型,F.prototype是通过这个函数创建的对象的原型(因为术语常常混乱,一些文章中F.prototype被称之为“构造器的原型”,这是不正确的)。
延伸阅读
此文章所在专题列表如下:
- 我们应该如何去了解JavaScript引擎的工作原理
- JavaScript探秘:编写可维护的代码的重要性
- JavaScript探秘:谨慎使用全局变量
- JavaScript探秘:var预解析与副作用
- JavaScript探秘:for循环(for Loops)
- JavaScript探秘:for-in循环(for-in Loops)
- JavaScript探秘:Prototypes强大过头了
- JavaScript探秘:eval()是“魔鬼”
- JavaScript探秘:用parseInt()进行数值转换
- JavaScript探秘:基本编码规范
- JavaScript探秘:函数声明与函数表达式
- JavaScript探秘:命名函数表达式
- JavaScript探秘:调试器中的函数名
- JavaScript探秘:JScript的Bug
- JavaScript探秘:JScript的内存管理
- JavaScript探秘:SpiderMonkey的怪癖
- JavaScript探秘:命名函数表达式替代方案
- JavaScript探秘:对象Object
- JavaScript探秘:原型链 Prototype chain
- JavaScript探秘:构造函数 Constructor
- JavaScript探秘:可执行的上下文堆栈
- 执行上下文其一:变量对象与活动对象
- 执行上下文其二:作用域链 Scope Chains
- 执行上下文其三:闭包 Closures
- 执行上下文其四:This指针
- JavaScript探秘:强大的原型和原型链
- JavaScript函数其一:函数声明
- JavaScript函数其二:函数表达式
- JavaScript函数其三:分组中的函数表达式
- JavaScript函数其四:函数构造器
- JavaScript变量对象其一:VO的声明
- JavaScript变量对象其二:VO在不同的执行上下文中
- JavaScript变量对象其三:执行上下文的两个阶段
- JavaScript变量对象其四:关于变量
- JavaScript变量对象其五:__parent__ 属性
- JavaScript作用域链其一:作用域链定义
- JavaScript作用域链其二:函数的生命周期
- JavaScript作用域链其三:作用域链特征
- JavaScript闭包其一:闭包概论
- JavaScript闭包其二:闭包的实现
- JavaScript闭包其三:闭包的用法
本文地址:http://www.nowamagic.net/librarys/veda/detail/1665,欢迎访问原出处。
大家都在看