js基礎知識再探討——箭頭函式沒有this;淺拷貝深拷貝等等
1.箭頭函式沒有this
每個函式在定義被ECMAScript解析器解析時,都會建立兩個特殊的變數:this和arguments,換句話說,每個函式都有屬於自己的this物件;
<script> window.a = 'windous' window.b = 'bbbbwindous' window.name = 'lisi' //1.塊級作用域下不會被建立this;this只有全域性物件window和常規函式才會建立 { let a = 'aaaa'; console.log(this.a); } //2物件下也不會被建立this;都只能去外層尋找 let obj1 = { name:'lihua', nicheng:this.name, } console.log(obj1.nicheng); //列印的windou的namelisi,所以物件也沒有this console.log(obj1.name);
//3.自定義常規函式的this,ECMAScript解析器會幫助我們自動建立並指向函式本身 function f1(){ console.log(this);//ECMAScript解析器會幫助我們自動建立,列印是fi(){} this.b = 'bbbbb'; //我們可以呼叫它,做一些賦值 console.log(this.b);//結果是‘bbb’ } let ff = new f1();//當我們初始化例項的時候,this就會指向例項ff console.log(ff.b);//因此我們可以通過例項名.屬性名的方式呼叫this.b,列印是‘bbb’ //4.常規函式作為物件的方法時,ECMAScript解析器會幫助我們自動建立,並指向物件 let obj2 = { name:'lihua', nicheng(){ let name = 'xiaoming'; console.log(this);//指向的是obj2 return this.name; } } console.log(obj2.nicheng());//列印lihua
//5.自定義箭頭函式時,我們知道箭頭函式和無論如何都不會被ECMAScript解析器建立this, //所以雖然也可以用this但是都是呼叫外層的this,所以這裡直接呼叫window let f2 = () => { console.log(this); //列印window } f2(); //6.箭頭函式作為物件方法時,仍然需要呼叫外層的this, //物件也沒有this,所以只能呼叫物件外面的this,那還是this //注意和常規函式做方法時區別,常規函式是由this,並被編譯器指向了物件,而箭頭函式是直接就沒有this,只能去外面找; let obj3 = { name:'lihua', nicheng:() => { console.log(this);//列印window return this.name } } console.log(obj3.nicheng());//列印lisi </script>
總結:
- 只有window和常規函式會被編譯器建立this物件,塊級作用域、物件{}、箭頭函式都不會,他們只能去外面尋找。
- 常規函式的this在自定義時指向自己或例項,在物件做方法時指向物件;
特性:
- 特性一:this是靜態的,this始終指向函式申明時外層作用域下的this值;而非箭頭函式this會隨著call和apply而改變;
- 特性二:箭頭函式不能作為建構函式,去例項化物件;
- 特性三: 不可以使用aruguments變數
- 特性四:箭頭函式在符合條件的情況下,還可以簡寫,更少書寫程式碼;
使用:
-
對於需要使用object.method()方式呼叫的函式,使用普通函式定義,不要使用箭頭函式。物件方法中所使用的this值有確定的含義,指的就是object本身。
-
其他情況下,全部使用箭頭函式。
2.var和let const區別
(1)bianliang提升
我們首先通過var
關鍵字聲明瞭name
變數。這意味著變數被提升了(記憶體空間在建立階段就被設定好了),直到程式執行到定義變數位置之前預設值都是undefined
。因為當我們列印name
變數時還沒有執行到定義變數的位置,因此變數的值保持為undefined
。
通過let
和const
關鍵字宣告的變數也會提升,但是和var
不同,它們不會被初始化。在我們宣告(初始化)之前是不能訪問它們的。這個行為被稱之為暫時性死區。當我們試圖在宣告之前訪問它們時,JavaScript 將會丟擲一個ReferenceError
錯誤。
總結,var和let、const都會變數提升,但是var會被自動初始化為undefined,但是letconst不會被自動初始化,如果沒有被初始化引用會報錯;、
(2)自動賦值
如果一個變數定義的時候沒有被賦予訪問型別,那麼自動會被定義為全域性變數var
就像這個防抖動函式裡的refresh,timer = setTimeout的時候被系統自動初始化了為var,所以如果多次執行refresh函式,有可能會被下一個函式的clearTimeout給清除掉。
這樣看全域性變數var
https://www.bilibili.com/video/BV15741177Eh?p=175
3、物件
物件是js非常重要的資料型別,所以,重點再講解一下;
物件屬性方法的定義格式
物件的屬性和方法,
- 其中屬性就是鍵值對,
- 其中方法就是鍵函式對,
具體定義格式如下圖:
定義物件的屬性沒什麼說的,定義物件的方法我們一般用第一種格式,即myFunc1,這種方式預設會創造一個匿名函式作為鍵函式對;我們知道,每個函式都有一個name屬性,這種方式定義的時候會將鍵名賦值給name屬性;
最正式的定義格式是格式二,即myFunc2,這個函式是具名函式,函式的name屬性就是函式名dirName;
最簡介的定義格式是格式三;
(2)物件的鍵
物件的鍵都是字串型別,或者symbol型別
(3)操作物件的屬性和方法
如何操作物件的屬性:
- e.myVar1
- e["myVar1"]
4、==和===
如果是基本資料型別:
- ==值相等即true,型別會被自動轉換成一種
- ===值要相等,型別也要相等,否則false
如果是引用資料型別:
- ==判斷值是否相等
- ===判斷記憶體地址是否相等;
5.引用資料型別與淺拷貝深拷貝
首先我們知道,引用資料型別儲存的都是地址;
其次,淺拷貝引用資料型別時比如說叫1層物件,淺拷貝只拷貝1層物件即只複製這個物件這一層的所有屬性並返回一個新的陣列,但如果這層物件裡面還有一個引用資料型別型別比如說2層物件,那麼只拷貝地址而不拷貝里面2層物件的值只拷貝2層物件的地址比如0xa43b;
這就導致一個問題,新返回的物件和1層物件對深層物件即2層物件的修改是會發生衝突的;
這個時候我們可以使用深拷貝,即所有深層次的物件也全都拷貝一份新值,而不是儲存地址,可以使用先JSON。stringfy()轉化為字串,再JSON.parse()轉化為兌物件的方式實現深拷貝;
當然,如果這種方式很麻煩,我們可以藉助第三方庫比如loadsh,——clone()是淺拷貝,——cloneDeep()是深拷貝;