js基礎文件

yuey6670發表於2024-06-17

資料型別

資料分為基本資料型別(String, Number, Boolean, Null, Undefined,Symbol)和物件資料型別。

基本資料型別的特點:直接儲存在棧(stack)中的資料
引用資料型別的特點:儲存的是該物件在棧中引用,真實的資料存放在堆記憶體裡

引用資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。當直譯器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

深複製和淺複製的區別

1.淺複製只複製物件的頂層屬性(頂層屬性"通常指的是物件直接擁有的那些屬性,而不是透過引用指向的其他物件或陣列,對於一個包含多個層級的物件,淺複製會複製這個物件本身的屬性(第一層屬性),但如果是屬性值為引用型別(比如物件或陣列),它只會複製這些引用型別值的引用,而不是它們的實際內容。這意味著原始物件和複製物件將共享這些引用型別值。)

2.深複製會複製物件的所有頂層屬性

3.淺複製的速度通常比深複製快

=區別

==(相等運算子)

  • 進行值的比較,但在比較前會進行型別轉換,也被稱為“抽象相等比較”。
  • 如果比較的兩個值型別不同,JavaScript會嘗試將它們轉換成相同的型別再進行比較。
  • 例如,數字5和字串"5"用 ==比較會返回 true,因為JavaScript會嘗試將字串"5"轉換成數字5再進行比較。、

===(恆等運算子)

  • 同時進行值和型別的比較,也被稱為“嚴格相等比較”。
  • 如果比較的兩個值型別不同,===會直接返回 false,不會進行型別轉換。
  • 例如,數字5和字串"5"用 ===比較會返回 false,因為它們的型別不同。

閉包

方法裡返回一個方法

存在的意義:1.延長變數的生命週期,2.創造私有環境

應用場景:

1.防抖和節流

防抖(多次觸發,只執行最後一次)

高頻率觸發的事件,在指定的單位時間內,只響應最後一次,如果在指定的時間內再次觸發,則重新計算時間
防抖類似於英雄聯盟回城6秒,如果回城中被打斷,再次回城需要再等6秒。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>防抖函式</title>
</head>
<body>
    <div>
        <label for="inp"></label><input type="text" id="inp">
    </div>
    <script>
        //封裝防抖函式
        function debounce(fn,time){
            // 建立一個標記用來存放定時器的返回值
            let timeout=null
            return function(){
                // 每當使用者觸發input事件  把前一個 setTimeout 清楚掉
                clearTimeout(timeout)
                // 然後又建立一個新的 setTimeout, 這樣就能保證輸入字元後等待的間隔內 還有字元輸入的話,就不會執行 setTimeout裡面的內容
                timeout=setTimeout(()=>{
                    // 這裡進行防抖的內容
                    fn()
                },time)
            }
        }
        // 獲取inpt元素
        const inp=document.getElementById('inp');
        // 測試防抖臨時使用的函式
        function sayHi(){
            console.log('防抖成功')
        }
        // 給inp繫結input事件  呼叫封裝的防抖函式  傳入要執行的內容與間隔事件 
        inp.addEventListener('input',debounce(sayHi,1000))
    </script>
</body>
</html>

節流(規定時間內,只觸發一次)

高頻率觸發的事件,在指定的單位時間內,只響應第一次
節流類似於英雄聯盟裡的英雄平A 一定是內點選多次只進行攻擊一次

原型和原型鏈

非同步函式

setTimeout

setTimeout() 是屬於 window 的方法,該方法用於在指定的毫秒數後呼叫函式或計算表示式。

setTimeout(要執行的程式碼, 等待的毫秒數)

clearTimeout

clearTimeout() 方法可取消由setTimeout方法設定的定時操作。

clearTimeout() 方法的引數必須是由 setTimeout() 返回的 ID 值。

const myVar;
 
function myFunction() {
    myVar = setTimeout(function(){ alert("Hello"); }, 3000);
}
 
function myStopFunction() {
    clearTimeout(myVar);
}

ES6新特性

塊級作用域的變數宣告:let、const

let a=10;
const b=20;

箭頭函式

const add=(x,y)=>x+y;

模板字串

使用反引號(```)建立多行字串和內嵌表示式

const name=`Alice`
const greeting=`hello,${name}!`

解構賦值

允許從陣列或物件中提取資料,並將其賦值給變數

let [x,y]=[1,2]
let { name , age } = { name: 'Alice',age: 25}

預設引數

允許為函式引數指定預設值。

function greet(name = 'Guest') {
  console.log(`Hello, ${name}!`);
}

展開運算子

用於展開陣列或物件的元素

let arr=[1,2,...[3,,4,5]] //[1, 2, 3, 4, 5]
// 物件展開
let obj1 = { a: 1, b: 2 }
let obj2 = { ...obj1, c: 3 }

簡寫物件屬性和方法

可以簡潔地定義物件的屬性和方法

let x = 1, y = 2
let obj = { x, y, greet() { console.log('Hello'); } }

模組化

使用 exportimport關鍵字支援模組化開發


// 匯出模組
export const pi = 3.14;
export function add(a, b) { return a + b; }

// 匯入模組
import { pi, add } from './math';

引入了基於類的物件導向程式設計語法

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    console.log(`Hello, ${this.name}!`);
  }
}

let person = new Person('Alice');
person.greet();

Promise

用於處理非同步操作的更強大和靈活的方式

then函式非同步執行

let promise = new Promise((resolve, reject) => {
  // 非同步操作
  if (/* 成功 */) {
    resolve('Success');
  } else {
    reject('Error');
  }
});
promise.then(result => console.log(result)).catch(error => console.log(error));

交換兩值變數(不借助第三變數)

let a=1
let b=1
[a,b]=[b,a]   //解構知識點

快速去重

let arr=[1,2,2,3,4,5]
let item=[...new Set(arr)]
console.log(item)

面試題

同步和非同步區別

基礎面試題

字串拼接問題

當使用加號(+)運算子連線一個數字和一個字串時,JavaScript會將數字轉換為字串,然後進行字串拼接。

當使用減號(-)運算子時,JavaScript嘗試從兩個運算元中計算數值結果。如果其中一個運算元是字串,JavaScript會嘗試將該字串轉換為一個數字。

console.log(1+'2') // 12
console.log(1-'2') // -1

var、let、const區別

varletconst三者區別可以圍繞下面五點展開:

(1)變數宣告提升

🐖:變數提升(Hoisting)是JavaScript中一個常見的行為,它會將變數和函式宣告提升到其所在作用域的頂部。

var宣告的變數會被提升到函式或全域性作用域的頂部,但其賦值不會被提升,即變數可以在宣告之前呼叫,值為 undefined。

let和const宣告的變數也會被提升,但它們在實際賦值前不能訪問,處於“暫時性死區”,即它們所宣告的變數一定要在宣告後使用,否則報錯。

console.log(a) //undefined
var a = 10

console.log(b) // Cannot access 'b' before initialization
let b=10

console.log(c) //   Cannot access 'c' before initialization
const c=10

(2)暫時性死區

var不存在暫時性死區

letconst存在暫時性死區,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數

// var
console.log(a)  // undefined
var a = 10

// let
console.log(b)  // Cannot access 'b' before initialization
let b = 10

// const
console.log(c)  // Cannot access 'c' before initialization
const c = 10

(3)塊級作用域

var不存在塊級作用域

letconst存在塊級作用域

// var
{
    var a = 20
}
console.log(a)  // 20

// let
{
    let b = 20
}
console.log(b)  // Uncaught ReferenceError: b is not defined

// const
{
    const c = 20
}
console.log(c)  // Uncaught ReferenceError: c is not defined

(4)重複宣告

var允許重複宣告變數

letconst在同一作用域不允許重複宣告變數

// var
var a = 10
var a = 20 // 20

// let
let b = 10
let b = 20 // Identifier 'b' has already been declared

// const
const c = 10
const c = 20 // Identifier 'c' has already been declared

(5)修改宣告的變數

varlet可以

const宣告一個只讀的常量。一旦宣告,常量的值就不能改變

// var
var a = 10
a = 20
console.log(a)  // 20

//let
let b = 10
b = 20
console.log(b)  // 20

// const
const c = 10
c = 20
console.log(c) // Uncaught TypeError: Assignment to constant variable

如何選擇哪種宣告

能用const的情況儘量使用const,其他情況下大多數使用let,避免使用var

箭頭函式和普通函式的區別

1.語法區別

    function add(a,b){
        return a+b;
    }
    const add=(a,b)=>{return a+b;}

2.this繫結

// 普通函式  
const obj1 = {
        name: 'john',
        greet: function () {
            console.log("hello, "+this.name)
        }
    }
obj1.greet() // hello, john
// 箭頭函式
const obj2={
        name: 'john',
        greet:()=>{ 
            console.log(this.name)
        }
    }
    obj2.greet()  // 空

3.arguments物件、args剩餘引數區別

    function sum(){
        let res=0;
        for(let i=0;i<arguments.length;i++){
            res+=arguments[i];
        }
        return res;
    }
    sumArray=(...args)=>{ // 剩餘函式
        let res=0;
        /*
        * for(let arg of args){
        *  res+=arg;
        * }
        **/
        for(let i=0;i<args.length;i++){
            res+=args[i];
        }
        return res;
    }
    console.log(sum(1,2,3,4,5))
    console.log(sumArray(1,2,3,4,5))

4.建構函式

箭頭函式不能作為建構函式

    function Person(name) {
        this.name = name;
    }
    const john = new Person("John");
    console.log(john.name); // 輸出: John

    const PersonArrow = (name) => {
        this.name = name; // 這裡會丟擲錯誤
    };
    const alice = new PersonArrow("Alice");

null和underfined

null是物件型別,underfined是underfined型別

四種情況是underfined

1.已經宣告,但為賦值

let a
console.log(a)

2.物件某個型別不存在

let obj={}
console.log(obj.a)

3.函式呼叫,引數空缺

function get(a,b){
	console.log(a,b)
}

4.常規函式(不包括建構函式)預設返回值是undefined

function get(){
	console.log(a,b)
}
a=get()

mvvm和mvc

v-if和v-show的區別

v-if:不滿足條件,不會渲染dom,單次判斷

v-show:隱藏dom,多次切換,不能用於許可權操作

nextTick

單頁與多頁的區別及優缺點

單頁應用:只有一個主頁面的應用

元件=>頁面片段

跳轉=>重新整理區域性資源

場景=>pc端

優點

a.體驗好,快

b.改變內容,不用載入整個頁面

c.前後端分離

d.效果可以很酷炫

缺點:

a.不利於SEO

b.初次載入比較慢

c.頁面複雜度很高

v-if和v-for

不能同時使用,vue2中v-for的優先順序比v-if高,vue3中v-if的優先順序高於v-if

Vue-router與location.href的區別

location.href:簡單方便,重新整理頁面(跳外鏈)

vue-router:實現了按需載入,減少了dom消耗(內部頁面),js原生history

事件委託

事件的三個階段:1.事件捕獲階段,2.事件目標階段,3.事件泡階段

  • 事件捕獲:當某個元素觸發某個事件(如onclick),頂層物件document就會發出一個事件流,隨著DOM樹的節點向目標元素節點流去,直到到達事件真正發生的目標元素。在這個過程中,事件相應的監聽函式是不會被觸發的。
  • 事件目標:當到達目標元素之後,執行目標元素該事件相應的處理函式。如果沒有繫結監聽函式,那就不執行。
  • 事件冒泡:從目標元素開始,往頂層元素傳播。途中如果有節點繫結了相應的事件處理函式,這些函式都會被觸發。

優點

使用事件委託對於web應用程式帶來的幾個優點:
  1.管理的函式變少了。不用為每個元素都新增監聽函式。同一個父節點下面類似的子元素,可以透過委託給父元素的監聽函式來處理事件。
  2.可以方便地動態新增和修改元素,不需要因為元素的改動而修改事件繫結。
  3.JavaScript和DOM節點之間的關聯變少了,這樣也就減少了因迴圈引用而帶來的記憶體洩漏發生的機率。

事件迴圈

1.JS是單執行緒,防止程式碼阻塞,我們把程式碼(任務)分為:同步和非同步
2.同步程式碼給js引擎執行,非同步程式碼交給宿主環境
3.同步程式碼放入執行棧中,非同步程式碼等待時機成熟送入任務佇列排隊
4.執行棧執行完畢,會去任務佇列看是否有非同步任務,有就送到執行棧執行,反覆迴圈檢視執行,這個過程是事件迴圈(eventloop)

執行順序:

  • 一開始整個指令碼作為一個宏任務執行
  • 執行過程中同步程式碼直接執行,宏任務進入宏任務佇列,微任務進入微任務佇列
  • 當前宏任務執行完出隊,檢查微任務列表,有則依次執行,直到全部執行完
  • 執行瀏覽器UI執行緒的渲染工作
  • 檢查是否有Web Worker任務,有則執行
  • 執行完本輪的宏任務,回到2,依次迴圈,直到宏任務和微服務佇列都為空

微任務包括: MutationObserverPromise.then()或reject()Promise為基礎開發的其它技術,比如fetch APIV8的垃圾回收過程、Node獨有的process.nextTick

宏任務包括scriptscriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

理解 JavaScript 的事件迴圈往往伴隨著宏任務和微任務、JavaScript 單執行緒執行過程及瀏覽器非同步機制等相關問題,而瀏覽器和 NodeJS 中的事件迴圈實現也是有很大差別。熟悉事件迴圈,瞭解瀏覽器執行機制將對我們理解 JavaScript 的執行過程和排查執行問題有很大幫助。

註釋

在所有任務開始的時候,由於宏任務中包括了 script,所以瀏覽器會先執行一個宏任務,在這個過程中你看到的延遲任務(例如 setTimeout)將被放到下一輪宏任務中來執行。

Promise本身是同步的,then、catch的回撥函式是非同步的微任務

執行順序是:1.同步任務,2.微任務的非同步任務(promise),3.宏任務的非同步程式碼(settimeout,setinterval)

事件 宏任務 微任務
誰發起的 宿主(Node、瀏覽器) JS引擎
具體事件 1.script
2.setTimeout
3.setInterval
4.UI rendering(UI事件)
5.postMessage
6.MessageChannel
7.setImmediate
8.I/O(Node.js)
1.Promise.then() / catch()
2.MutaionObserver
3.Proxy
4.process.nextTick(Node.js)
5.Async / Await
誰先執行 後執行 先執行
會觸發新一輪Tick嗎 不會

改變this的函式

call、bind、apply函式