js基礎(chǔ)知識(二)

第四章 變量、作用域和內(nèi)存問題

基本類型和引用類型的值

ECMAScript 變量可能包含兩種不同數(shù)據(jù)類型的值:基本類型值和引用類型值?;绢愋椭抵傅氖呛唵蔚臄?shù)據(jù)段,而引用類型值指那些可能由多個值構(gòu)成的對象。

引用類型的值是保存在內(nèi)存中的對象。在操作對象時,實際上是在操作對象的引用而不是實際的對象。

1、復(fù)制變量值
(1)復(fù)制基本類型的值

var num1 = 5;
var num2 = num1; 

此后,這兩個變量是相互獨(dú)立的,可以參與任何操作而不會相互影響。
image.png

(2)復(fù)制引用類型的值

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas" 
image.png

2、檢測類型
typeof 操作符是確定一個變量是字符串、數(shù)值、布爾值,還是 undefined 的最佳工具。

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 操作符

result = variable instanceof constructor

如果變量是給定引用類型(根據(jù)它的原型鏈來識別;第 6 章將介紹原型鏈)的實例,那么 instanceof 操作符就會返回 true。

執(zhí)行環(huán)境及作用域

執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。
每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。

在 Web 瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是 window 對象,因此所有全局變量和函數(shù)都是作為 window 對象的屬性和方法創(chuàng)建的。

某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀(全局執(zhí)行環(huán)境直到應(yīng)用程序退出——例如關(guān)閉網(wǎng)頁或瀏覽器——時才會被銷毀)。

每個函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈(scope chain)。作用域鏈的用途,是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。

每個環(huán)境都可以向上搜索作用域鏈,以查詢變量和函數(shù)名;但任何環(huán)境都不能通過向下搜索作用域鏈而進(jìn)入另一個執(zhí)行環(huán)境。

函數(shù)參數(shù)也被當(dāng)作變量來對待,因此其訪問規(guī)則與執(zhí)行環(huán)境中的其他變量相同。

1、延長作用域鏈
有些語句可以在作用域鏈的前端臨時增加一個變量對象,該變量對象會在代碼執(zhí)行后被移除。具體來說,就是當(dāng)執(zhí)行流進(jìn)入下列任何一個語句時,作用域鏈就會
得到加長:
? try-catch 語句的 catch 塊;
? with 語句。
對 with 語句來說,會將指定的對象添加到作用域鏈中。對 catch 語句來說,會創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。

function buildUrl() {
 var qs = "?debug=true";
 with(location){
 var url = href + qs;
 }
 return url;
} 

2、沒有塊級作用域
3、小結(jié)
JavaScript 變量可以用來保存兩種類型的值:基本類型值和引用類型值?;绢愋偷闹翟醋砸韵?5 種基本數(shù)據(jù)類型:Undefined、Null、Boolean、Number 和 String?;绢愋椭岛鸵妙愋椭稻哂幸韵绿攸c(diǎn):

? 基本類型值在內(nèi)存中占據(jù)固定大小的空間,因此被保存在棧內(nèi)存中;
? 從一個變量向另一個變量復(fù)制基本類型的值,會創(chuàng)建這個值的一個副本;
? 引用類型的值是對象,保存在堆內(nèi)存中;
? 包含引用類型值的變量實際上包含的并不是對象本身,而是一個指向該對象的指針;
? 從一個變量向另一個變量復(fù)制引用類型的值,復(fù)制的其實是指針,因此兩個變量最終都指向同一個對象;
? 確定一個值是哪種基本類型可以使用 typeof 操作符,而確定一個值是哪種引用類型可以使用instanceof 操作符。

所有變量(包括基本類型和引用類型)都存在于一個執(zhí)行環(huán)境(也稱為作用域)當(dāng)中,這個執(zhí)行環(huán)境決定了變量的生命周期,以及哪一部分代碼可以訪問其中的變量。以下是關(guān)于執(zhí)行環(huán)境的幾點(diǎn)總結(jié):

? 執(zhí)行環(huán)境有全局執(zhí)行環(huán)境(也稱為全局環(huán)境)和函數(shù)執(zhí)行環(huán)境之分;
? 每次進(jìn)入一個新執(zhí)行環(huán)境,都會創(chuàng)建一個用于搜索變量和函數(shù)的作用域鏈;
? 函數(shù)的局部環(huán)境不僅有權(quán)訪問函數(shù)作用域中的變量,而且有權(quán)訪問其包含(父)環(huán)境,乃至全局環(huán)境;
? 全局環(huán)境只能訪問在全局環(huán)境中定義的變量和函數(shù),而不能直接訪問局部環(huán)境中的任何數(shù)據(jù);
? 變量的執(zhí)行環(huán)境有助于確定應(yīng)該何時釋放內(nèi)存。

JavaScript 是一門具有自動垃圾收集機(jī)制的編程語言,開發(fā)人員不必關(guān)心內(nèi)存分配和回收問題??梢詫?JavaScript 的垃圾收集例程作如下總結(jié):

? 離開作用域的值將被自動標(biāo)記為可以回收,因此將在垃圾收集期間被刪除。
? “標(biāo)記清除”是目前主流的垃圾收集算法,這種算法的思想是給當(dāng)前不使用的值加上標(biāo)記,然后再回收其內(nèi)存。
? 另一種垃圾收集算法是“引用計數(shù)”,這種算法的思想是跟蹤記錄所有值被引用的次數(shù)。JavaScript引擎目前都不再使用這種算法;但在 IE 中訪問非原生 JavaScript 對象(如 DOM 元素)時,這種算法仍然可能會導(dǎo)致問題。
? 當(dāng)代碼中存在循環(huán)引用現(xiàn)象時,“引用計數(shù)”算法就會導(dǎo)致問題。
? 解除變量的引用不僅有助于消除循環(huán)引用現(xiàn)象,而且對垃圾收集也有好處。為了確保有效地回收內(nèi)存,應(yīng)該及時解除不再使用的全局對象、全局對象屬性以及循環(huán)引用變量的引用。

第五章 引用類型

Object 類型

1、創(chuàng)建 Object 實例的方式有兩種。
(1)使用 new 操作符后跟 Object 構(gòu)造函數(shù)

var person = new Object();
person.name = "Nicholas";
person.age = 29; 

(2)另一種方式是使用對象字面量表示法。(推薦)

var person = {
 name : "Nicholas",
 age : 29
}; 

var person = {}; //與 new Object()相同
person.name = "Nicholas";
person.age = 29; 

在通過對象字面量定義對象時,實際上不會調(diào)用 Object 構(gòu)造函數(shù)。

2、訪問對象屬性(除非必須使用變量來訪問屬性,否則我們建議使用點(diǎn)表示法。)
一般來說,訪問對象屬性時使用的都是點(diǎn)表示法,這也是很多面向?qū)ο笳Z言中通用的語法。不過,在 JavaScript 也可以使用方括號表示法來訪問對象的屬性。在使用方括號語法時,應(yīng)該將要訪問的屬性以字符串的形式放在方括號中。

alert(person["name"]);    //"Nicholas"
alert(person.name);         //"Nicholas" 

從功能上看,這兩種訪問對象屬性的方法沒有任何區(qū)別。但方括號語法的主要優(yōu)點(diǎn)是可以通過變量來訪問屬性:

var propertyName = "name";
alert(person[propertyName]); //"Nicholas" 

如果屬性名中包含會導(dǎo)致語法錯誤的字符,或者屬性名使用的是關(guān)鍵字或保留字,也可以使用方括號表示法:

person["first name"] = "Nicholas"; 

Array 類型

ECMAScript 數(shù)組的每一項可以保存任何類型的數(shù)據(jù)。而且,ECMAScript 數(shù)組的大小是可以動態(tài)調(diào)整的,即可以隨著數(shù)據(jù)的添加自動增長以容納新增數(shù)據(jù)。
1、創(chuàng)建數(shù)組的基本方式有兩種。
(1)是使用 Array 構(gòu)造函數(shù)

var colors = new Array(); 

var colors = new Array(20);    // 創(chuàng)建 length 值為 20 的數(shù)組。

也可以向 Array 構(gòu)造函數(shù)傳遞數(shù)組中應(yīng)該包含的項。

var colors = new Array("red", "blue", "green"); 

另外,在使用 Array 構(gòu)造函數(shù)時也可以省略 new 操作符。

var colors = Array(3); // 創(chuàng)建一個包含 3 項的數(shù)組
var names = Array("Greg"); // 創(chuàng)建一個包含 1 項,即字符串"Greg"的數(shù)組

(2)使用數(shù)組字面量表示法
數(shù)組字面量由一對包含數(shù)組項的方括號表示,多個數(shù)組項之間以逗號隔開。

var colors = ["red", "blue", "green"];           // 創(chuàng)建一個包含 3 個字符串的數(shù)組
var names = [];                                                 // 創(chuàng)建一個空數(shù)組
var values = [1,2,];                                           // 不要這樣!這樣會創(chuàng)建一個包含 2 或 3 項的數(shù)組
var options = [,,,,,];                                         // 不要這樣!這樣會創(chuàng)建一個包含 5 或 6 項的數(shù)組

數(shù)組的項數(shù)保存在其 length 屬性中,這個屬性始終會返回 0 或更大的值。

var colors = ["red", "blue", "green"];   // 創(chuàng)建一個包含 3 個字符串的數(shù)組
var names = [];                                         // 創(chuàng)建一個空數(shù)組
alert(colors.length);                                 //3
alert(names.length);                                //0 

2、檢測數(shù)組
(1)instanceof 操作符

if (value instanceof Array){
 //對數(shù)組執(zhí)行某些操作
} 

instanceof 操作符的問題在于,它假定只有一個全局執(zhí)行環(huán)境。如果網(wǎng)頁中包含多個框架,那實際上就存在兩個以上不同的全局執(zhí)行環(huán)境,從而存在兩個以上不同版本的 Array 構(gòu)造函數(shù)。如果你從一個框架向另一個框架傳入一個數(shù)組,那么傳入的數(shù)組與在第二個框架中原生創(chuàng)建的數(shù)組分別具有各自不同的構(gòu)造函數(shù)。

(2)Array.isArray()

if (Array.isArray(value)){
 //對數(shù)組執(zhí)行某些操作
} 

不過這個方法在低版本的 IE 的瀏覽器中是不支持的。

(3)數(shù)組構(gòu)造函數(shù)

let arr = [1, 2, 3];
console.log(arr.constructor === Array)      // true

這種方法和方法 1 有相同的缺點(diǎn),在多個環(huán)境下 Array 構(gòu)造函數(shù)有可能是不同的,而且在多個環(huán)境下如果要進(jìn)行數(shù)組傳遞的話很有可能會出現(xiàn)問題。

(4)Object.prototype.toString.call() 方法
在任何值上調(diào)用 Object 原生的 toString 方法,都會返回 [object NativeConstructorName] 格式的字符串,利用這一點(diǎn)也可以用來檢測是不是數(shù)組。

let arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr) === "[object Array]"); // true

由于原生數(shù)組的構(gòu)造函數(shù)名與全局環(huán)境無關(guān),因此無論在哪一個環(huán)境下這個方法都可以正確的檢測一個數(shù)組。

3、轉(zhuǎn)換方法
數(shù)組繼承的 toLocaleString()、toString()和 valueOf()方法,在默認(rèn)情況下都會以逗號分隔的字符串的形式返回數(shù)組項。
而如果使用 join()方法,則可以使用不同的分隔符來構(gòu)建這個字符串。join()方
法只接收一個參數(shù),即用作分隔符的字符串,然后返回包含所有數(shù)組項的字符串。

var colors = ["red", "green", "blue"];
alert(colors.join(","));                   //red,green,blue
alert(colors.join("||"));                 //red||green||blue 

如果數(shù)組中的某一項的值是 null 或者 undefined,那么該值在 join()、
toLocaleString()、toString()和 valueOf()方法返回的結(jié)果中以空字符串表示。

4、棧方法
棧是一種 LIFO(Last-In-First-Out,后進(jìn)先出)的數(shù)據(jù)結(jié)構(gòu),也就是最新添加的項最早被移除。而棧中項的插入(叫做推入)和移除(叫做彈出),只發(fā)生在一個位置——棧的頂部。

(1)pop ()
pop()方法則從數(shù)組末尾移除最后一項,減少數(shù)組的 length 值,然后返回移除的項。

(2)push ()
push()方法可以接收任意數(shù)量的參數(shù),把它們逐個添加到數(shù)組末尾,并返回修改后數(shù)組的長度。

var colors = new Array(); // 創(chuàng)建一個數(shù)組
var count = colors.push("red", "green"); // 推入兩項
alert(count); //2
count = colors.push("black"); // 推入另一項
alert(count); //3
var item = colors.pop(); // 取得最后一項
alert(item); //"black"
alert(colors.length); //2 

4、隊列方法
棧數(shù)據(jù)結(jié)構(gòu)的訪問規(guī)則是 LIFO(后進(jìn)先出),而隊列數(shù)據(jù)結(jié)構(gòu)的訪問規(guī)則是 FIFO(First-In-First-Out,先進(jìn)先出)。
(1)shift()
移除數(shù)組中的第一個項并返回該項,同時將數(shù)組長度減 1。

var colors = new Array();                                //創(chuàng)建一個數(shù)組
var count = colors.push("red", "green");     //推入兩項
alert(count);                                                       //2
count = colors.push("black");                         //推入另一項
alert(count);                                                        //3
var item = colors.shift();                                   //取得第一項
alert(item);                                                          //"red"
alert(colors.length);                                           //2 
count = colors.unshift("black");                      //推入另一項
alert(count);                                                         //3 

(2)unshift()
能在數(shù)組前端添加任意個項并返回新數(shù)組的長度。

5、重排序方法
數(shù)組中已經(jīng)存在兩個可以直接用來重排序的方法:reverse()和 sort()。

var values = [1, 2, 3, 4, 5];
values.reverse();       // reverse()方法會反轉(zhuǎn)數(shù)組項的順序。
alert(values);             //5,4,3,2,1 

在默認(rèn)情況下,sort()方法按升序排列數(shù)組項——即最小的值位于最前面,最大的值排在最后面。為了實現(xiàn)排序,sort()方法會調(diào)用每個數(shù)組項的 toString()轉(zhuǎn)型方法,然后比較得到的字符串,以確定如何排序。即使數(shù)組中的每一項都是數(shù)值,sort()方法比較的也是字符串。

sort()的比較函數(shù)在第一個值應(yīng)該位于第二個之后的情況下返回 1,而在第一個值應(yīng)該在第二個之前的情況下返回?1。交換返回值的意思是讓更大的值排位更靠前,也就是對數(shù)組按照降序排序。當(dāng)然,如果只想反轉(zhuǎn)數(shù)組原來的順序,使用 reverse()方法要更快一些。

6、操作方法
(1)concat() (不會影響原始數(shù)組)
可以基于當(dāng)前數(shù)組中的所有項創(chuàng)建一個新數(shù)組。具體來說,這個方法會先創(chuàng)建當(dāng)前數(shù)組一個副本,然后將接收到的參數(shù)添加到這個副本的末尾,最后返回新構(gòu)建的數(shù)組。

var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); //red,green,blue
alert(colors2); //red,green,blue,yellow,black,brown 

(2)slice() (不會影響原始數(shù)組)
它能夠基于當(dāng)前數(shù)組中的一或多個項創(chuàng)建一個新數(shù)組。slice()方法可以接受一或兩個參數(shù),即要返回項的起始和結(jié)束位置,但不包括結(jié)束位置的項。在只有一個參數(shù)的情況下,slice()方法返回從該參數(shù)指定位置開始到當(dāng)前數(shù)組末尾的所有項。

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);
alert(colors2); //green,blue,yellow,purple
alert(colors3); //green,blue,yellow 

如果 slice()方法的參數(shù)中有一個負(fù)數(shù),則用數(shù)組長度加上該數(shù)來確定相應(yīng)的位
置。例如,在一個包含 5 項的數(shù)組上調(diào)用 slice(-2,-1)與調(diào)用 slice(3,4)得到的
結(jié)果相同。如果結(jié)束位置小于起始位置,則返回空數(shù)組。

(4)splice()
splice()的主要用途是向數(shù)組的中部插入項,有如下三種規(guī)則:

? 刪除:可以刪除任意數(shù)量的項,只需指定 2 個參數(shù):要刪除的第一項的位置和要刪除的項數(shù)。例如,splice(0,2)會刪除數(shù)組中的前兩項。

? 插入:可以向指定位置插入任意數(shù)量的項,只需提供 3 個參數(shù):起始位置、0(要刪除的項數(shù))和要插入的項。如果要插入多個項,可以再傳入第四、第五,以至任意多個項。例如,splice(2,0,"red","green")會從當(dāng)前數(shù)組的位置 2 開始插入字符串"red"和"green"。

? 替換:可以向指定位置插入任意數(shù)量的項,且同時刪除任意數(shù)量的項,只需指定 3 個參數(shù):起始位置、要刪除的項數(shù)和要插入的任意數(shù)量的項。插入的項數(shù)不必與刪除的項數(shù)相等。例如,splice (2,1,"red","green")會刪除當(dāng)前數(shù)組位置 2 的項,然后再從位置 2 開始插入字符串"red"和"green"。
splice()方法始終都會返回一個數(shù)組,該數(shù)組中包含從原始數(shù)組中刪除的項(如果沒有刪除任何項,則返回一個空數(shù)組)。

7、位置方法
(1)indexOf()
接收兩個參數(shù):要查找的項和(可選的)表示查找起點(diǎn)位置的索引,從數(shù)組的開頭(位置 0)開始向后查找

(2)lastIndexOf()
接收兩個參數(shù):要查找的項和(可選的)表示查找起點(diǎn)位置的索引,則從數(shù)組的末尾開始向前查找。

這兩個方法都返回要查找的項在數(shù)組中的位置,或者在沒找到的情況下返回-1。。在比較第一個參數(shù)與數(shù)組中的每一項時,會使用全等操作符;也就是說,要求查找的項必須嚴(yán)格相等(就像使用===一樣)。

var numbers = [1,2,3,4,5,4,3,2,1];
alert(numbers.indexOf(4));                        //3 
alert(numbers.lastIndexOf(4));                 //5
alert(numbers.indexOf(4, 4));                    //5
alert(numbers.lastIndexOf(4, 4));             //3
var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];
alert(people.indexOf(person));                    //-1
alert(morePeople.indexOf(person));          //0

8、迭代方法
ECMAScript 5 為數(shù)組定義了 5 個迭代方法。每個方法都接收兩個參數(shù):要在每一項上運(yùn)行的函數(shù)和(可選的)運(yùn)行該函數(shù)的作用域?qū)ο蟆绊?this 的值。傳入這些方法中的函數(shù)會接收三個參數(shù):數(shù)組項的值、該項在數(shù)組中的位置和數(shù)組對象本身。
? every():對數(shù)組中的每一項運(yùn)行給定函數(shù),如果該函數(shù)對每一項都返回 true,則返回 true。
? filter():對數(shù)組中的每一項運(yùn)行給定函數(shù),返回該函數(shù)會返回 true 的項組成的新數(shù)組。
? forEach():對數(shù)組中的每一項運(yùn)行給定函數(shù)。這個方法沒有返回值,改變原數(shù)組。
? map():對數(shù)組中的每一項運(yùn)行給定函數(shù),返回每次函數(shù)調(diào)用的結(jié)果組成的新數(shù)組,不改變原數(shù)組。
? some():對數(shù)組中的每一項運(yùn)行給定函數(shù),如果該函數(shù)對任一項返回 true,則返回 true。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容