來吧!一文徹底搞懂引用類型!

  • 2020 年 2 月 13 日
  • 筆記

作者 | Jeskson

掘金 | https://juejin.im/user/5a16e1f3f265da43128096cb

2020年01月09日

引用類型的值(對象)是引用類型的一個實例。引用類型有時也稱對象定義,因為它們描述的是一類對象所具有的屬性和方法。

對象是某個特定引用類型的實例,新對象是使用new操作符後跟一個構造函數來創建的,構造函數本身就是一個函數,只不過該函數是出於創建新對象的目的而定義的。

ver dada = new Object();

JavaScript對象劃分為:

引用類型,基本包裝類型,內置對象

JavaScript引用類型分:Object類型,Array類型,RegExp類型,Date類型,Function類型。

基本包裝類型,Boolean類型,Number類型,String類型。

Object類型

Object類型是JavaScript中常見的一種類型,使用new操作符

vardada =newObject();dada.name ="dada";dada.age =12;

使用對象字面量表示法:

vardada = {name:'dada',age:12,}

訪問對象的屬性

console.log(dada.name);console.log(dada['name']);

vardadaname ="dada";console.log(dada[dadaname]);

注意,除非是必須非要使用變量來訪問屬性,否則不建議使用,還是建議使用點方法來表示。

new運算符的作用就是創建一個對象的實例,這個對象可以是用戶自定義,也可以是帶有構造函數的一些系統自帶的對象。

創建Object類型有兩種方法:

第一,使用new運算符創建Object,第二,使用對象字面量方式創建Object,使用最多。

判斷js對象是否擁有這個屬性,第一種,使用in判斷對象的所有屬性,包含對象實例以及其原型的屬性;第二種,使用

Object.hasOwnProperty(proName);判斷的是對象的實例是否具有某個屬性。

引用類型通常叫做類,即是,遇到引用值,可以理解為對象。

對象是由 new 運算符加上要實例化的對象的名字創建的

varo =newObject();

Object對象具有的屬性

constructor對創建對象的函數的引用(指針),對於Object對象,該指針指向原始的Object()函數。

檢測數組

instanceof

Array.isArray(value)

Object.prototype.toString.call(obj)

轉換方法

toString() 返回字符串

valueOf() 返回數組本身

toLocaleString()

join()

vardada = ['da1','da2'];dada.toString()"da1,da2"dada.valueOf()['da1','da2']

添加移除

push()數組末尾接收任意數量,並返回修改後的數組的長度。

pop()數組末尾移除最後一項,減少length值,返回移除的項

shift()移除數組第一項,並返回該項,數組長度減一

unshift()數組前面添加任意項並返回新的數組的長度

重排序

reverse() 反轉

sort()排序

vararr = [1,3,7,9,4,5]vararr2 = arr.sort(function(a,b){// 從小到大的排序returna-b// 從大到小的排序returnb-a})console.log(arr2);

操作方法,concat()創建當前數組的一個副本,如果有參數則添加這個副本的末尾,如果沒有參數就返回當前數組的副本。

slice(start,end)接收一個或兩個參數,但不返回結束位置的項,不會影響原數組;splice(起始位置,刪除的項數量,要插入的項),刪除,替換,插入。

dada.splice(1, 0, 'da1','da2');

// dada從1位置插入兩項

dada.splice(1,1, 'da1','da2');

// 為在1位置刪除1項,插入兩項

indexOf()從數組開頭向後查找

lastIndexOf()從數組的末尾開始向前查找

返回的是查找項在數組中的位置,如果沒有找到就返回-1

基本類型值和引用類型值,基本類型值是簡單的數據,而引用類型是指由多個值構成的對象。

基本類型,undefined,null,boolean,number,string

引用類型,Object,Array等

從一個變量向另一個變量複製引用類型的值時,同樣也會將存儲在變量對象中的值複製一份到新變量分配的空間中。不同的是,這個值的副本實際上是一個指針,這個指針指向存儲在堆中的一個對象,複製後的結果是,兩個變量實際上將引用同一個對象,兩個變量對象指向堆內存中同一個Object對象,所以當改變其中一個變量,就會影響另一個變量。

vardada =newObject();vardashu = dada;dada.name='da1';console.log(dashu.name);// da1

數值的傳遞,函數的參數

在JavaScript中所有函數的參數都是使用值傳遞的,就是把函數外的值複製傳遞給函數內部的參數,就好像從一個變量賦值到另一個變量一樣

基本類型值的傳遞如同基類型變量的複製效果一樣,引用類型的值傳遞,就如同引用類型變量的複製一樣的效果。

在向參數傳遞基本類型值時,被傳遞的值會複製給一個局部變量,在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給這個局部變量。

基本類型

functionadd(num){ num +=1;returnnum;}varda1 =12;varresult = add(da1);console.log(da1);console.log(result);

引用類型

functionsetName(obj){ obj.name ='dada';}varperson =newObject();setName(person);console.log(person.name);dada

typeof檢測基本類型

typeof中 var da; // 檢測為undefined

typeof中 var da = null; // 檢測為object

typeof中 var da = new Object(); // 檢測為object

instanceof檢測引用類型的值

如果不是檢測的不是引用類型的值,而是基本類型的值,則返回false

如果是引用類型的值,返回為true

vard =newNumber(11);console.log(dinstanceofNumber);true

Object.prototype.toString表示返回一個表示該對象的字符串

Object.prototype.toString.call(null);

"[object Null]"

Object.prototype.toString.call(1111);

"[object Number]"

Object.prototype.toString.call('string');

"[object String]"

總結Object類型,大部分引用類型值都是Object類型的實例,創建Object實例的方式有兩種,一種是使用new操作符後跟Object構造函數,一種是使用對象字面量表示法。

array類型,Date類型,RegExp類型,Function類型,基本包裝類型是什麼呢?

我相信你在學習時一定見到過Array類型,也是最常見的,數組的每一項可以存儲任何類型的數據,數組的大小是可以動態變動的,數組的長度越大就可以存儲更多的數據。

那麼第一問,數組是怎麼創建的呢?

數組有哪些自帶的屬性,如何檢查是否為一個數組,數組元素的增刪改等,數組與字符串的相互轉化,數據的一些方法,如,截取,合併,排序,查找數組元素的元素,如何遍曆數組,進行迭代等。

數組的創建,同樣數組也有兩種基本的創建方式,一種是使用array構造函數,和創建對象一樣new Object()方法。看看使用Array構造函數的語句代碼:

varda =newArray();newArray();// 創建一個數組newArray([size]);// 創建一個數組並指定長度newArray(element0, element1, …, elementn);// 創建一個數組並賦值

採用字面量的方法

varda = [];// 創建一個空數組varda1 = [1,2,3];// 創建一個有三個元素的數組

varda1 = [1,2,3];console.log(da1instanceofArray);VM272:1true

vardada = [1,2,3];undefinedconsole.log(Array.isArray(dada));VM422:1true

檢查是否是數組的兩種方法:

一種使用instanceof,一種使用Array.isArray()方法。

constructor// 返回創建數組對象的原型函數length// 返回數組的長度prototype// 可以增加數組的原型方法和函數

可以看出一個數組中方法:

_proto_concatconstructorcopyWithinentrieseveryfillfilterfindfindIndexflatflatMapforEachincludesindexOfjoinkeyslastIndexOflengthmappoppushreducereduceRightreverseshiftslicesomesortsplicetoLocaleStringtoStringunshift

數組的length屬性,返回數組的長度。

vararray =newArray();undefinedconsole.log(array.constructor);VM576:1ƒArray() { [native code] }undefinedconsole.log(array.constructor ===Array);VM615:1trueundefined

constructor屬性返回對創建此對象的數組函數的引用,即是返回對象相對應的構造函數。

array.push(元素1,元素2,…)將一個或多個元素添加到數組的末尾,並返回新數組的長度。

array.unshift(元素1,元素2,…)將一個或多個元素添加到數組的開頭,並返回新數組的長度。

array.pop()從數組中刪除最後一個元素,並返回最後一個元素的值。

原始數組的最後一個元素被刪除。

array.shift()刪除數組的第一個元素,並返回第一個元素。

原始數組的第一個元素被刪除。

array.splice(start 第一個參數為指定開始修改的位置,如果超出了數組的長度就從末尾開始添加內容,如果為負值,就從數組末尾開始的第幾位,從1開始計數, deleteCount第二個參數為,要刪除的元素的個數,是從start位置開始要刪除的元素個數,為0時表示不刪除元素,其後的參數為要加入的元素,從start位置開始,添加的元素。

數組轉為字符串

array.join('')

所有對象都具有toLocaleString(),toString(),valueOf()方法

varda = [1,23,4];undefinedconsole.log(da.toString());VM727:11,23,4undefinedconsole.log(da.valueOf());VM813:1(3) [1,23,4]undefinedconsole.log(da.toLocaleString());VM861:1undefined

toString()方法(toLocaleStirng方法),返回這個數組中每個值的字符串形式,拼接而成的一個以逗號分隔的字符串,valueOf()方法,返回的是數組本身。

當有一個值為null或者是undefined時,在join()方法,toStirng()方法和valueOf()方法,toLocaleString()方法的返回則都是以空字符串表示。

字符串變化為數組

string.split(第一個參數為字符串或者是正則表達式,從該參數指定的地方對字符串進行分割,第二個參數為指定返回的數組的最大長度)用於把一個字符串分割成字符串數組

數組的截取與合併

array.slice(start,end)方法,從數組中返回start下標開始,直到end下標結束(不包含),該方法不會改變原始數組,只會返回一個新的子數組。

vararr = [2,23,3,23];undefinedarray.slice(-1);[]console.log(arr);VM1019:1(4) [2,23,3,23]undefinedarr.slice(1,3)(2) [23,3]console.log(arr);VM1131:1(4) [2,23,3,23]undefined

可以看出這個方法不會修改原始數組,只是會返回一個新的子數組,如果想要刪除數組中的元素,可以使用array.splice()。

數組的合併

array.concat()方法

sort()方法用於對數組的元素進行排序,並返回原數組。

不帶參數,按照字符串UniCode碼的順序進行排序。

constarray= ['a','d','c','b'];array.sort();//['a', 'b', 'c', 'd']

按照數值大小進行排序-升序

array.sort((a,b) => {returna-b;// 從小到大排序});

按照數值大小進行排序-降序

array.sort((a,b)=>{returnb-a;// 從大到小排序});

array.reverse()方法

reverse() 方法將數組中元素的位置顛倒,第一個數組元素成為最後一個數組元素,最後一個數組元素成為第一個。

數組的sort()和reverse()方法都對原數組進行了修改,返回值是經過排序之後的數組。

find(callback[, thisArg])方法,用於找出第一個符合條件的數組元素。

[1,3,-5,20].find((n) =>n <0)// -5

findIndex(callback[, thisArg])返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回-1。

[1,3,13,25].findIndex(function(value, index, arr){returnvalue >9;})// 2

functionf(value){returnvalue >this.age;}letperson = {name:'dada',age:20};[1,12,25,5].find(f, person);// 25

includes(第一個參數為要查找的元素,第二個參數為開始查找的索引位置)方法,返回一個布爾值,表示是否存在給定值在該數組中。

array.filter()方法,array.every()方法檢測數組中的每個元素是否通過測試的,如果測試通過為true。

arr.some()判斷數組匯總是否包含可以通過測試的元素怒,和every不同的是,這裡只要某一個元素通過測試,即返回true。

[2,3,15,1,4].some(item=>item >6);// true

leta = [1,2,3,4,5];letb = [];a.forEach((item) =>{ b.push(item +1);});console.log(b);// [2,3,4,5,6]

keys()是對鍵名的遍歷、values()對鍵值的遍歷、entries()是對鍵值對的遍歷。

for(letitemof['a','b'].keys()){ consloe.log(item);//0//1}for(letitemof['a','b'].values()){ consloe.log(item);//'a'//'b'}letarr = [0,1];for(letitemofarr.entries()){console.log(item);// [0, 0]// [1, 1]}

consttotal = [0,1,2,3].reduce((a, b) =>{returna + b;},0);// total is 6

constda = [[0,1], [2,3], [4,5]].reduce((a, b) =>{returna.concat(b);}, []);// da is [0, 1, 2, 3, 4, 5]

Array.from()方法可以用來將json數組格式轉換為數組

letda = {'0':'a','1':'b','2':'c',length:3};letarr =Array.from(da);// ['a', 'b', 'c']

Array.of()方法可以將一組值轉換為數組

letda =Array.of(1,2,3,4);console.log(da);//[1,2,3,4]

console.log(…[1,2,3])// 1 2 3console.log(1, …[2,3,4],5)// 1 2 3 4 5

棧方法,隊列方法

數組的表示像棧一樣,棧是一種後進先出的數據結構。

棧中項的插入(叫做推入)和移除(叫做彈出),只發生在一個位置——棧的頂部。

push()方法可以接收任意數量的參數,在數組的末尾逐個添加,並返回修改後數組的長度,pop()方法可以從數組末尾移除最後一項,返回移除的項。

隊列方法

隊列數據結構的訪問方法是先進先出,隊列在列表末端添加項,從列表的前端移除項。

every()方法,傳入的函數必須對每一項返回true,才會返回true,否則它就返回false,some()方法則是只要傳入的函數對數組中的某項返回true,就會返回true。

Object對象具有的屬性

constructor

對創建對象的函數的引用(指針),對於Object對象,該指針指向原始的Object()函數。

Prototype

對該對象的對象原型 的引用,對於所有的對象,它默認返回Object對象的一個實例。

方法:

hasOwnProperty(property)

判斷對象是否有某個特定的屬性,必須要字符串指定該屬性

IsPrototypeOf(object)

判斷該對象是否為另一個對象的原型

PropertyIsEnumerable

判斷給定的屬性是否可以用for…in語句進行枚舉

toString()返回對象的原始字符串表示。

valuOf()方法返回最合適該對象的原始值。

reduce()方法從數組的第一項開始,逐個遍歷到最後。

reduceRight()方法從數組的最後一個項開始,向前遍歷到第一項。

JavaScript引用類型中的Date對象

Date 類型使用自 UTC(Coordinated Universal Time,國際協調時間)1970 年 1 月 1 日午夜(零時)開始經過的毫秒數來保存日期。

創建Date對象,使用new 操作符和Date構造函數:

vardate =newDate();vardate =newDate("1178/08/06 04:20:00");vardate =newDate(154656847478617);

靜態方法,Date.now()方法返回當前時間距離時間零點,1970年1月1日00:00:00UTC的毫秒數,相當於Unix時間戳乘以1000。

getTime()方法

返回實例距離1970年1月1日00:00:00的毫秒數,等同於valueOf方法

Date類型使用UTC國際協調時間1970年1月1日午夜(零時)開始經過的毫秒數來保存日期。如果調用Date構造函數而不傳遞參數,則新創建的對象獲取的是當前的日期和時間。

如果想要特定的日期和時間創建日期對象,傳入參數。

// 提供了兩個方法Date.parse()Date.UTC()

Date.parse()方法接收一個表示日期的字符串參數

varnewDate =newDate(Date.parse('May 23, 2010'));

Date.UTC()方法返回表示日期的毫秒數。

getFullYear() 返回四位的年份getMonth() 返回月份(0 ~ 11,0表示1月,11表示12月)getDay() 返回星期幾,星期日為0,星期一為1,以此類推getDate() 返回實例對象對應每個月的幾號(從1開始)getHours() 返回小時數 (0 ~ 23)getMinutes() 返回分鐘數 (0 ~ 59)getSeconds() 返回秒數 (0 ~ 59)getMilliseconds() 返回毫秒數 (0 ~ 999)setTime(milliseconds) 設置毫秒時間戳setYear(year) 設置距離1900年的年數setFullYear(year[, month, date]) 設置四位年份setMonth(month[, date]) 設置月份(0-11)setDate(date) 設置實例對象對應的每個月的幾號(1-31),返回改變後毫秒時間戳setHours(hour[, min, sec, ms]) 設置小時(0-23)setMinutes(min[, sec, ms]) 設置分鐘(0-59)setSeconds(sec[, ms]) 設置秒(0-59)setMilliseconds() 設置毫秒(0-999)

toDateString()

顯示格式星期幾,月,日,年

toTimeString()

顯示時,分,秒和時區

toLocalDateString()

顯示星期幾,月,日,和年

toLocaleTimeString()

顯示實現的格式顯示時,分,秒

toUTCString()

顯示完整的UTC日期

RegExp類型

ECMAScript 通過 RegExp 類型來支持正則表達式。

varexpression =/ pattern /flags ;// 模式(pattern)部分可以是任何簡單或複雜的正則表達式// 標誌(flags)用以標明正則表達式的行為

g表示全局模式,即是模式將被應用於所有字符串

i表示不區分大小寫模式

m表示多行模式

RegExp 構造函數接收兩個參數:

一個是要匹配的字符串模式,一個是可選的標誌字符串。

RegExp實例方法

exec()方法,用於檢索字符串的正則表達式的匹配。

string,檢索的字符串,返回一個數組,沒有則返回null

/d{2,4}/ //匹配2~4個數字

/w{3}d?/ //精確匹配三個單詞和一個可選的數字

函數,函數其實是對象,每個函數都是Function類型的實例。

韓式是定義一次就可以調用或者是執行任意多次的一段代碼,通過函數可以封裝很多代碼塊,而且可以在任何地方,任何時候調用,函數本身沒有運行功能,只有調用才能運行。

函數實際上是對象,每個函數都是Function類型的實例

函數名是指向函數對象的指針,不會與某個函數綁定,使用不帶有圓括號的函數名是訪問函數指針,不是調用函數。

在函數中,有兩個特殊的對象,arguments和this。

functionmethod(){// 沒有參數的函數console.log("da");}functionmethod(name){// 帶有參數的函數console.log("da1");}

函數聲明:

使用函數聲明語法

functionmethod(name){returnname;}

使用變量初始化函數

varda =function(name){returnname;}

使用function構造函數

varda1 =newFunction(name);

varadd=newFunction(『num1』,』num2』,』return num1+num2』);// 不推薦這種方式,因為會導致解析兩次代碼,// 第一次解析常規ECMAscript代碼(全部代碼),// 第二次解析傳入構造函數中的字符串,從而影響性能。

參數,把函數的返回值作為參數傳遞,把函數本身作為參數傳遞。

varresult=add(sum(10),10);varresult=add(sum,10);//sum是一個函數

return返回值,函數也可以不帶有返回值,將返回undefined

函數名實際上也是一個指向函數對象的指針,不會與某個函數綁定。

定義函數,第一,使用函數聲明語法,第二,使用函數吧表達式,第三,使用function構造函數。

函數聲明與函數表達式的區別

解釋器會先讀取函數聲明,在使其在執行之前可以訪問。

函數聲明語法

functionsum(num1, num2){returnnum1 + num2;}

使用函數表達式則必須要等到解析器執行到它所在的代碼行時,才會執行,變量聲明提前,而值留在原地。

console.log(sum(10,10));functionsum(num1, num2){returnnum1 + num2;}

在代碼執行前,解析器會通過函數聲明提升,讀取並將函數聲明添加到執行環境中,放入到代碼樹的頂部。

console.log(sum(10,10));// 報錯varsum =function(num1, num2){returnnum1 + num2;};

此代碼函數位於一個初始化語句中,不是函數聲明,不會提前,會把var sum提前,也就是在執行到函數所在語句之前,變量sum中不會保存對函數的引用。

函數是對象來着,函數名是指針。函數名僅僅是指向函數的指針,所以,一個函數對象可以有多個名字。

沒有重載的概念

functiondada(num){returnnum +100;}functiondada(num){returnnum +200;}varresult = dada(100);//300// 後面的函數覆蓋了前面的函數

以上代碼實際上與下面的代碼沒有什麼區別:

varda1=function(num){returnnum +100;};da1=function(num){returnnum +200;};varresult = da1(100);//300

函數作為參數傳遞給另一個函數

要訪問函數的指針而不執行函數的話,必須去掉函數名後面的那對圓括號;從一個函數中返回另一個函數。

函數內部的屬性

arguments是個類數組對象

傳入函數的所有參數,對象有一個叫callee的屬性,屬性值是一個指針,指向擁有arguments對象的函數本身。

this對象,指向函數執行的環境對象,在哪個對象中執行,就指向哪個對象。

caller屬性,保存着調用當前函數的函數的引用

如果是在全局作用域中調用當前函數,它的值為 null。

functionouter(){ inner(); }functioninner(){console.log(inner.caller); } outer();/*輸出結果:

ƒunction outer(){

inner();

}

*/

prototype屬性:

是一個對象,通過調用構造函數而創建的一個對象,可以由特定類型的所有實例共享的屬性和方法。

toString()方法將函數體轉換成對應的字符串。

bind()方法:

會創建一個函數的實例,其 this 值會被綁定到傳給 bind() 函數的值。

apply()與call()方法

兩個方法的作用都是在特定的作用域中調用函數

實際上就是設置函數內部的this值

apply():接收兩個參數

一個是函數運行的作用域,

一個是參數數組,可以是數組,也可以是arguments這個類數組對象;

在使用 call()方法時,傳遞給函數的參數必須逐個列舉出來;

區別僅在於接收參數的方式不同。

apply() 和 call() 真正的本質是用來,擴充函數賴以運行的作用域。

基本包裝類型

為了便於操作基本數據類型,ECMAScript還提供了3個特殊的引用數據類型:Boolean、Number和String。

引用類型與基本包裝類型的區別,在於它們的對象生命周期不同:引用類型:使用new創建引用類型的實例,在執行數據流離開當前作用域時會一直存儲在內存中。基本包裝類型:自動創建基本包裝類型的對象,只執行一行代碼的瞬間之後就會立即銷毀。這意味着在運行時為基本包裝類型值添加屬性和方法是無效的。

創建Boolean對象

// 定義了一個名為 da 的布爾對象varda =newBoolean(true);

如果布爾對象無初始值或者其值為:0、-0、null、""、false、undefined、NaN,那麼對象的值為false,否則,其值為true

在布爾表達式中,所有的對象都會被轉換為true。

引用類型的布爾值,其實是對象,所以在布爾表達式中使用Boolean 對象都會返回為true。

varda =newBoolean(false); alert(typeofda);// object varda1 =false; alert(typeofda1);// boolean

varda =newBoolean(false); alert(dainstanceofBoolean);// true varda1 =false;alert(da1instanceofBoolean);// false

toString()方法

功能:根據布爾值返回字符串 "true" 或"false"。

valueOf()方法

功能:返回 Boolean 對象的原始值,就是返回基本類型值

true 或 false。

創建Number對象

Number類型是數字值創建的引用類型。

varda =newNumber(10);

toFixed()方法

Number類型的toFixed()方法可以接受一個數值,表示保留的小數的個數(會自動四捨五入)。

toString()方法

將Number數值轉換為字符串,該方法接受一個可選參數基數,告訴它返回幾進制數值的字符串形式,若省略,則默認基數為10,即十進制。

valueOf()方法

valueOf()返回一個 Number 對象的基本數字值。

創建String對象

String類型是字符串值創建的引用類型。

varda =newString("hello world");

charAt()和 charCodeAt()兩個方法都接收一個參數

即基於 0 的字符位置

charAt():

以單字符字符串的形式返回給定位置的那個字符

charCodeAt():

返回給定位置的那個字符的字符編碼

截取字符串的方法:

slice()、substr()和 substring()

這三個函數,都不改變原本的字符串,只是在原先的字符串上創建了一個副本,返回操作副本後的值。

slice()方法會將傳入的負值與字符串的長度相加substr()方法將負的第一個參數加上字符串的長度,而將負的第二個參數轉換為0substring()方法會把所有負值參數都轉換為0。

indexOf()

方法從索引 0 的位置開始查詢子字符串

lastIndexOf()

方法從最後一個索引開始查找字符串

trimLeft()和 trimRight()方法,

分別用於刪除字符串開頭和末尾的空格。

字符串大小寫轉換的方法:toLowerCase()、toUpperCase()var stringValue ="hello world"; alert(stringValue.toUpperCase());//"HELLO WORLD"alert(stringValue.toLowerCase());//"hello world"

match()方法

vars ='hello21 world21';console.log(s.match(/d{2}/));// [ '21', index: 5, input: 'hello21 world21' ]

parseInt():轉換為整型;

parseFloat():轉換為浮點型;

isNaN():判斷為是否不是數值;

encodeURI():對字符串進行編碼;

decodeURI():

對encodeURI()函數編碼過的字符串進行解碼。

內置對象:

ECMA-262對內置對象是,由ECMAScript實現提供的,不依賴宿主環境的對象。

程序執行之前就已經存在了。不需要自己定義,不需要自己實例化。

定義了兩個內置對象:Global和Math。