JavaScript探秘:SpiderMonkey的怪癖
大家都知道,命名函数表达式的标识符只在函数的局部作用域中有效。但包含这个标识符的局部作用域又是什么样子的吗?其实非常简单。在命名函数表达式被求值时,会创建一个特殊的对象,该对象的唯一目的就是保存一个属性,而这个属性的名字对应着函数标识符,属性的值对应着那个函数。这个对象会被注入到当前作用域链的前端。然后,被“扩展”的作用域链又被用于初始化函数。
在这里,有一点十分有意思,那就是ECMA-262定义这个(保存函数标识符的)“特殊”对象的方式。标准说“像调用new Object()表达式那样”创建这个对象。如果从字面上来理解这句话,那么这个对象就应该是全局Object的一个实例。然而,只有一个实现是按照标准字面上的要求这么做的,这个实现就是SpiderMonkey。因此,在SpiderMonkey中,扩展Object.prototype有可能会干扰函数的局部作用域:
Object.prototype.x = 'outer'; (function(){ var x = 'inner'; /* 函数foo的作用域链中有一个特殊的对象——用于保存函数的标识符。这个特殊的对象实际上就是{ foo: <function object> }。 当通过作用域链解析x时,首先解析的是foo的局部环境。如果没有找到x,则继续搜索作用域链中的下一个对象。下一个对象 就是保存函数标识符的那个对象——{ foo: <function object> },由于该对象继承自Object.prototype,所以在此可以找到x。 而这个x的值也就是Object.prototype.x的值(outer)。结果,外部函数的作用域(包含x = 'inner'的作用域)就不会被解析了。 */ (function foo(){ alert(x); // 提示框中显示:outer })(); })();
不过,更高版本的SpiderMonkey改变了上述行为,原因可能是认为那是一个安全漏洞。也就是说,“特殊”对象不再继承Object.prototype了。不过,如果你使用Firefox 3或者更低版本,还可以“重温”这种行为。
另一个把内部对象实现为全局Object对象的是黑莓(Blackberry)浏览器。目前,它的活动对象(Activation Object)仍然继承Object.prototype。可是,ECMA-262并没有说活动对象也要“像调用new Object()表达式那样”来创建(或者说像创建保存NFE标识符的对象一样创建)。 人家规范只说了活动对象是规范中的一种机制。
好,那我们下面就来看看黑莓浏览器的行为吧:
Object.prototype.x = 'outer'; (function(){ var x = 'inner'; (function(){ /* 在沿着作用域链解析x的过程中,首先会搜索局部函数的活动对象。当然,在该对象中找不到x。 可是,由于活动对象继承自Object.prototype,因此搜索x的下一个目标就是Object.prototype;而 Object.prototype中又确实有x的定义。结果,x的值就被解析为——outer。跟前面的例子差不多, 包含x = 'inner'的外部函数的作用域(活动对象)就不会被解析了。 */ alert(x); // 显示:outer })(); })();
不过神奇的还是,函数中的变量甚至会与已有的Object.prototype的成员发生冲突,来看看下面的代码:
(function(){ var constructor = function(){ return 1; }; (function(){ constructor(); // 求值结果是{}(即相当于调用了Object.prototype.constructor())而不是1 constructor === Object.prototype.constructor; // true toString === Object.prototype.toString; // true // …… })(); })();
要避免这个问题,要避免使用Object.prototype里的属性名称,如toString, valueOf, hasOwnProperty等等。
JScript解决方案
var fn = (function(){ // 声明要引用函数的变量 var f; // 有条件地创建命名函数 // 并将其引用赋值给f if (true) { f = function F(){ } } else if (false) { f = function F(){ } } else { f = function F(){ } } // 声明一个与函数名(标识符)对应的变量,并赋值为null // 这实际上是给相应标识符引用的函数对象作了一个标记, // 以便垃圾回收器知道可以回收它了 var F = null; // 返回根据条件定义的函数 return f; })();
最后我们给出一个应用上述技术的应用实例,这是一个跨浏览器的addEvent函数代码:
// 1) 使用独立的作用域包含声明 var addEvent = (function(){ var docEl = document.documentElement; // 2) 声明要引用函数的变量 var fn; if (docEl.addEventListener) { // 3) 有意给函数一个描述性的标识符 fn = function addEvent(element, eventName, callback) { element.addEventListener(eventName, callback, false); } } else if (docEl.attachEvent) { fn = function addEvent(element, eventName, callback) { element.attachEvent('on' + eventName, callback); } } else { fn = function addEvent(element, eventName, callback) { element['on' + eventName] = callback; } } // 4) 清除由JScript创建的addEvent函数 // 一定要保证在赋值前使用var关键字 // 除非函数顶部已经声明了addEvent var addEvent = null; // 5) 最后返回由fn引用的函数 return fn; })();

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务
