js作用域链和函数表达式的深入讨论

l676331991 2014-03-18 12:27:34

var test="wrong"
Object.prototype.test="right";

var score=0;

void function f0(){
if(test=="right")score++;
}()

void function(){
if(test=="wrong")score++;
}()

var e="right";
try{
throw new Error;
} catch(e) {
e="wrong";
if(test=="right")score++;
}
if(e=="right")score++;

var f;
void function f(){
}
if(typeof f=="undefined")score++;


var err;
function a(){
var m="right";
var n;
return function(){
var err;
return arguments[0].call(this,arguments[1]);
}
}
try{
var b=a();
if(b(eval,"m")=="right")score++;
} catch(err) {
}


alert(score+"/6")


偶得这样一段代码,出处称符合ES3标准的应该结果是6/6。
与大家共同讨论一下,涉及到js作用域链的深入理解。

//另,发帖时添加自定义标签敲回车为毛是提交表单~~误发两次了
...全文
262 16 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
不理了,霸王硬上弓。
	function MyConsole(obj){
		var top={};
		for(var i in Object.prototype){
			top[i]=Object.prototype[i];
		}
		var low = {};
		for(var i in obj){
			low[i]=obj[i];
		}
		function merge(o,d){			
			for(var i in d){
				o[i]=d[i];
			}			
		}
		merge(low,top);
		
	    this.global = low;
	}
	MyConsole.prototype.log = function(key){
	    alert(this.global[key]);
	} 
	function myWindow(){
	    this.test = 'wrong';
	    this.console = new MyConsole(this);
	}
	win = new myWindow();
	win.console.log('test');
	 
	Object.prototype.test = "right";
	 
	var console = new MyConsole(win);
	console.log('test');
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
越说越乱。 不过我注意到了一点。
test = 'gss';


///放断点. 在console中看.

Object.prototype.test = "right";

///放断点. 在console中看.
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
该过来
function MyConsole(obj){
	this.global = obj;
}
MyConsole.prototype.log = function(key){
	alert(this.global[key]);
} 
function myWindow(){
	
	this.console = new MyConsole(this);
}
win = new myWindow();
win.test = 'wrong';
win.console.log('test');

Object.prototype.test = "right";

 
var console = new MyConsole(new myWindow());
console.log('test');
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
!!!,原来是这样的,上面的有一些观点是错的,不理了
function MyConsole(obj){
	this.global = obj;
}
MyConsole.prototype.log = function(key){
	alert(this.global[key]);
} 
function myWindow(){
	this.test = 'wrong';
	this.console = new MyConsole(this);
}
myWindow = new myWindow();
myWindow.console.log('test');

Object.prototype.test = "right";

var console = new MyConsole({});
console.log('test');
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
哦是存在。 你这样看看。
test = 'gss';
Object.prototype.test = "right";
var $c = window.console.log(test);
l676331991 2014-03-18
  • 打赏
  • 举报
回复
引用 9 楼 u011461314 的回复:
你这Object.prototype.test = "right";在他生成后才设值的。

貌似不对的。

你一直在申明window是在设置原型的test属性之前就创建的,这能说明什么呢?

var $A1 = new Object();
var $t1 = $A1.test; //在设置原型的test值之前就取值,那自然
Object.prototype.test = "right";
var $B1 = new Object();
var $t2 = $B1.test;
var $t11 = $A1.test; //敢问这里不是能取到right么?


window.console明明是存在的。console是怎么实现的我已经在上文提出来了,我是来探讨的,交流的,不是来提问的。

chrome的console的实现是这样:
eval("with(commandLineAPI){"+code+"}"); //一个eval套一个with块
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
1window 在页面开始时 通过 window = new Window() 生成了。 你这Object.prototype.test = "right";在他生成后才设值的。 2window.console 这方法不存在, 所以console 不属于window. 3.搞不好 console是这样写的
window.test = 'wrong';
		Object.prototype.test = "right";
		function wrapConsole(){
			var global={};
			for(var i in Object.prototype){
				global[i] = Object.prototype[i];
			}			
			return function (key){
				 
				// test  在 说不好是Object函数 的一个closure层里面的值;
				//  先找Object 中的 test,找不到window中的test.
				alert(global[key] || window[key]);
			}
		}		
		myConsole = wrapConsole();
		myConsole('test');
l676331991 2014-03-18
  • 打赏
  • 举报
回复

Object.prototype.test = "right";
console.log(test); //right
test = "wrong";
console.log(test); //怎么还是right?

这个例子是我在chrome的console下手动一行一行敲的,和一起复制粘贴不是一回事,每一次敲回车是一次执行,而每次执行是在不同的with块中,因此就会出现这个怪异的问题。with块的临时作用域在上文有解释,即with(commandLineAPI){//code }。
l676331991 2014-03-18
  • 打赏
  • 举报
回复
引用 6 楼 u011461314 的回复:
window 的实例化,比Object.prototype.test = "right";早。
var $A1 = new Object();
		var $t1 = $A1.test;		
		Object.prototype.test = "right";
		var $B1 = new Object();
		var $t2 = $B1.test;
我的例子里是直接用变量test,不是对象的属性。且看这个test为什么修改不了。

Object.prototype.test = "right";
console.log(test); //right
test = "wrong";
console.log(test); //怎么还是right?
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
window 的实例化,比Object.prototype.test = "right";早。
var $A1 = new Object();
		var $t1 = $A1.test;		
		Object.prototype.test = "right";
		var $B1 = new Object();
		var $t2 = $B1.test;
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
window.console 不存在,所以console不属于window. 至于chrome debug中console 跟window 是什么关系,为什么会读到window 上的属性与方法。
l676331991 2014-03-18
  • 打赏
  • 举报
回复
引用 2 楼 u011461314 的回复:
是想说 window.test = "wrong" 这个观点吗?
修正一下,问题不是这么重现的,应该是这样:

Object.prototype.test = "right";
console.log(test); //right
test = "wrong";
console.log(test); //怎么还是right?
l676331991 2014-03-18
  • 打赏
  • 举报
回复
引用 2 楼 u011461314 的回复:
是想说 window.test = "wrong" 这个观点吗?
不是的,不是这么简单。你在chrome的开发工具的console下粘贴以下代码,就知道了:

//先粘贴这一段
var test = "wrong"; 
Object.prototype.test = "right"
console.log(test); 

 //wrong对不对?

//接着第二次再粘贴这段代码
console.log(test);

//right了?灵异了?
zhjdg 2014-03-18
  • 打赏
  • 举报
回复
是想说 window.test = "wrong" 这个观点吗?
l676331991 2014-03-18
  • 打赏
  • 举报
回复
我觉得按ES5标准来解析,正确值为3/6 ======================================================

void function f0(){
    if(test=="right")score++;
}()
 
void function(){
    if(test=="wrong")score++;
}()
函数表达式分两种,常用的匿名函数表达式(Anonymous Function Expression AFE)和命名函数表达式(Named Function Expression NFE)。他们的共同点是,都是函数表达式,和函数声明有本质区别。唯一不同的是,命名函数表达式内部可以通过函数名来访问函数自身(arguments.callee被废除),而这个函数名却不对外渗透。参见js版置顶帖【你自认为理解javascript?】第二题。ES对这个特性的规定是使用类似"new Object()"的形式创建一个特殊对象,这个对象只有一个属性,命名函数表达式的名字,指向这个函数的引用,并将这个特殊对象压入匿名函数表达式的作用域链顶端。这就解释了本例中,命名函数表达式查找test得到继承自Object的("right"),而匿名函数表达式查找test得到的是window下的("wrong")。然而新版本已经更正了此问题,特殊对象已经不再继承自Object,因此,本例中匿名函数和命名函数内部的test都应该是window.test。 测试chrome版本 33.0.1750.149 ,发现情况根本不是这样。分析得,从开发者工具F12粘贴入代码,代码是通过eval运行的,并且,使用了with(commandLineAPI){ // your code} 进行包裹。这个commandLineAPI对象提供了命令行可以直接访问的诸多API,而它是继承自Object的,因此代码从作用域链顶端的withBlock Scope找到test值。新建html粘贴入代码则此二例子test值皆为"wrong",表现正常。 ======================================================

var e="right";
try{
    throw new Error;
} catch(e) {
    e="wrong";
    if(test=="right")score++;
}
if(e=="right")score++;
catch块中的代码执行的时候,作用域链顶端会插入catchBlock Scope,这个异常e就是在这里声明的。按出题者所述,这个catchBlock Scope也是控制作用域的特殊对象,因此test值在这个特殊对象中找到,值为“right”。然而现在的浏览器对控制作用域的这种内部特殊对象都做了修正,他们不再继承自Object(同命名函数表达式)。 至于变量e,catch内部的e来自catchBlock scope,因此catch内部修改e对外部没有影响。这个解释通了。 相同的还有finally块。 ======================================================

var f;
void function f(){
}
if(typeof f=="undefined")score++;
这个很好解释,命名函数表达式,f值只对函数表达式内部有效,不会渗透到外部作用域。因此f还是undefined。chrome解析正确。 ======================================================

var err;
function a(){
    var m="right";
    var n;
    return function(){
        var err;
        return arguments[0].call(this,arguments[1]);
    }
}
try{
var b=a();
if(b(eval,"m")=="right")score++;
} catch(err) {
}
这个涉及到js引擎对eval的处理。按题意,m值应该可以正确获取,但是现代浏览器对eval的处理更多的想一个保留字,eval使用不当会影响js的优化。因此现在的处理方式是,如果直接使用eval,则eval会引用当前EC的scope chain,如果间接使用eval,则scope chain中只有global。因此这个m应该是获取不到的。换成这样便可:

var err;
function a(){
    var m="right";
    var n;
    return function(){
        var err;
        return eval(arguments[0]);
    }
}
try{
var b=a();
if(b("m")=="right")score++;
} catch(err) {
}

87,997

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧