1. 程式人生 > 其它 >js基礎知識再探討——箭頭函式沒有this;淺拷貝深拷貝等等

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>

總結:

  1. 只有window和常規函式會被編譯器建立this物件,塊級作用域、物件{}、箭頭函式都不會,他們只能去外面尋找。
  2. 常規函式的this在自定義時指向自己或例項,在物件做方法時指向物件;

特性:

  • 特性一:this是靜態的,this始終指向函式申明時外層作用域下的this值;而非箭頭函式this會隨著call和apply而改變;
  • 特性二:箭頭函式不能作為建構函式,去例項化物件;
  • 特性三: 不可以使用aruguments變數
  • 特性四:箭頭函式在符合條件的情況下,還可以簡寫,更少書寫程式碼;

使用:

  1. 對於需要使用object.method()方式呼叫的函式,使用普通函式定義,不要使用箭頭函式。物件方法中所使用的this值有確定的含義,指的就是object本身。

  2. 其他情況下,全部使用箭頭函式。

2.var和let const區別

(1)bianliang提升

我們首先通過var關鍵字聲明瞭name變數。這意味著變數被提升了(記憶體空間在建立階段就被設定好了),直到程式執行到定義變數位置之前預設值都是undefined。因為當我們列印name變數時還沒有執行到定義變數的位置,因此變數的值保持為undefined

通過letconst關鍵字宣告的變數也會提升,但是和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()是深拷貝;