exports.Scope = class Scope
Scope 類別會在 CoffeeScript 中調整詞彙範圍。當您產生程式碼時,會建立一個範圍樹,其形狀與巢狀函式主體相同。每個範圍都知道在其內部宣告的變數,並具有對其父層封裝範圍的參考。這樣,我們就知道哪些變數是新的,需要用 var
宣告,以及哪些變數與外部範圍共用。
exports.Scope = class Scope
使用其父層初始化一個範圍,以便沿著鏈條查詢,以及對它所屬的 Block 節點的參考,這是它應宣告其變數的地方,對它所屬函式的參考,以及在原始碼中引用的變數清單,因此在產生變數時應避免使用。同時追蹤應作為變數宣告一部分輸出的註解。
constructor: (@parent, @expressions, @method, @referencedVars) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@comments = {}
@positions = {}
@utilities = {} unless @parent
@root
是給定檔案的頂層 Scope 物件。
@root = @parent?.root ? this
新增一個新變數或覆寫現有的變數。
add: (name, type, immediate) ->
return @parent.add name, type, immediate if @shared and not immediate
if Object::hasOwnProperty.call @positions, name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.push({name, type}) - 1
當呼叫 super
時,我們需要找出目前所在方法的名稱,這樣我們才知道如何呼叫父類別的相同方法。如果從內部函式呼叫 super,這可能會變得複雜。namedMethod
會向上遍歷範圍樹,直到找到第一個已填入名稱的函式物件,或到底為止。
namedMethod: ->
return @method if @method?.name or !@parent
@parent.namedMethod()
在詞法範圍內查詢變數名稱,如果尚未存在,則宣告變數。
find: (name, type = 'var') ->
return yes if @check name
@add name, type
no
保留變數名稱,作為此範圍的函式參數來源。內部參照不需要 var
。
parameter: (name) ->
return if @shared and @parent.check name, yes
@add name, 'param'
僅檢查變數是否已宣告,而不會保留,會一直往上到根範圍。
check: (name) ->
!!(@type(name) or @parent?.check(name))
在給定的索引處產生暫時變數名稱。
temporary: (name, index, single=false) ->
if single
startCode = name.charCodeAt(0)
endCode = 'z'.charCodeAt(0)
diff = endCode - startCode
newCode = startCode + index % (diff + 1)
letter = String.fromCharCode(newCode)
num = index // (diff + 1)
"#{letter}#{num or ''}"
else
"#{name}#{index or ''}"
取得變數的類型。
type: (name) ->
return v.type for v in @variables when v.name is name
null
如果我們需要儲存中間結果,請為編譯器產生的變數尋找可用的名稱。_var
、_var2
,依此類推…
freeVariable: (name, options={}) ->
index = 0
loop
temp = @temporary name, index, options.single
break unless @check(temp) or temp in @root.referencedVars
index++
@add temp, 'var', yes if options.reserve ? true
temp
確保在此範圍的頂端(或在頂層範圍,如果要求的話)進行指定。
assign: (name, value) ->
@add name, {value, assigned: yes}, yes
@hasAssignments = yes
此範圍是否有任何已宣告的變數?
hasDeclarations: ->
!!@declaredVariables().length
傳回在此範圍內首次宣告的變數清單。
declaredVariables: ->
(v.name for v in @variables when v.type is 'var').sort()
傳回應在此範圍頂端進行的指定清單。
assignedVariables: ->
"#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned