摘要:字面形式允許你在不需要使用操作符和構造函數顯式創建對象的情況下生成引用值。操作符以一個對象和一個構造函數作為參數鑒別數組有前一小結可以知道鑒別數組類型可以使用。屬性是函數獨有的,表明該對象可以被執行。這種函數被稱為匿名函數。
引子:
1.JavaScript 中的變量類型和類型檢測
1.1原始類型
1.2引用類型
1.3內建類型的實例化
1.4函數的字面形式
1.5正則表達式的字面形式
1.6類型檢測
1.6.1原始類型的檢測
1.6.2鑒別引用類型
1.6.3鑒別數組
1.6.4原始封裝類型
2.JavaScript 中的函數
2.1定義函數的兩種方式
2.1.1函數聲明
2.1.2函數表達式
2.2JavaScript函數的參數
2.3函數的重載
2.4函數使用最重要的3個點
2.4.1 this的使用
2.4.2 call和apply的使用
2.4.3 bind的使用
引子:最近看了兩本書,書中有些內容對自己還是很新的,有些內容是之前自己理解不夠深的,所以拿出來總結一下,這兩本書的名字如下:
JavaScript 面向對象精要
JavaScript 啟示錄
如果對于 JavaScript 面向對象編程理解不夠深的話,第一本書還是強烈推薦的。第二本書比較適合初中級的開發者閱讀。對各種知識點都有代碼示例。內容中規中矩。
1.JavaScript 中的變量類型和類型檢測C#和Java等編程語言用棧存儲原始類型,用堆存儲引用類型,JavaScript則完全不同:它使用一個變量對象追蹤變量的生存期。原始值被直接保存在變量對象內,而引用值則作為一個指針保存在變量對象內,該指針指向實際對象在內存中的存儲位置。
1.1原始類型在 JavaScript 中有5中原始類型,分別如下:
類型表達式 | 類型描述 |
---|---|
boolean | 布爾,值為 false或者 true |
number | 數字,值為任何整型或者浮點數值 |
string | 字符串,值由單引號或者雙引號括出的單個字符或者連續字符(JavaScript不區分字符類型) |
null | 空類型,該原始類型僅有一個值:null |
undefined | 未定義,該原始類型僅有一個值:undefined(undefined會被賦給一個還沒有初始化的變量) |
所有原始類型的值都有字面形式,字面形式是不被保存在變量中的值。
//string var name="zhiqiang"; var selection="a"; //number var count=235; var cost=1.51; //boolean var found=true; //null var object=null; //undefined var flag=undefined; var ref; console.log(ref); //undefined
原始類型的變量直接保存原始值(而不是一個指向對象的指針)。當將原始值賦值給一個變量時,該值將被復制到變量中。也就是說,如果你使一個變量等于另一個時,每個變量有它自己的一份數據拷貝。
示例代碼如下:
var color1="red"; var color2=color1;
內存中的保存形式,如下圖:
1.2引用類型引用類型是在JavaScript中找到最能接近類的東西。引用值是引用類型的實例,也是對象的同義詞。屬性包含鍵(始終是字符串)和值。如果一個屬性的值是函數,它就被稱為方法。JavaScript中函數其實是引用值,除了函數可以運行以外,一個包含數組的屬性和一個包含函數的屬性沒有區別。
創建引用類型的兩種方式看下面的一段代碼:
//第一種使用new操作符 var obj1 = new Object(); // var obj2 = obj1; //第二種 var obj3 = {}
以上兩種創建對象的方式并沒有本質的區別,是等價的。
那么當我們創建了一個對象,且發生了賦值的時候,在內存中發生了什么呢?
看下圖:
1.當發生了new操作的時候,先在內存中開辟一塊空間,存放創建的對象,并且使obj1指向這塊開辟的空間;
2.引用類型發生賦值的時候,僅僅是引用地址指向了內存中的同一塊區域;
JavaScript語言有"垃圾回收"功能,所以在使用引用類型的時候無需擔心內存分配。但是為了防止"內存泄露"還是應該在不實用對象的時候將該對象的引用賦值為null。讓"垃圾回收"器在特定的時間對那一塊內存進行回收。
1.3內建類型的實例化JavaScript中的內建類型如下:
類型 | 類型描述 |
---|---|
Array | 數組類型,以數字為索引的一組值的有序列表 |
Date | 日期和時間類型 |
Error | 運行期錯誤類型 |
Function | 函數類型 |
Object | 通用對象類型 |
RegExp | 正則表達式類型 |
內建引用類型有字面形式。字面形式允許你在不需要使用new操作符和構造函數顯式創建對象的情況下生成引用值。(包括字符串,數字,布爾,空類型和未定義);
1.4函數的字面形式創建函數的三種方式:
//第一種函數聲明 function abc(){ console.log(1); } //使用構造函數的形式 var value = new Function("","console.log(1)"); //函數表達式 var a = function(){ console.log(1); };
使用構造函數的方式創建函數,不易讀,且調試不方便,不建議使用這種方式創建函數。
1.5正則表達式的字面形式在JavaScript中使用正則表達式有兩種方式:
var a1 = /d+/g;//使用字面形式 var a2 = new RegExp("d+","g");//使用構造函數的形式
在JavaScript中建議使用字面形式的正則表達式,因為不需要擔心字符串中的轉義字符。比如上面示例代碼中字面形式使用d而構造函數使用的是d;
1.6類型檢測 1.6.1原始類型的檢測使用typeof運算符可以完成對原始類型的檢測,看下面的一段代碼:
上面的代碼中有一段比較特殊就是
typeof null //object
這里其實是不準確的,如果我們要判斷一個值是否為空類型的最佳的方式是直接和null進行比較
console.log(value === null);
==和===之間的最主要的區別就是前者在進行比較的時候會進行類型轉化,而后者不會;
console.log(5==5);//true console.log("5"==5);//false console.log("5"===5);//fasle1.6.2鑒別引用類型
JavaScript中對于引用類型的檢測較為復雜。對于函數類型的引用使用typeof返回的是Function,而對于非函數的引用類型返回的則是object。所以在JavaScript中鑒別引用類型的類型引入了instanceof。
instanceof操作符以一個對象和一個構造函數作為參數;
function a (){} var b = {}; var c =[]; typeof a // function typeof b //object typeof c //object a instanceof Function //true b instanceof Object //true c instanceof Array //true1.6.3鑒別數組
有前一小結可以知道鑒別數組類型可以使用instanceof。但是在ECMAScript5中,Array對象提供了更好的方式來鑒別一個變量是不是數組類型。
var a = []; var b =3; Array.isArray(a); //true Array.isArray(b); //false
注意:IE8及更早的IE不支持該方法
1.6.4原始封裝類型JavaScript中的原始封裝類型共有3種。這些特殊引用類型的存在使得原始類型用起來和對象一樣方便。當讀取字符串,數字,布爾類型時,原始封裝類型被自動創建。
var a ="qwer"; var firstChar = a.chatAt(0); console.log(firstChar);// q
在JavaScript引擎中發生了如下的過程:
var a ="qwer"; var temp = new String(a); var firstChar = temp.chatAt(0); temp =null; console.log(firstChar);// q
由于要把字符串當成對象使用,JavaScript引擎創建了一個字符串實體讓charAt可以工作,字符串對象(temp)的存在僅僅用于該語句(temp.chatAt(0)),隨后便被銷毀(temp =null)。
我們可以簡單測試一下
var a ="qwer"; a.temp ="122"; console.log(a.temp); //undefined
上面代碼的過程如下:
var a ="qwer"; var temp = new String(a); temp.temp ="122"; temp=null; var temp = new String(a); console.log(a.temp); //undefined temp=null;
由上面的代碼我們可以看到我們實際上是在一個立刻就會被銷毀的對象上而不是字符串上添加了一個新屬性。當試圖訪問這個屬性時,另一個不同的臨時對象被創建,而新屬性并不存在。雖然原始封裝類型會被自動創建,但是在這些值上進行instanceof檢查對應類型的返回值卻都是false;
var a ="1234"; var num = 10; a instanceof String //false num instanceof Number //false
這是因為臨時對象僅在值被讀取的時候創建,隨即被銷毀。instanceof操作符并沒有讀取到任何東西,也沒有臨時對象的創建,因此它告訴我們這些值并不屬于原始封裝類型;
但是我們可以手動創建原始封裝類型,但是此時使用typeof沒辦法檢測對象的實際類型,只能夠使用instanceof來檢測變量類型;
2.JavaScript 中的函數在JavaScript中函數就是對象。函數不同于其他對象的決定性特點是,函數存在一個被稱為[[Call]]的內部屬性。內部屬性無法通過代碼訪問而是定義了代碼執行時的行為。ECMAScript為JavaScript的對象定義了多種內部屬性,這些內部屬性都用雙重中括號來標注。
[[Call]]屬性是函數獨有的,表明該對象可以被執行。由于僅函數擁有該屬性,ECMAScript定義了typeof操作符對任何具有[[Call]]屬性的對象返回**function**>
2.1定義函數的兩種方式 2.1.1函數聲明函數聲明是以function關鍵字開頭,這也是區別函數聲明和函數表達式的一個重要的方法。函數聲明會在編譯期對整個作用域內的變量名字進行查詢,函數聲明的變量被提升至上下文的頂部,也就是說可以先使用函數后聲明它們。
abc(); function abc(){ console.log(2); }2.1.2函數表達式
函數表達式是function關鍵字后邊不需要加上函數的名字。這種函數被稱為匿名函數。因為函數對象本身沒有名字,所以函數表達式通常會被一個變量或者屬性引用。
abcd() var abcd=function(){ console.log(1) }; var aaa={ abc:function(){ } }
函數表達式只能通過變量引用,無法提升匿名函數的作用域。在使用函數表達式之前必須先創建它們,否則代碼會報錯。看示例代碼的運行結果:
2.2JavaScript函數的參數JavaScript函數參數與很多語言函數參數不一樣。你可以給函數傳遞任意數量的參數卻不造成錯誤。那是因為函數實際上被保存在一個被稱為arguments的類似數組的對象中。arguments可以自由增長來包含任意個數的值,這些值可以通過數字索引來引用。arguments的length屬性會告訴你目前有多少個值(函數接受了多少個參數)。
arguments是一個類數組對象,它本身并不具有JavaScript數組應該具有的全部的屬性和方法。
這里我們思考一個問題,我們怎么將一個類數組轉化為真正的數組?
最基本的我們應該想到的是創建一個原始的空數組,使用for循環將類數組中的每一項添加到新的數組中;
如果使用Zepto或者jQuery的話,會有一個toArray()的方法可以使用;
ES6有Array.from(arrayLike[, mapFn[, thisArg]])可以將類數組轉化為數組對象;
最后一種也是最高級的一種方法就是使用原型的方式;
借用原型的方式把一個類數組轉化為真正的數組的示例代碼:
function abc(){ console.log(arguments); var arrTemp = [].slice.apply(arguments); //相當于Array.prototype.slice == [].slice console.log(arrTemp); console.log(Array.isArray(arrTemp)); } abc(1,2,3);
輸出結果:
2.3函數的重載依稀的記得在學習的從C# 的時候,這些強類型語言對重載的定義:函數名相同,參數不同,或者是參數類型不同都可以叫做函數的重載。
但是在JavaScript這樣的語言中因為 arguments的存在,JavaScript的函數根本就不存在所謂的簽名,所以重載在JavaScript中實際是不存在的。
但是我們可以根據arguments傳入函數體的參數個數來模擬_函數重載_:
function abc(){ if (arguments.length ===1){ //A } if(arguments.length ===2){ //B } } abc(11); abc(11,22);
這里主要是滿足某些特殊場合的需求吧。
2.4函數使用最重要的3個點this;
apply()和call();
bind();
關于this,call,apply,bind這幾個概念在之前博客文章已經介紹過很多遍了。在這里還是做一下簡單的介紹。
2.4.1 this的使用this的指向在函數定義的時候是確定不了的,只有函數執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調用它的對象;
this指向的對象是在代碼的運行期決定的。既上面說的,誰調用了它,就指向誰。一個很簡單的總結就是,在函數中使用this,當前this指向的是當前window對象。在對象的方法中使用this,this指向的是當前對象(這個也是最容易出錯的地方)。
2.4.2 call和apply的使用關于這兩個概念,之前的博客文章也介紹多很多次。這里也簡單總結介紹一下。call和apply主要是在執行某個對象的方法的時候來改變當前this的指向。主要用在對象繼承的時候。
2.4.3 bind的使用bind也是改變對象this指向的一個方法。這個方法是ECMAScript5中新添加的一個方法。但是bind和call,apply的主要區別就是bind的第一個參數是要傳給新函數的this的值。其它所有參數代表需要被永久設置在新函數中的命名參數。可以在之后繼續設置任何非永久參數。
來看一段示例代碼:
function abc (lab){ console.log(lab + this.name); } var person1 = { name:"xiaogang" } var person2={ name:"zhiqiang21" } var sayNamePer1 =abc.bind(person1); sayNamePer1("person1"); var sayNamePer2 =abc.bind(person2,"person2"); sayNamePer2(); person2.sayName = sayNamePer1; person2.sayName("person2");
上面的代碼中:
sayNamePer1在綁定的時候沒有傳入參數,所以仍需要后續執行sayNamePer1來傳入lab參數;sayNamePer2不僅綁定了this為person2,還綁定了輸入的第一個參數是person2。意味著可以可以直接執行sayNamePer2()。最后一個是將sayNamePer1設置位person2的sayName方法。由于其this的值已經綁定,所以雖然sayNamePer1是person2的方法,但是輸出的仍然是person1.name的值。
其實總結一句話call,apply和bind的主要區別就是:
call和apply是綁定既執行。bind是有返回值的,先綁定后執行。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90892.html
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現在已經一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現將已經寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
閱讀 8892·2021-11-18 10:02
閱讀 2578·2019-08-30 15:43
閱讀 2651·2019-08-30 13:50
閱讀 1363·2019-08-30 11:20
閱讀 2701·2019-08-29 15:03
閱讀 3623·2019-08-29 12:36
閱讀 926·2019-08-23 17:04
閱讀 613·2019-08-23 14:18