javascript
JS的数据访问及优化访问速度
JavaScript中有四種基本的數(shù)據(jù)存取位置:
- ? 直接量
直接量只代表自身,不存儲在特定位置。JS中的直接量有:字符串,數(shù)字,布爾值,對象,數(shù)組,函數(shù),正則表達(dá)式,以及特殊的null和undefined值。
- ? 變量
用var定義的數(shù)據(jù)存儲單元
- 數(shù)組元素
存儲在JS數(shù)組對象內(nèi)部,以數(shù)字作為索引
- ? 數(shù)據(jù)成員
存儲在JS對象內(nèi)部,以字符串為索引
作用域工作原理和標(biāo)識符解析
關(guān)鍵字:內(nèi)部屬性[[Scope]]、可變對象、運行期上下文、執(zhí)行環(huán)境、活動對象
1. 作用域工作原理
?
作用域鏈的作用:存儲執(zhí)行環(huán)境可以訪問的數(shù)據(jù),使用方法是標(biāo)識符解析
每一個JS函數(shù)都表示為一個對象,更確切的說,是Function對象的一個實例。Function對象同其他對象一樣,擁有可以編程訪問的屬性,和一系列不能通過代碼訪問而僅供JS引擎存取的內(nèi)部屬性。內(nèi)部屬性[[Scope]]指向一個函數(shù)被創(chuàng)建的作用域中對象的集合。這個集合被稱為函數(shù)的作用域鏈,它決定哪些數(shù)據(jù)可以被函數(shù)訪問。函數(shù)作用域中的每個對象被稱為可變對象,每個可變對象都以“鍵值對”的形式存在。當(dāng)一個函數(shù)被創(chuàng)建后,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象所填充。思考下面的全局函數(shù)
function add(num1, num2) {????????????
???????? var sum = num1 + num2;
???????? return sum;
}
當(dāng)函數(shù)add()創(chuàng)建時,它的作用域鏈填入了一個單獨的可變對象,包含了函數(shù)能訪問到的所有數(shù)據(jù)。本可變對象是時刻存在的全局對象。
函數(shù)add的作用域?qū)趫?zhí)行時用到。
var total = add(1, 2);
???????? 執(zhí)行此函數(shù)時會創(chuàng)建一個稱為”運行期上下文”的內(nèi)部對象。一個運行期上下文定義了一個函數(shù)的執(zhí)行環(huán)境。函數(shù)每次執(zhí)行時對應(yīng)的運行期上下文都是獨一無二的,所以多次調(diào)用同一函數(shù)就會導(dǎo)致創(chuàng)建多個運行期上下文。當(dāng)函數(shù)執(zhí)行完畢,執(zhí)行期上下文會被銷毀。
???????? 每個運行期上下文都有自己的作用域鏈,用于標(biāo)識符解析。當(dāng)它被創(chuàng)建時,它的作用域鏈被初始化為當(dāng)前運行函數(shù)的[[Scope]]屬性所指向的對象。這些值按照它們出現(xiàn)在函數(shù)中的順序,被復(fù)制到運行期上下文的作用域鏈中。這個過程一旦完成,一個被稱為”活動對象”的新對象就為運行期上下文創(chuàng)建好了。活動對象作為函數(shù)運行期中的可變對象,包含了所有局部變量、命名參數(shù)、參數(shù)集合arguments以及this.然后此對象被推入到作用域鏈的前端。當(dāng)運行期上下文被銷毀,活動對象也隨之銷毀。
2. 標(biāo)識符解析過程
???????? 在函數(shù)執(zhí)行過程中,每遇到一個變量,都會經(jīng)歷一次標(biāo)識符解析過程以決定從哪里獲取或存儲數(shù)據(jù)。該過程搜索運行期上下文的作用域鏈,查找同名的標(biāo)識符。搜索過程從作用域鏈頭部開始,也就是當(dāng)前運行函數(shù)的活動對象。如果找到了,就使用這個標(biāo)識符對應(yīng)的變量;如果沒找到,繼續(xù)搜索作用域鏈中的下一個對象。搜索過程會持續(xù)進(jìn)行,直到標(biāo)識符被找到,或者沒有可用于搜索的對象為止,這種情況下標(biāo)識符被認(rèn)為是未定義的。函數(shù)執(zhí)行過程中,每個標(biāo)識符都要經(jīng)歷這樣的搜索過程。
???????? 對于所有瀏覽器而言,總的趨勢是,一個標(biāo)識符所在的位置越深(作用域鏈末端),它的讀寫速度也就越慢。一個好的經(jīng)驗法則是:如果某個跨作用域值在函數(shù)中被引用一次以上,那么就把它存儲在局部變量里。
閉包,作用域和內(nèi)存
官方解釋為: 所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個函數(shù)),因而這些變量也是該表達(dá)式的一部分。
我認(rèn)為閉包就是能夠讀/寫函數(shù)內(nèi)部的某些變量的子函數(shù),并將這些變量保存在內(nèi)存中.
function assignEvents() {
???????? var id = ‘index’;
???????? document.getElementById(“save-btn”).onclick = function(event) {
???????? saveDocument(id);
? ? };
}
函數(shù)給一個DOM元素設(shè)置事件處理器。這個事件處理器就是一個閉包,它在函數(shù)執(zhí)行時被創(chuàng)建,并且能訪問所屬作用域的id變量。為了讓這個閉包訪問id變量,必須創(chuàng)建一個特別的作用域鏈。
???????? 當(dāng)assignEvents()函數(shù)被執(zhí)行時,一個包含變量id的以及其他一些數(shù)據(jù)的活動對象被創(chuàng)建。它成為運行期上下文作用域鏈中第一個對象,而全局對象緊隨其后。當(dāng)閉包被創(chuàng)建時,它的[[Scope]]屬性被初始化為這些對象。(如圖)
因為閉包的[[Scope]]屬性包含了與運行期上下文作用域相同的對象的引用,因此會有一些副作用。
通常的活動對象會隨同運行期上下文一同銷毀。但引入閉包時,由于引用仍然存在閉包的[[Scope]]屬性中,因此活動對象無法被銷毀。這意味著閉包的存在,需要更多的內(nèi)存開銷。
???????? 當(dāng)閉包被執(zhí)行時,一個運行期上下文被創(chuàng)建,它的作用域鏈與屬性[[Scope]]中引用的兩個相同的作用域鏈對象同時被初始化,然后一個活動對象為閉包自身所創(chuàng)建。
???????? 注意在閉包中用到的兩個標(biāo)識符,id和saveDocument,它們存在作用域鏈第一個對象之后的位置。這就是使用閉包最主要的性能關(guān)注點:要經(jīng)常訪問大量跨作用域的標(biāo)識符,每次訪問都會導(dǎo)致性能損失。
閉包的應(yīng)用環(huán)境
1. 保護函數(shù)內(nèi)的變量安全
2. 在內(nèi)存中維持一個變量
????????
對象成員
???????? 訪問對象成員的速度比訪問直接量或變量更慢,在某些瀏覽器中比訪問數(shù)組元素還要慢。
???????? JS中的對象是基于原型的。原型是其他對象的基礎(chǔ),定義并實現(xiàn)了一個新對象必須包含的成員列表。這一概念完全不同于傳統(tǒng)面向?qū)ο缶幊陶Z言的‘類’的概念,‘類’定義了創(chuàng)建新對象的過程。而原型對象為所有對象實例所共享,因此這些實例也共享了原型對象的成員。
???????? 實例通過一個內(nèi)部屬性綁定它的原型__proto__,比如一旦你創(chuàng)建一個內(nèi)置對象(Object、Array)的實例,它們就會自動擁有一個Object實例作為原型。
???????? 實例可以有兩種成員類型:
實例成員(也稱‘own’成員):存在對象實例中
原型成員:由對象原型繼承而來。
var Book = {
???????? title: “JavaScript”,
???????? content: ‘JavaScript是一門博大精深的語言’
};
alert(BooK.toString());????????????? //[object Object]
???????? 方法toString()是對象Book繼承而來的原型成員。解析對象成員的過程同解析變量十分相似。當(dāng)Book.toString()
被調(diào)用時,會從對象實例開始,搜索名為’toString’成員。如果實例中不存在,那么會繼續(xù)搜索其原型對象,直到
toString()方法被找到并且執(zhí)行。實例可以訪問它原型中的每一個屬性和方法。
hasOwnProperty()判斷對象是否包含特定的實例成員。
in操作符判斷對象是否包含特定的屬性。
原型鏈
???????? 對象的原型決定了實例的類型。默認(rèn)情況下,所有對象都是Object的實例,并繼承了所有基本方法。
嵌套成員
???????? window.localhost.href每次遇到點操作符,嵌套成員會導(dǎo)致JavaScript引擎搜索所有對象成員。這些屬性不是對象的實例屬性,那么成員解析還需要搜索原型鏈,這會花更多的時間。
???????? 緩存對象成員值,不適合緩存對象的方法。
小結(jié):
摘自:高性能JavaScript
轉(zhuǎn)載于:https://www.cnblogs.com/mackxu/archive/2012/12/02/2798744.html
總結(jié)
以上是生活随笔為你收集整理的JS的数据访问及优化访问速度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 封装事件绑定函数解决this在ie下的绑
- 下一篇: 协议森林