2009-11-27 39 views
3

給了JavaScript下面的代碼片斷在一個範圍內的互動x'是確定的,因爲當解決x是環境走向範圍鏈時。使用JavaScript作用域鏈

你甚至可以做的「重新編譯」的水平與EVAL在運行時注入了新的領域。例如:

var x = 10; 

function sayx() { 
    alert(x); 
} 

function wrap(f) { 
    return eval('(function() { var x = 20;(' + f + ')(); })'); 
} 

wrap(sayx)(); 

這工作,因爲該函數將呼籲其toString函數將返回「原創'來源..因此,我們基本上創建了一個函數的包裝版本,它有一個覆蓋x的新範圍。結果將是一個打印'20'而不是'10'的警告框。

然而,表面上看起來可以工作,但範圍鏈被打破,鏈中的下一個項目不再是相同的,因爲函數'f'現在定義在不同的位置......更糟的是它繼承的範圍鏈可能包含調用函數不應該訪問的許多引用。

那麼,是否有一個更支持,可行的方式來注入一個範圍項目?是這樣的:

function withScope(f, scope) { ??? } 

--- 

var x = 10, y = 10; 

function dothemath() { 
    alert(x + y); 
} 

var haxthemath = withScope(dothemath, { x: 9000 }); 
haxthemath(); // 9010 not 20 

我猜的答案是「不」,有些人可能會存在這樣的範圍內注射的「安全」問題,但考慮到你可以做的伎倆呢(儘管嚴重損壞)我不不認爲這是..

這樣做的好處是,你可以基本上僞造自己的僞變量。

在此先感謝。


編輯,只是澄清一點,想象我想「withScope」那有以下作用域鏈功能:

Window - window properties 
Object - { var x = 10 } 
Object - { var y = 5 + x } 

我希望能夠得到一個功能回到那有效地具有相同的鏈+一個範圍我公司提供..即:

withScope(somefunc, { foo: 'bar' }) 

能給我

Window - window properties 
Object - { var x = 10 } 
Object - { var y = 5 + x } 
Ext scope - { foo = 'bar' } 

所有以前的常規變量都會被找到,因爲我的擴展範圍沒有提及任何關於它們的內容。

回答

4

如果您按範圍引用函數和/或閉包中的局部變量,我認爲答案是否定的。

您可以使用functionName.call(scope,arg1,...)或functionName.apply(scope,[arg1,...])更改this關鍵字的作用域;這可以與原型一起用來創建類似於您所描述的鏈 - 如果在對象自己的屬性中找不到某種東西,則會在其原型中查找它。如果該屬性不存在,則鏈中的下一個原型將被使用,依此類推。

+1

是的,這是關於封閉上下文與調用上下文。 – meandmycode 2009-11-27 23:31:38

+0

謝謝Jani,我會回答你的回答「不」,我認爲沒有辦法,但我當然認爲這是值得扔在那裏,以防有任何有趣的'黑客'存在.. – meandmycode 2009-12-09 13:43:30

0

答案我認爲您正在尋找的是內置的「with」聲明。

不過,我不會reccomend使用它,因爲它已被棄用,並且將很可能不會在ECMAScript中存在6

唯一的其他方式,我認爲你可以做這樣的事情是從主機內部應用程序本身,從外部操縱JavaScript環境。或者,如果您使用的是rhino,實際上您可以在javascript中執行該操作,因爲Java apis和Javascript之間的鏈接在犀牛中是無縫的。當史蒂夫葉戈第一次指出這件事情時,我發現了它。從JavaScript之外操作JavaScript,但是從JavaScript內部操作!這是天才!

如果你被困在瀏覽器環境中,也許你可以使用JavaScript編寫的JavaScript解釋器,比如水仙。

+0

我不認爲with語句讓你以我想要的方式推斷範圍,但是這可能是超出了javascript範圍的東西..它只是令人沮喪,你可以完全吹走閉合範圍和提供你自己的(導致誰知道什麼是混亂),但是你不能僅僅在現有的閉包之上添加一點點。合法的情況就像添加你自己的僞變量一樣非常有用。 – meandmycode 2009-11-27 23:34:38

+0

with語句接受一個對象,並將其直接插入到現有作用域鏈的頭部,作爲作用域,保留其餘的作用域。他們已經放棄了,因爲不得不支持,因此很難優化JavaScript解釋器。 – Breton 2009-11-28 01:09:40

+0

是的,但是它將範圍插入到它定義的範圍的頭部,而我正在創建一個具有新範圍的函數的副本。 – meandmycode 2009-11-28 13:51:03

0

也許使用javascript的內置toString方法來實現函數?

function withScope(f, scope) { 
    var toExec = ""; 
    for (var prop in scope) { 
     toExec += "var " + prop + " = scope['" + prop + "'];" 
    } 
    toExec += "f = (" + f.toString() + ")"; 
    eval(toExec); 
    return f; 
} 
var x = 10; 
var f = function() { 
    console.log(x); 
}; 
withScope(f, {x: 20})(); 

這似乎是一個壞主意,但...

+0

是的,第二個代碼snip可以做到這一點(對於演示而言更簡單),它的作用在於最接近的作用域按預期工作,但作用域鏈現在完全不同,並且不會從原始作用域連鎖 – meandmycode 2009-11-27 23:21:20

+0

對不起,我沒有仔細閱讀問題... – Caleb 2009-11-27 23:31:59