javascript高階基礎的深入總結

坤坤超愛打球發表於2020-11-02

基礎總結深入

1、資料型別

分類

  1. 基本(值)型別
    • String
    • Number
    • Boolean:true|false
    • undefined:undefined
    • null:null
  2. 物件(引用)型別
    • Object:任意物件
    • Function:一種特別的物件(可以執行)
    • Array:一種特別的物件(數值下標,內部資料是有序的)

判斷

  • typeof
  • instanceof
  • ===(全等,不會做資料轉換) | ==(會做資料轉換)

1、undefined和null的區別?

undefined 代表定義未賦值

null 代表定義並賦值了,只是值為null

2、什麼時候給變數賦值為null?

起始的時候初始賦值為null,表明將要賦值為物件。

var b = null;

確定物件就賦值

b = [] || b = {}

最後,釋放資源,讓變數指向的物件成為垃圾物件(被回收)

b = null

3、嚴格區別變數型別與資料型別

變數型別

  • 基本型別
  • 物件型別

資料型別

  • 基本型別:儲存的就是基本型別的資料
  • 引用型別:儲存的是地址值

2、資料,變數與記憶體

1、什麼是資料?

儲存在記憶體中代表特定資訊的“東東”,本質上是010101…

資料的特點:可傳遞(基本型別傳遞的是指,引用型別傳遞的是地址值)、可運算

2、什麼是記憶體?

記憶體只是一種概念,現實形象的就是記憶體條了,記憶體條通電後產生的課儲存資料的空間(臨時的)

記憶體產生和死亡:記憶體條(電路板)–> 通電–> 產生記憶體空間–> 儲存資料–> 處理資料–> 斷電–> 記憶體空間和資料讀消失

一塊小記憶體的2個資料:地址值與儲存的資料。

記憶體分類:

  • 棧:全域性變數、區域性變數
  • 堆:物件(物件本身在堆空間中,但是表示物件的標識(變數名)在棧空間中)

3、什麼是變數?

可以變化的量,有變數名和變數值組成。常量是不嫩變化的量。

每個變數都對應一塊小記憶體,變數名可用來查詢對應的記憶體,變數值就是記憶體中儲存的資料。

4、前三者之間的關係

記憶體用來儲存資料的空間;

變數是記憶體的標識,

5、相關問題**

1、關於賦值與記憶體的問題?

問題:var a=xxx; a記憶體中到底儲存的是什麼?

  1. 如果xxx是基本資料,儲存的就是這個資料
  2. 如果xxx是物件,儲存的是物件的地址值
  3. 如果xxx是變數,儲存的是xxx的記憶體內容(可能是基本資料,也可能是地址值)

2、關於引用變數賦值問題?

小總結一下:關於變數賦值問題:

  • 如果變數所指向的是基本型別,則賦值是直接將儲存的值複製過去。
  • 如果變數所指向的是引用型別,則賦值是將儲存的地址值複製過去。

多個引用變數指向同一個物件,通過一個變數修改物件內部的資料,其他變數看到的是修改之後的資料。

var obj1 = {name:"wwj"}
var obj2 = obj1;
obj2.name = "Tom";
console.log(obj1.name + "---" + obj2.name);		//Tom---Tom

// ======
function fun(obj){
    obj.name = "A";
}
fun(obj2);	// 把實參obj2的值賦值給形參,即把obj2變數儲存的物件的地址值賦值給形參。
console.log(obj1.name + "---" + obj2.name);	//A---A

2個引用變數指向同一個物件,讓其中一個引用變數指向另一個物件,另一個引用變數依然指向前一個物件。

var a = {age:31}
var b = a;
a={name:"wwj", age:18};
console.log(b.age + "---" + a.name + "---" + a.age);//31---wwj---18

//==============
function fun2(obj){	
    // 說明:當呼叫fun2方法是,把實參變數a的值(即使物件的地址值)賦值給形參obj,obj為區域性變數,這時,變數a和區域性變數obj都是指向同一個物件。
    // 當區域性變數obj重新指向另一個物件時,區域性變數obj儲存的地址值改變,而變數a的值沒有改變,所以即使改變區域性變數obj所指向的物件的值,也改變不了不了a所指向的物件的值。而當函式執行完畢後,清除區域性變數obj
    obj = {age:88}
}
fun2(a);
console.log(a.name + "---" + a.age);	//wwj---18

3、在js呼叫函式時傳遞變數引數時,是值傳遞還是引用傳遞?

總結:

  • 都是值傳遞(基本型別的值|引用型別的地址值)
  • 如果變數所指向的是基本型別,則賦值是直接將儲存的值複製過去。
  • 如果變數所指向的是引用型別,則賦值是將儲存的地址值複製過去。
var a = 3;
function fun(a){
    a = a + 1;
}
fun(a)
console.log(a)	// 3

4、js引擎任何管理記憶體?

  • 記憶體生命週期
    • 分配小記憶體空間,得到它的使用權
    • 儲存資料,可以反覆進行操作
    • 釋放小記憶體空間
  • 釋放空間
    • 區域性變數:函式執行完畢自動釋放
    • 物件:成為垃圾物件==》垃圾回收期回收

3、物件

1、什麼是物件?

  • 多個資料的封裝體
  • 用來儲存多個資料的容器
  • 一個物件代表現實中的一個事物

2、為什麼要用物件?

  • 統一管理多個資料

3、物件的組成

屬性:

  • 代表現實事物的狀態資料
  • 由屬性名和屬性值組成
  • 屬性名都是字串型別,屬性值是任意型別

方法:

  • 代表現實事物的行為資料
  • 是特別的屬性==》屬性值是函式

4、任何訪問物件內部資料?

屬性名:編碼簡單,但有時不能用

[“屬性名”]:編碼麻煩,但通用

4、函式

1、什麼是函式?

實現特定功能的n條語句的封裝體

只有函式是可以執行的,其他型別的資料不能執行

2、為什麼要用函式?

提高程式碼複用

便於閱讀交流

3、如何定義函式?

函式宣告

  • function fun(){}
    

函式表示式

- ```js
var fun = function (){}

4、任何呼叫(執行)函式?

function fun(){
    test:function(){}
}

fun():直接呼叫

fun.test():通過物件呼叫

new fun():new呼叫

fun.call()|fun.apply():臨時讓某個物件呼叫

5、回撥函式

1、什麼函式才是回撥函式?

  1. 自己定義的
  2. 而且自己沒有呼叫
  3. 但是最終函式執行了
document.body.onclick = function(){
    alert("這個函式回撥了")
}

2、常見的回撥函式

  • 定時器回撥函式
    • 超時定時器
    • 迴圈定時器
  • Dom事件回撥函式
  • ajax請求回撥函式
  • 生命週期回撥函式

6、IIFE-立即執行函式

全稱:Immediately-invoked Function Expression(立刻執行函式表示式 或立刻呼叫函式表示式)

作用:

  • 隱藏實現
  • 不會汙染外部(全域性)名稱空間
  • 用來編碼js模組
// 無慘
;(function(){
    alert("立即執行了")
})()

// 有慘
;!function(obj){
    alert("立即執行了---" + obj)
}("這是個引數...")

//===========
;-function(){
    alert("立即執行了")
}()

//===========
;+function(){
    alert("立即執行了")
}()

//===========
;~function(){
    alert("立即執行了")
}()

// ...... 

;!function() {
    var obj = {
        name: "wwj"
    }
    function fun() {
        console.log(obj);
    }
    window.$ = function() { // 向外暴露一個全域性變數
        return {
            obj: obj,
            fun: fun
        }
    }
}()
// $ 是一個函式,函式返回一個物件。
$().fun();

7、函式中的this

檢視js基礎中的函式,有詳細說明;

8、語句加不加分號

沒有應該不應該,只有你自己喜歡不喜歡。

但是,有幾種不加分號,在壓縮程式碼的時候會有問題:

  1. 小括號開頭的前一條語句
  2. 中方括號開頭的前一條語句
  3. 定義變數的語句

函式高階

1、原型與原型鏈

1.1 原型-prototype

函式的prototype屬性

  • 每個函式都有一個prototype屬性,它預設指向一個Object空物件(空物件:沒有我們的自定義的屬性。並且這個Object空物件是Object物件的例項,所以有一個隱式屬性__proto__)(即稱為:原型物件)

    var obj = function() {
    }
    console.log(obj.prototype);
    obj = new obj();
    console.log(obj);
    console.log(Date.prototype);	
    
    • 當使用new 關鍵字建立一個例項是,都會有一個隱藏的屬性__proto__,下面有講解。。。
  • 原型物件中有一個屬性Constructor,它指向函式物件

    var obj = function() {
    }
    console.log(obj.prototype.constructor === obj);
    console.log(Date.prototype.constructor === Date);
    

給原型物件新增屬性(一般都是方法)

  • 作用:函式的所有例項物件自動擁有原型中的屬性(方法)

在這裡插入圖片描述

var Fun = function() {
}
Fun.prototype.test = function(){
    alert("~~~");
}
var fun= new Fun();
fun.test();

1.2 顯示原型月隱式原型

  1. 每個函式function都要一個prototype,即顯式原型(屬性)

    var Fun = function() {
    }
    console.log(Fun.prototype)
    Fun.prototype.test = function(){
        alert("~~~");
    }
    console.log(Fun.prototype)
    
  2. 每個例項物件都要一個__proto__,可稱為隱式原型(屬性)

    var Fun = function() {
    }
    Fun.prototype.test = function(){
        alert("~~~");
    }
    var fun= new Fun();
    console.log(Fun.__proto__)
    
  3. 物件的隱式原型的值為其對應建構函式的顯示原型的值

    var Fun = function() {
    }
    Fun.prototype.test = function(){
        alert("~~~");
    }
    var fun= new Fun();
    fun.test();
    // 證明~~~
    console.log(fun.__proto__ === Fun.prototype)
    
  4. 內容結構(圖)

  5. 在這裡插入圖片描述

  6. 總結:

    • 函式的prototype屬性:在定義函式時自動新增的,預設值是一個空的Object物件
    • 物件的__proto__屬性:建立物件時自動新增的,預設值為建構函式的prototype屬性值
    • 程式設計師能直接操作顯示原型,但不能直接操作隱式原型(ES6之前)

1.3 原型鏈

  1. 原型鏈(圖解):(空物件:沒有我們的自定義的屬性。並且這個Object空物件是Object物件的例項,所以有一個隱式屬性__proto__

在這裡插入圖片描述

在這裡插入圖片描述

  1. 訪問一個物件的屬性時,先在自身屬性中查詢,找到返回;如果沒有,在沿著__proto__這條鏈向上查詢,找到返回,如果最終都沒找到,則返回undefined。

  2. 別名:隱式原型鏈

  3. 作用:查詢物件的屬性(方法)

  4. 建構函式 /原型/實體物件的關係(圖解)[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳

  5. 在這裡插入圖片描述

  6. 建構函式 /原型/實體物件的關係2(圖解2)

    1. 任何的函式都是通過new Function() 建立的,無論是內建函式,還是自定義函式
    2. 例項物件的隱式原型等於建構函式的顯式原型

在這裡插入圖片描述

在這裡插入圖片描述

補充:

  • 函式的顯式原型指向的物件預設是空Object例項物件(但Object不滿足):

    console.log(Fn.prototype instanceof Object)		// true
    console.log(Object.prototype instanceof Object)		// false
    console.log(Function.prototype instanceof Object)		// true
    
  • 實際上上圖中的 Function.prototype 還有一個 __proto__屬性,並且指向Object.prototype。

  • 所有函式都是Function的例項(包括Function)

    console.log(Function.prototype ===  Function.__proto__)		// true
    
  • Object的原型物件是原型鏈的盡頭。並且__proto__的返回值為null

    console.log(Object.prototype.__proto__)		// null
    

1.3.1 屬性問題

  • 讀取物件的屬性值時:會自動到原型鏈中查詢(會先在物件本身上查詢,沒有則沿著原型鏈查詢)
  • 設定物件的屬性值時,不糊查詢原型鏈,如果當前物件中沒有此屬性,直接新增此屬性並設定其值
  • 方法一般定義在原型中,屬性一般通過建構函式定義在物件本身上。

1.4 探索instanceof

作用:判斷左邊的物件 是否是 右邊型別的例項

表示式:A instanceof B

結果:如果B函式的顯式原型物件在A物件的原型鏈上,返回true,否則返回false。

例子一:
在這裡插入圖片描述

例子二:

在這裡插入圖片描述

在這裡插入圖片描述

2、執行上下文與 執行上下為棧

1.變數提升與函式提升

  • 變數宣告提升

    • 通過var定義(宣告)的變數,在定義語句之前就可以訪問到

    • 值:undefined

    • console.log(a);	// undefined
      var a = "xx";
      // 上面的程式碼相當於是:
      var a;
      console.log(a);	// undefined
      a = "xx";
      
  • 函式宣告提升

    • 通過function宣告的函式,在定義語句之前就可以直接呼叫

    • 值:函式定義(物件)

    • b();
      function b(){
          console.log("~")
      }
      // 上面的程式碼相當於是:
      function b(){
          console.log("~")
      }
      b();
      
  • 問題:變數提升和函式提升是如何產生的?

    c();
    var c = function(){
        console.log("~")
    }
    // 上面的程式碼相當於是:
    var c;
    c();
    var c = function(){
        console.log("~")
    }
    

2.執行上下文

  1. 程式碼分類(位置)
    • 全域性程式碼
    • 函式(區域性)程式碼
  2. 全域性執行上下文:
    • 在執行全域性程式碼前將window確定為全域性執行上下文
    • 對全域性資料進行預處理
      • var定義的全域性變數==》undefined,新增為window的屬性
      • function宣告的全域性函式==》賦值(fun),新增為window的方法
      • this==》賦值(window)
    • 開始執行全域性程式碼
  3. 函式執行上下文
    • 在呼叫函式,準備執行函式體之前,建立對應的函式執行上下文物件(是一個虛擬物件,執行完後就被刪除。換一個說法就是,在定義一個方法是,不會建立一個函式執行上下文物件,只有當呼叫函式時,才後建立一個函式執行上下文物件)
    • 對區域性資料進行預處理
      • 形參變數==》賦值(實參)==》新增文執行上下文的屬性
      • arguments==》賦值(實參列表),新增為執行上下文的屬性
      • var定義的區域性變數==》undefined,新增為執行上下文的屬性
      • function宣告的函式==》賦值(fun),新增為執行上下文的方法
      • this==》賦值(呼叫函式的物件)
    • 開始執行函式體程式碼

3.執行上下文棧

  1. 在全域性程式碼執行前js引擎就會建立一個棧來儲存管理所有的執行上下文物件
  2. 在全域性執行上下文(window)確定)後,將window新增到棧中(壓棧)
  3. 當開始執行一個函式時,將該函式執行上下文物件新增到棧中(壓棧)
  4. 即使正在執行的函式裡又有多個該函式執行上下文物件,都要按照順序新增到棧中(壓棧)中並執行
  5. 在當前函式執行完畢後,將棧頂的物件移除(出棧)
  6. 如此迴圈後(第三步開始),當所有的函式執行上下文物件執行完後,棧中只有window

4.面試題

console.log("gb:", i);
var i = 1;
foo(i);
function foo(i){
    if(i == 4) return;
    console.log("fb:" + i);
    foo(i + 1);	
    console.log("fe:" + i);
}
// 上面的程式碼中有幾個上下為物件? 5
// 上面的程式碼的結果是什麼?

// ----------------------------

function a(){}
var a;
console.log(a);
// 結果是什麼?	結果為:"function"
// 實際上問的是變數提升和函式提升的問題,這兩種提升到底是誰先提升。看結果得知,是先變數提升,後函式提升

// ----------------------------

if(!(b in window)){
    var b = 1;
}
console.log(b);
// 結果為  undefined
// 為什麼?  還會變數提升問題

// ----------------------------

var c =1;
function c(c){
    console.log(c)
}
c(2);	// 報錯,提示c不是一個function
//為什麼會這樣呢?這還是變數提升和函式提升問題,程式碼的實際執行時這樣的:
var c;
function c(c){
    console.log(c)
}
c =1;
c(2);
// 先變數提升,但是變數還沒賦值,接著函式提升,在變數賦值。所以最後c是變數

3、作用域域作用域鏈

  1. 理解

    • 作用域就是一塊“地盤”,一個程式碼段所在的區域
    • 它是靜態的(相對於上下文物件),在編寫程式碼是就確定了
  2. 分類

    1. 全域性作用域

    2. 函式作用域

    3. 沒有快作用域(ES6有了)

      if(true){
          var a = 3;
      }
      console.log(a);		// 3
      
  3. 作用:

    1. 隔離變數,不同作用域下同名變數不會有衝突。

============

作用域與執行上下文:

區別1:

  • 全域性作用域之外,每個函式都會建立自已的作用域,作用域在函式定義時就已經確定了。而不是在函式呼叫時
  • 全域性執行上下文環境是在全域性作用域確定之後,j5程式碼馬上執行之前建立
  • 函式執行上下文是在呼叫函式時,函式體程式碼執行之前建立

區別2:

  • 作用域是靜態的只要函式定義好了就一直存在,且不會再變化
  • 執行上下文是動態的,呼叫函式時建立,函式呼叫結束時上下文環境就會被釋放

聯絡:

  • 執行上下文(物件)是從屬於所在的作用域
  • 全域性上下文環境==>全域性作用域
  • 函式上下文環境=>對應的函式使用域

============

作用域鏈:說白了,就是獲取物件的順序,從內到外。

4、閉包

最終理解:就是Java中類的get和set方法的應用

1.閉包的理解

1、任何產生閉包?
當一個巢狀的內部(子)函式引用了巢狀的外部(父)函式的變數(函式)時,就產生了閉包。

2、閉包到底是什麼?

  • 使用瀏覽器除錯檢視
  • 理解一:閉包是巢狀的內部函式(絕大部分人認為)
  • 理解二:包含是被引用變數(函式)的物件(極少數人認為)
  • 注意:閉包存在於巢狀的內部函式中

3、產生閉包的條件?

  • 函式巢狀
  • 內部函式引用了外部函式的資料(變數、函式)
function fun1(){
    var a = 2;
    var b = "abc";
    function fun2(){ // 執行函式定義,就會產生閉包(不用非得呼叫內部函式)
        console.log(a)
    }
}
fun1();

2.常用閉包

將函式作為另一個函式的返回值:

function fun1(){
    var a=2;
    function f(){
        a++;
        console.log(a);
    }
    return f;
}
var f = fun1();
f();	// 3
f();	// 4
// 從上面的例子中可以看出,閉包中應用的變數(函式)時沒有關閉重新定義,而是保留著引用

將函式作為實參傳遞給另一個函式呼叫:

function showDelay(msg, time){
    setTimeout(function(){
        console.log(msg);
    }, time)
}
showDelay(23, 500)

3.閉包的作用

1.使用函式內部的變數在函式執行完成後,仍然存活在記憶體中(延長了區域性變數的生命週期)

2.讓函式外部可以操作(讀寫)到函式內部的資料(變數、函式)

問題:

  1. 函式執行完後,函式內部宣告的區域性變數(函式)是否還存在? 一般是不存在,存在於閉包的變數才可能存在。
  2. 在函式外部能直接訪問函式內部的區域性變數嗎?不能,但是能通過閉包讓外部操作
function fun1() {
    var a = 2;
    var b = "abc";

    function setA(x) {
        a = x;
        console.log(a);
    }
    function getA() {
        console.log(a);
        return a;
    }
    // test
    function test() {
        console.log(a);
        return a;
    }
    return {
        setA:setA,
        getA:getA
    };
}
var f = fun1();  // 在上面的函式中,有兩個閉包(setA,getA),在函式fun1執行完後,函式中的變數一般都都會回收,但是隻有且僅只有被閉包引用時才能存在。同時,函式fun1中的閉包也會被回收,只有當且僅當閉包被引用時,閉包才能存在而不被回收(未被引用的會被回收),而被閉包引用的變數延長生命週期。
f.getA()
f.setA(343)			
f.getA()
// 死亡
f = null;

4.閉包的生命週期

產生:在巢狀內部函式定義執行完成時就產生了(不是在呼叫)

死亡:在巢狀的內部函式成為垃圾物件時

5.閉包的應用

定義js模組:

  • 具有特定功能的js檔案
  • 將所有的資料和功能封裝在衣蛾函式內部(私有的)
  • 只向外暴露一個包含n個方法的物件或函式
  • 模組的使用者,只需要通過模組暴露的物件呼叫方法來實現對應的功能

方式一(普通方式引入模組)

一個js檔案:test.js

function myModule() {
    var msg = "hello";

    function toUperCase() {
        console.log(msg.toUpperCase());
        // console.log(msg);
    }

    function toLowerCase() {
        console.log(msg.toLowerCase());
        // console.log(msg);
    }

    function setMsg(xxx) {
        msg = xxx;
        console.log(msg);
        console.log(this);
    }
    // 方式一
    return {
        toUperCase: toUperCase,
        toLowerCase: toLowerCase,
        setMsg:setMsg
    }
}

一個用於模組的html檔案:test.html

<!DOCTYPE html>
<html>	<head>	</head>
    <body>
        <script src="./test.js"></script>
        <script>
            // 方式一:
            var module = myModule();
            module.toUperCase();
            module.toLowerCase();
            module.setMsg("1345");	
            module.toUperCase();
            module.toLowerCase();
        </script>
    </body>
</html>

方式二(立即執行函式式引入模組)

一個js檔案:test.js

(function myModule(window) {
	var msg = "hello";

	function toUperCase() {
		console.log(msg.toUpperCase());
		// console.log(msg);
	}

	function toLowerCase() {
		console.log(msg.toLowerCase());
		// console.log(msg);
	}

	function setMsg(xxx) {
		msg = xxx;
		console.log(msg);
		console.log(this);
	}
	// 方式3二
	window.myModule2 = {
		toUperCase: toUperCase,
		toLowerCase: toLowerCase,
		setMsg: setMsg
	}
})(window)

一個用於模組的html檔案:test.html

<script src="./閉包應用1.js"></script>
<script>
    // 方式二
    myModule2.toUperCase();
    myModule2.toLowerCase();
    myModule2.setMsg("1345");	
    myModule2.toUperCase();
    myModule2.toLowerCase();
</script>

6.閉包的缺點

缺點:

  • 函式執行完後,函式內的區域性變數沒有釋放,佔用記憶體時間會邊長
  • 容易造成記憶體洩漏

解決:

  • 能不用閉包就不用
  • 及時釋放

7.閉包最終理解題

<script>
    function fun(n, o){
        console.log(o);
        return {
            fun: function(m){
                return fun(m, n);
            }
        }
    }
    var a = fun(0);
    a.fun(1);
    a.fun(2);
    a.fun(3);
    // ------
    var b = fun(0).fun(1).fun(2).fun(3);
    // ------
    var c = fun(0).fun(1);
    c.fun(2);
    c.fun(3);
</script>

物件高階

1、物件建立模式

1.Object建構函式模式

  • 套路:先建立空Object物件,再動態新增屬性/方法
  • 應用場景:起始時不確定物件內部資料
  • 問題:語句太多
var p = new Object();
p.name = "text";
p.age = 12;
p.....

2.物件字面量模式

  • 套路:使用{} 建立物件,同時指定屬性/方法
  • 使用場景:起始時物件內部資料是確定的
  • 問題:如果建立多個物件,有重複程式碼
var p = {
    p.name = "text",
    p.age = 12,
    p.....
}

3.工廠模式

  • 套路:通過工廠函式動態建立物件並返回
  • 適用場景:需要建立多個物件
  • 問題:物件沒有一個具體的型別,都是Object型別
<script>
    // 人的型別
    function createPerson(name, age) {
        return {
            name: name,
            age: age,
            setName: function(name) {
                this.name = name;
            }
        }
    }
    var p1 = createPerson("tom", 23);// ... 建立更多
    console.log(typeof p1)	// object

    // 學生的型別
    function createStudent(name, age) {
        return {
            name: name,
            age: age
        }
    }
    var s1 = createStudent("Low", 11);// ... 建立更多
    console.log(typeof s1) // object
</script>
// 通過上面可知,物件沒有一個具體的型別,都是Object型別

4.自定義建構函式模式

  • 套路:自定義建構函式,通過new建立物件
  • 適用場景:需要建立多個型別確定的物件
  • 問題:每個物件都要相同的資料(每個物件的屬性可能不同,但是方法是相同的),浪費記憶體
// 人的型別
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.setName = function(name){this.name = name};
}
var p1 = new Person("tom", 23);// ... 建立更多
console.log(typeof p1)	// Person

// 學生的型別
function Student(name, age) {
    this.name = name;
    this.age = age;
    this.setName = function(name){this.name = name};
}
var s1 = new Student("Low", 11);// ... 建立更多
console.log(typeof s1) // Student

5.建構函式+原型的組合模式

  • 套路:自定義建構函式,屬性在函式中初始化(在建構函式中只初始化一般屬性),方法新增到原型上
  • 使用場景:需要建立多個型別確定的物件
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.setName = function(name) {
    this.name = name
};
var p1 = new Person("tom", 23); // ... 建立更多
console.log(p1 instanceof Person) // true
console.log(p1) // true

// 學生的型別
function Student(name, age) {
    this.name = name;
    this.age = age
}
Student.prototype.setName = function(name) {
    this.name = name
};
var s1 = new Student("Low", 11); // ... 建立更多
console.log(s1 instanceof Student) // true
console.log(s1) // true

2、繼承模式

1.原型鏈繼承

  • 套路:
    • 定義父型別建構函式
    • 給父型別的原型新增方法
    • 定義子型別的建構函式
    • 建立父型別的物件賦值給子型別的原型
    • 將子型別原型的構造屬性設定為子型別
    • 給子型別原型新增方法
    • 建立子型別的物件:可以呼叫父型別的方法
  • 關鍵:子型別的原型為父型別的一個例項物件
// 父型別
function Supper(){
    this.supProp = "supper prototype"
}
Supper.prototype.showSupperProp = function(){
    console.log(this.supProp);
}

// 子型別
function Sub(){
    this.subProp = "subProp prototype"
}
// 子型別的原型為父型別的一個例項物件
Sub.prototype = new Supper();
// 將子型別原型的構造屬性設定為子型別
Sub.prototype.constructor = Sub;
Sub.prototype.showSubProp =function (){
    console.log(this.subProp);
}
var sub = new Sub();
sub.showSupperProp();
sub.showSubProp();

2.借用建構函式繼承

  • 套路
    • 定義父型別建構函式
    • 定義子型別的建構函式
    • 在子型別建構函式中呼叫父型別構造
  • 關鍵:在子型別建構函式中通用call() 呼叫父型別建構函式。
function Person(name, age){
    this.name = name;
    this.age = age;
}
function Student(name, age, price){
    Person.call(this, name, age);
    this.price = price;
}
var s1=new Student('通', 23, 442);
console.log(s1)

3.組合繼承

原型鏈+借用建構函式的組合繼承

  • 利用原型鏈實現對父型別物件的方法繼承
  • 利用call() 基於父型別建構函式初始化相同的屬性
function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.setName = function(name){
    this.name = name;
}
function Student(name, age, price){
    Person.call(this, name, age);
    this.price = price;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.setPrice = function(price){
    this.price = price;
}
var s1=new Student('通', 23, 442);
console.log(s1)

執行緒機制與事件機制

1、程式與執行緒

程式:程式的一次執行,它佔有一片獨有的記憶體空間;可以通過windows工作管理員檢視程式。

執行緒:執行緒是程式內的一個獨立執行單元,是程式執行的一個完整流程,是cpu的最小的排程單元

在這裡插入圖片描述

相關知識:

  • 應用程式必須執行在某個程式的某個執行緒上
  • 一個程式中至少有一個執行的執行緒:主執行緒,程式啟動後自動建立
  • 一個程式中也可以同時執行多個執行緒,我們會說程式是多執行緒執行的
  • 一個程式內的資料可以供其中的多個執行緒直接共享
  • 多個程式之間的資料是不能直接共享的
  • 執行緒池(thread pool):儲存多個執行緒物件的容器,實現執行緒物件的反覆利用

相關問題:

  • 何為多程式和多執行緒?
    • 多程式–一哥應用程式可以同時啟動多個例項執行
    • 多執行緒—在一個程式內,同時又多個執行緒執行
  • 比較單執行緒與多執行緒?
    • 多執行緒
      • 優點:能有效提升CPU的利用率
      • 缺點:建立多執行緒開銷、執行緒間切換開銷、死鎖與狀態同步問題
    • 單執行緒
      • 優點(順序程式設計簡單易懂)、缺點(效率低)
  • js是單執行緒
  • 瀏覽器執行是多執行緒

2、瀏覽器核心

核心:支撐瀏覽器執行的最核心的程式

不同的瀏覽器可能核心不一樣:

  • Chrome、Safari:webkit
  • FireFox:Gecko
  • IE:Trident
  • 360、搜狗等國內瀏覽器:Trident+webkit

核心由很多模組組成:
在這裡插入圖片描述

3、定時器引發的思考

在這裡插入圖片描述

4、js是單執行緒執行的

在這裡插入圖片描述

視訊連線

5、瀏覽器的事件迴圈(輪詢)模型

模型原理圖:
在這裡插入圖片描述

相關重要概念:
在這裡插入圖片描述

在這裡插入圖片描述

6、H5 Web Workers(多執行緒)

在這裡插入圖片描述

注意:在分執行緒中的全域性物件不再是window,所有在分執行緒中不可能更新介面

在這裡插入圖片描述

使用多執行緒:

<!DOCTYPE html>
<html>    <head>    </head>
    <body>
        <input type="text" placeholder="數值" id="number"/>
        <button id="bun">計算</button>
        <script>
            var input = document.getElementById("number");
            document.getElementById("bun").onclick = function(){
                var number = input.value;

                // 建立一個Worker物件。其中引數是一個js檔名路徑名
                var worker = new Worker("worker.js");
                // 繫結接受訊息的監聽
                worker.onmessage = function (event){
                    console.log("主執行緒接收分執行緒返回的資料:", event.data)

                }
                // 向分執行緒傳送資訊
                worker.postMessage(number);
                console.log("主執行緒向分執行緒傳送的資料:", number)
            }
        </script>
    </body>
</html>

被引用多執行緒的js檔案

function fiboacci(n) {
	return n <= 2 ? 1 : fiboacci(n - 1) + fiboacci(n - 2);
}

//  這個格式要固定,不能使用函式宣告
var onmessage = function (event){
	console.log("分執行緒接收主執行緒傳送的資料:", event.data)
	var number = event.data;
	// 計算(自己定義的邏輯)
	var result = fiboacci(number);
	postMessage(result);
	console.log("分執行緒向主執行緒返回資料:", result)
}

// 注意:在分執行緒中的全域性物件不再是window,所有在分執行緒中不可能更新介面

多執行緒的不知:

  • 不能跨域載入js
  • worker內程式碼不能訪問DOM(更新UI)
  • 不是沒個瀏覽器都支援這個新特性

補充

相關文章