動態的屬性前端
定義基本類型值和引用類型值的方式是相似的:建立一個變量併爲該變量賦值算法
對於引用類型的值,能夠爲其添加屬性和方法,也能夠改變和刪除其屬性和方法編程
var person = new Object(); person.name = "Nicholas"; alert(person.name); //"Nicholas"
不能給基本類型的值添加屬性,儘管這樣作不會致使任何錯誤數組
var name = "Nicholas"; name.age = 27; alert(name.age); //undefined
複製變量值瀏覽器
若是從一個變量向另外一個變量複製基本類型的值,會在變量對象上建立一個新值,而後把該值複製
到爲新變量分配的位置上,這兩個變量能夠參與任何操做而不會相互影響安全
var num1 = 5; var num2 = num1;
當從一個變量向另外一個變量複製引用類型的值時,一樣也會將存儲在變量對象中的值複製一份放到爲新變量分配的空間中。不一樣的是,這個值的副本其實是一個指針,而這個指針指向存儲在堆中的一個對象。複製操做結束後,兩個變量實際上將引用同一個對象。所以,改變其中一個變量,就會影響另外一個變量編程語言
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"
傳遞參數函數
訪問變量有按值和按引用兩種方式,而參數只能按值傳遞(全部函數的參數都是按值傳遞的),把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣工具
在向參數傳遞基本類型的值時,被傳遞的值會被複制給一個局部變量(即命名參數, 也是arguments 對象中的一個元素)性能
function addTen(num) { num += 10; return num; } var count = 20; var result = addTen(count); alert(count); //20,沒有變化 alert(result); //30
在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變化會反映在函數的外部
//例1: function setName(obj) { obj.name = "Nicholas"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas" //例2: function setName(obj) { obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
檢測類型
typeof
操做符
//typeof 操做符是肯定一個變量是字符串、數值、布爾值,仍是 undefined 的最佳工具 //若是變量的值是一個對象或 null,則 typeof 操做符會返回"object" //使用 typeof 操做符檢測函數時,該操做符會返回"function" var s = "Nicholas"; var b = true; var i = 22; var u; var n = null; var o = new Object(); alert(typeof s); //string alert(typeof i); //number alert(typeof b); //boolean alert(typeof u); //undefined alert(typeof n); //object alert(typeof o); //object
instanceof
操做符
//在檢測一個引用類型值和 Object 構造函數時,instanceof 操做符始終會返回 true //若是使用 instanceof 操做符檢測基本類型的值,則該操做符始終會返回 false alert(person instanceof Object); // 變量 person 是 Object 嗎? alert(colors instanceof Array); // 變量 colors 是 Array 嗎? alert(pattern instanceof RegExp); // 變量 pattern 是 RegExp 嗎?
概念
執行環境(execution context,也稱爲「環境」)定義了變量或函數有權訪問的其餘數據,決定了它們各自的行爲。每一個執行環境都有一個與之關聯的變量對象(variable object),環境中定義的全部變量和函數都保存在這個對象中。某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬;
全局執行環境是最外圍的一個執行環境,宿主環境不一樣,表示執行環境的對象也不同,在 Web 瀏覽器中,全局執行環境被認爲是 window 對象,全局執行環境直到應用程序退出——例如關閉網頁或瀏覽器——時纔會被銷燬;
每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境;
當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈(scope chain)。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。
做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。若是環境是函數,則將其活動對象(activation object)做爲變量對象。活動對象在最開始時只包含一個變量,即 arguments 對象(這個對象在全局環境中是不存在的)。做用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。
標識符解析是沿着做用域鏈一級一級地搜索標識符的過程。搜索過程始終從做用域鏈的前端開始,而後逐級地向後回溯,直至找到標識符爲止(若是找不到標識符,一般會致使錯誤發生)
var color = "blue"; function changeColor(){ if (color === "blue"){ color = "red"; } else { color = "blue"; } } changeColor(); alert("Color is now " + color);
在局部做用域中定義的變量能夠在局部環境中與全局變量互換使用,每一個環境均可以向上搜索做用域鏈,以查詢變量和函數名;但任何環境都不能經過向下搜索做用域鏈而進入另外一個執行環境。
var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; // 這裏能夠訪問 color、anotherColor 和 tempColor } // 這裏能夠訪問 color 和 anotherColor,但不能訪問 tempColor swapColors(); } // 這裏只能訪問 color changeColor();
延長做用域鏈
try-catch 語句的 catch 塊:會建立一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明
with 語句:會將指定的對象添加到做用域鏈中
function buildUrl() { var qs = "?debug=true"; with(location){ var url = href + qs; } return url; }
沒有塊級做用域
if 語句中的變量聲明會將變量添加到當前的執行環境(在這裏是全局環境)中
if (true) { var color = "blue"; } alert(color); //"blue"
for 語句建立的變量 i 即便在 for 循環執行結束後,會存在於循環外部的執行環境中
for (var i=0; i < 10; i++){ doSomething(i); } alert(i); //10
使用 var 聲明的變量會自動被添加到最接近的環境中,若是初始化變量時沒有使用 var 聲明,該變量會自
動被添加到全局環境
function add(num1, num2) { sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum); //30
查詢標識符的過程:
從做用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符
若是在局部環境中找到了該標識符,搜索過程中止,變量就緒
var color = "blue"; function getColor(){ var color = "red"; return color; } alert(getColor()); //"red"
若是在局部環境中沒有找到該變量名,則繼續沿做用域鏈向上搜索。搜索過程將一直追溯到全局環境的變量對象
var color = "blue"; function getColor(){ return color; } alert(getColor()); //"blue"
若是在全局環境中也沒有找到這個標識符,則意味着該變量還沒有聲明
概念
標記清除
引用計數
不太常見的垃圾收集策略,含義是跟蹤記錄每一個值被引用的次數。當聲明瞭一個變量並將一個引用類型值賦給該變量時,則這個值的引用次數就是 1。若是同一個值又被賦給另外一個變量,則該值的引用次數加 1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數減 1。當這個值的引用次數變成 0 時,則說明沒有辦法再訪問這個值了,於是就能夠將其佔用的內存空間回收回來。這樣,當垃圾收集器下次再運行時,它就會釋放那些引用次數爲零的值所佔用的內存
產生的問題:循環引用,指的是對象 A 中包含一個指向對象 B 的指針,而對象 B 中也包含一個指向對象 A 的引用,會致使內存得不到回收
function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }
IE 的 JavaScript 引擎是使用標記清除策略來實現的,但JavaScript 訪問的 COM(組件對象模型) 對象依然是基於引用計數策略的,涉及 COM 對象,就會存在循環引用的問題,爲了不相似這樣的循環引用問題,最好是在不使用它們的時候手工斷開鏈接
//因爲存在這個循環引用,即便將例子中的 DOM 從頁面中移除,它也永遠不會被回收 var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.someObject = myObject; //將變量設置爲 null 意味着切斷變量與它此前引用的值之間的鏈接 myObject.element = null; element.someObject = null;
性能問題
window.CollectGarbage()
方法會當即執行垃圾收集。在 Opera 7 及更高版本中,調用 window.opera.collect()
也會啓動垃圾收集例程。管理內存
JavaScript在進行內存管理及垃圾收集時面臨的最主要的一個問題,就是分配給 Web瀏覽器的可用內存數量一般要比分配給桌面應用程序的少。這樣作的目的主要是出於安全方面的考慮,目的是防止運行 JavaScript 的網頁耗盡所有系統內存而致使系統崩潰,內存限制問題不只會影響給變量分配內存,同時還會影響調用棧以及在一個線程中可以同時執行的語句數量,
所以,確保佔用最少的內存可讓頁面得到更好的性能。而優化內存佔用的最佳方式,就是爲執行中的代碼只保存必要的數據,一旦數據再也不有用,最好經過將其值設置爲 null 來釋放其引用——這個作法叫作解除引用(dereferencing)。這一作法適用於大多數全局變量和全局對象的屬性。局部變量會在它們離開執行環境時自動被解除引用
function createPerson(name){ var localPerson = new Object(); localPerson.name = name; return localPerson; } var globalPerson = createPerson("Nicholas"); // 手工解除 globalPerson 的引用 globalPerson = null;
解除一個值的引用並不意味着自動回收該值所佔用的內存。解除引用的真正做用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收