最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS進階教程之作用域、函數(shù)、解構(gòu)賦值、數(shù)組方法

 更新時間:2025年12月15日 08:26:49   作者:Doris893  
這篇文章主要介紹了JS進階教程之作用域、函數(shù)、解構(gòu)賦值、數(shù)組方法的相關(guān)資料,通過深入理解這些概念,可以編寫出更高質(zhì)量的JavaScript代碼,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

一、作用域與作用域鏈

1.1 作用域的類型

局部作用域

  • 函數(shù)作用域:在函數(shù)內(nèi)部聲明的變量只能在函數(shù)內(nèi)部訪問
function calculate(x, y) {
  const result = x + y;  // 函數(shù)作用域內(nèi)的變量
  console.log(result);
  return result;
}
calculate(10, 5);  // 15
// console.log(result); // 報錯:result未定義
  • 塊作用域:由{}代碼塊創(chuàng)建的作用域
{
  let blockScoped = "我在塊作用域內(nèi)";
  const constantValue = "我也是";
  console.log(blockScoped); // 正常訪問
}
// console.log(blockScoped); // 報錯
// console.log(constantValue); // 報錯

// var 沒有塊作用域
if (true) {
  var globalLike = "我實際上在全局作用域";
  let trulyBlocked = "我確實在塊作用域";
}
console.log(globalLike); // 正常訪問
// console.log(trulyBlocked); // 報錯

總結(jié):

  1. let 聲明的變量會產(chǎn)生塊作用域,var 不會產(chǎn)生塊作用域
  2. const 聲明的常量也會產(chǎn)生塊作用域
  3. 不同代碼塊之間的變量無法互相訪問
  4. 推薦使用 letconst
    注:開發(fā)中 letconst 經(jīng)常不加區(qū)分的使用,如果擔(dān)心某個值會不小被修改時,則只能使用 const 聲明成常量。

全局作用域

最外層的作用域,任何地方都可以訪問

const globalVar = "我是全局變量";

function accessGlobal() {
  console.log(globalVar); // 可以訪問
  innerGlobal = "危險!隱式全局變量"; // 沒有聲明關(guān)鍵字,成為全局變量
}

accessGlobal();
console.log(innerGlobal); // 可以訪問,但不推薦這種寫法

總結(jié):

  1. window 對象動態(tài)添加的屬性默認也是全局的,不推薦!
  2. 函數(shù)中未使用任何關(guān)鍵字聲明的變量為全局變量,不推薦!?。?/li>
  3. 盡可能少的聲明全局變量,防止全局變量被污染

1.2 作用域鏈與變量查找

作用域鏈本質(zhì)上是JavaScript底層的變量查找機制:在函數(shù)被執(zhí)行時,會優(yōu)先查找當(dāng)前函數(shù)作用域中查找變量,如果當(dāng)前作用域查找不到則會依次逐級查找父級作用域直到全局作用域

let outer = "全局變量";

function outerFunction() {
  let middle = "外層函數(shù)變量";
  
  function innerFunction() {
    let inner = "內(nèi)層函數(shù)變量";
    console.log(inner);    // 1. 優(yōu)先查找當(dāng)前作用域
    console.log(middle);   // 2. 查找父級作用域
    console.log(outer);    // 3. 查找全局作用域
    // console.log(notExist); // 報錯:所有作用域都找不到
  }
  
  innerFunction();
  // console.log(inner); // 報錯:父作用域不能訪問子作用域
}

outerFunction();

總結(jié):

  1. 嵌套關(guān)系的作用域串聯(lián)起來形成了作用域鏈
  2. 相同作用域鏈中按著從小到大的規(guī)則查找變量
  3. 子作用域能夠訪問父作用域,父級作用域無法訪問子級作用域

二、內(nèi)存管理與閉包

2.1 JavaScript垃圾回收機制

內(nèi)存生命周期

  1. 內(nèi)存分配:聲明變量、函數(shù)、對象時自動分配
  2. 內(nèi)存使用:讀寫內(nèi)存,使用變量、函數(shù)等
  3. 內(nèi)存回收:不再使用的內(nèi)存由垃圾回收器自動回收

說明:

  1. 全局變量一般不會回收(關(guān)閉頁面回收)
  2. 一般情況下局部變量的值,不用了,會被自動回收掉

內(nèi)存泄漏:程序中分配的內(nèi)存由于某種原因程序未釋放或無法釋放叫做內(nèi)存泄漏

堆??臻g分配

  • 棧(操作系統(tǒng)):由操作系統(tǒng)自動分配釋放函數(shù)的參數(shù)值、局部變量等,基本數(shù)據(jù)類型放到棧里面
  • 堆(操作系統(tǒng)):一般由程序員分配釋放,若程序員不釋放,由垃圾回收機制回收,復(fù)雜數(shù)據(jù)類型放到堆里面
// 棧內(nèi)存 - 存儲基本類型和引用地址
let num = 10;      // 棧內(nèi)存
let str = "hello"; // 棧內(nèi)存

// 堆內(nèi)存 - 存儲復(fù)雜類型
let obj = {        // obj在棧,{name: "John"}在堆
  name: "John",
  age: 30
};

let arr = [1, 2, 3]; // arr在棧,[1,2,3]在堆

垃圾回收算法

引用計數(shù)法(已逐漸淘汰)

跟蹤每個值被引用的次數(shù),引用次數(shù)為0,則清除

// 嵌套引用問題
function problem() {
  let objA = {};
  let objB = {};
  
  objA.ref = objB;  // objB引用次數(shù): 1
  objB.ref = objA;  // objA引用次數(shù): 1
  
  // 即使離開作用域,引用計數(shù)不為0,無法回收
}

標記清除法(現(xiàn)代瀏覽器主要使用)

從根對象(全局對象)開始標記所有可達對象,清除未被標記的對象

2.2 閉包

閉包的基本概念

閉包 = 內(nèi)層函數(shù) + 外層函數(shù)的變量

// 基本閉包格式
function outer() {
  let a = 100
  function fn() {
    console.log(a)
  }
  return fn
}
const fun = outer()
fun()

閉包的實際應(yīng)用

// 1. 數(shù)據(jù)私有化
// 統(tǒng)計函數(shù)調(diào)用次數(shù)
function count() {
  let i = 0;
  function fn() {
    i++;
    console.log(`函數(shù)被調(diào)用了${i}次`);
  }
  return fn;
}
const fun = count();
fun(); // 函數(shù)被調(diào)用了1次
fun(); // 函數(shù)被調(diào)用了2次

閉包的內(nèi)存泄漏問題

// 可能造成內(nèi)存泄漏的示例
function leakyFunction() {
  const largeData = new Array(1000000).fill("data");
  
  return function() {
    console.log("我持有l(wèi)argeData的引用");
    // 即使largeData不再需要,由于閉包引用,無法被回收
  };
}

// 解決方法:及時釋放引用
function safeFunction() {
  const largeData = new Array(1000000).fill("data");
  
  const innerFunction = function() {
    console.log("使用數(shù)據(jù)");
  };
  
  // 使用后手動解除引用
  largeData = null;
  
  return innerFunction;
}

總結(jié):

  1. 閉包 = 內(nèi)層函數(shù) + 外層函數(shù)的變量
  2. 閉包的作用:封閉數(shù)據(jù),實現(xiàn)數(shù)據(jù)私有,外部也可以訪問函數(shù)內(nèi)部的變量
  3. 閉包可能會造成內(nèi)存泄漏

三、變量提升與函數(shù)提升

3.1 變量提升

// 變量提升示例
// 把所有var聲明的變量提升到當(dāng)前作用域的最前面
// 只提升聲明,不提升賦值
function fn() {
  console.log(num); // undefined
  var num = 10;
}
// 相當(dāng)于
function fn() {
  var num;
  console.log(num);
  num = 10;
}

總結(jié):

  1. 變量在未聲明即被訪問時會報語法錯誤
  2. 變量在聲明之前即被訪問,變量的值為 undefined
  3. let 聲明的變量不存在變量提升,推薦使用 let
  4. 變量提升出現(xiàn)在相同作用域當(dāng)中
  5. 實際開發(fā)中推薦先聲明再訪問變量

3.2 函數(shù)提升

// 函數(shù)聲明會提升
// 會把所有函數(shù)聲明提升到當(dāng)前作用域的最前面
// 只提升函數(shù)聲明,不提升函數(shù)調(diào)用
fn(); // 函數(shù)提升
function fn() {
  console.log('函數(shù)提升');
}

// 函數(shù)表達式不會提升
fun(); // 錯誤
var fun = function() {
  console.log('函數(shù)表達式');
};

總結(jié):

  1. 函數(shù)提升能夠使函數(shù)的聲明調(diào)用更靈活
  2. 函數(shù)表達式不存在提升的現(xiàn)象
  3. 函數(shù)提升出現(xiàn)在相同作用域當(dāng)中

四、函數(shù)進階

4.1 函數(shù)參數(shù)

默認參數(shù)

<script>
  // 設(shè)置參數(shù)默認值
  function sayHi(name="小明", age=18) {
    document.write(`<p>大家好,我叫${name},我今年${age}歲了。</p>`);
  }
  // 調(diào)用函數(shù)
  sayHi();
  sayHi('小紅');
  sayHi('小剛', 21);
</script>

動態(tài)參數(shù)

arguments是函數(shù)內(nèi)部的偽數(shù)組,包含所有傳遞給函數(shù)的參數(shù)。

function getSum() {
  let sum = 0;
  for (let i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  console.log(sum);
}
getSum(2, 3, 4); // 9

總結(jié):

  1. arguments 是一個偽數(shù)組
  2. arguments 的作用是動態(tài)獲取函數(shù)的實參

剩余參數(shù)

function getSum(a, b, ...arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  console.log(sum);
}
getSum(1, 2, 3, 4, 5); // 12

總結(jié):

  1. ... 是語法符號(展開運算符),置于最末函數(shù)形參之前,用于獲取多余的實參
  2. 借助 ... 獲取的剩余實參,是個真數(shù)組

展開運算符

展開數(shù)組,將數(shù)組轉(zhuǎn)換為逗號分隔的參數(shù)序列,不會修改原數(shù)組,應(yīng)用于:求數(shù)組最大(?。┲?,合并數(shù)組

// 展開數(shù)組
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 1. 求最大(小)值
console.log(Math.max(...arr1)); // 3
console.log(Math.min(...arr1)); // 1

// 2. 合并數(shù)組
const arr = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 復(fù)制數(shù)組(淺拷貝)
const arrCopy = [...arr1];

// 展開對象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // {a:1, b:2, c:3}

4.2 箭頭函數(shù)

基本語法

// 基本語法
const fn1 = () => {
  console.log(123);
};

// 有參數(shù)的箭頭函數(shù)(單個參數(shù)可省略括號)
const fn2 = x => {
  console.log(x);
};

// 只有一行代碼時,可省略大括號和return
const fn3 = x => x + x;

// 直接返回對象(需要用括號包裹)
const fn4 = (uname) => ({ uname: uname });

總結(jié):

  1. 箭頭函數(shù)屬于表達式函數(shù),因此不存在函數(shù)提升
  2. 箭頭函數(shù)只有一個參數(shù)時可以省略圓括號 ()
  3. 箭頭函數(shù)函數(shù)體只有一行代碼時可以省略花括號 {},并自動做為返回值被返回

this 指向

箭頭函數(shù)的this指向是固定的,指向外層作用域的this

<script>
    // 以前this的指向:  誰調(diào)用的這個函數(shù),this 就指向誰
    // console.log(this)  // window
    // // 普通函數(shù)
    // function fn() {
    //   console.log(this)  // window
    // }
    // window.fn()
    // // 對象方法里面的this
    // const obj = {
    //   name: 'andy',
    //   sayHi: function () {
    //     console.log(this)  // obj
    //   }
    // }
    // obj.sayHi()

    // 2. 箭頭函數(shù)的this  是上一層作用域的this 指向
    // const fn = () => {
    //   console.log(this)  // window
    // }
    // fn()
    // 對象方法箭頭函數(shù) this
    // const obj = {
    //   uname: 'pink老師',
    //   sayHi: () => {
    //     console.log(this)  // this 指向誰? window
    //   }
    // }
    // obj.sayHi()

    const obj = {
      uname: 'pink老師',
      sayHi: function () {
        console.log(this)  // obj
        let i = 10
        const count = () => {
          console.log(this)  // obj 
        }
        count()
      }
    }
    obj.sayHi()

  </script>

箭頭函數(shù)參數(shù)

箭頭函數(shù)中沒有 arguments,只能使用 ... 動態(tài)獲取實參

const getSum = (...arr) => {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum;
};
console.log(getSum(2, 3, 5)); // 10

五、解構(gòu)賦值

5.1 數(shù)組解構(gòu)

數(shù)組解構(gòu)是將數(shù)組的單元值快速批量賦值給一系列變量的簡潔語法

// 基本用法
const [max, min, avg] = [100, 60, 80];
console.log(max); // 100

// 變量交換
let a = 1, b = 2;
[b, a] = [a, b];
console.log(a, b); // 2 1

5.2 數(shù)組解構(gòu)細節(jié)

// 1. 變量多,單元值少(未匹配的變量為undefined)
const [a, b, c, d] = [1, 2, 3];
console.log(d); // undefined

// 2. 變量少,單元值多(忽略多余的值)
const [x, y] = [1, 2, 3];

// 3. 使用剩余參數(shù)收集多余的值
const [i, j, ...k] = [1, 2, 3, 4];
console.log(k); // [3, 4]

// 4. 設(shè)置默認值
const [o = 0, p = 0] = [];
console.log(o, p); // 0 0

// 5. 跳過某些值
const [e, f, , h] = [1, 2, 3, 4];
console.log(e, f, h); // 1 2 4

// 6. 多維數(shù)組解構(gòu)
const [l, m, [n, g]] = [1, 2, [3, 4]];

5.3 對象解構(gòu)

// 基本用法
const { uname, age } = { uname: 'pink老師', age: 18 };

// 變量重命名
const { uname: username, age } = { uname: 'pink老師', age: 18 };

// 解構(gòu)數(shù)組中的對象
const pig = [
  {
    name: '佩奇',
    age: 6
  }
];
const [{ name: uname, age }] = pig;

5.4 多級對象解構(gòu)

// 多級數(shù)組對象解構(gòu)
const pig = [
  {
    name: '佩奇',
    family: {
      mother: '豬媽媽',
      father: '豬爸爸',
      sister: '喬治'
    },
    age: 6
  }
];
const [{ name, family: { mother, father, sister }, age }] = pig;

5.5 多級對象解構(gòu)案例

<body>
  <script>
    // 1. 這是后臺傳遞過來的數(shù)據(jù)
    const msg = {
      "code": 200,
      "msg": "獲取新聞列表成功",
      "data": [
        {
          "id": 1,
          "title": "5G商用自己,三大運用商收入下降",
          "count": 58
        },
        {
          "id": 2,
          "title": "國際媒體頭條速覽",
          "count": 56
        },
        {
          "id": 3,
          "title": "烏克蘭和俄羅斯持續(xù)沖突",
          "count": 1669
        },

      ]
    }

    // 需求1: 請將以上msg對象  采用對象解構(gòu)的方式 只選出  data 方面后面使用渲染頁面
    // const { data } = msg
    // console.log(data)
    // 需求2: 上面msg是后臺傳遞過來的數(shù)據(jù),我們需要把data選出當(dāng)做參數(shù)傳遞給 函數(shù)
    // const { data } = msg
    // msg 雖然很多屬性,但是我們利用解構(gòu)只要 data值
    function render({ data }) {
      // const { data } = arr
      // 我們只要 data 數(shù)據(jù)
      // 內(nèi)部處理
      console.log(data)

    }
    render(msg)

    // 需求3, 為了防止msg里面的data名字混淆,要求渲染函數(shù)里面的數(shù)據(jù)名改為 myData
    function render({ data: myData }) {
      // 要求將 獲取過來的 data數(shù)據(jù) 更名為 myData
      // 內(nèi)部處理
      console.log(myData)

    }
    render(msg)

  </script>

六、數(shù)組高階方法

6.1 forEach - 遍歷數(shù)組

forEach是數(shù)組的遍歷方法,適用于遍歷數(shù)組對象

const arr = ['red', 'green', 'pink'];
arr.forEach(function(item, index) {
  console.log(item); // 元素值
  console.log(index); // 索引
});

// 使用箭頭函數(shù)簡寫
arr.forEach((item, index) => {
  console.log(item, index);
});

6.2 filter - 篩選數(shù)組

filter() 方法創(chuàng)建一個新的數(shù)組,新數(shù)組中的元素是通過檢查指定數(shù)組中符合條件的所有元素

const arr = [10, 20, 30];

// 使用函數(shù)表達式
const newArr1 = arr.filter(function(item) {
  return item >= 20;
});

// 使用箭頭函數(shù)簡寫
const newArr2 = arr.filter(item => item >= 20);
console.log(newArr2); // [20, 30]

6.4 方法對比

方法返回值是否改變原數(shù)組用途
forEachundefined遍歷數(shù)組,執(zhí)行副作用
filter新數(shù)組篩選滿足條件的元素
map新數(shù)組轉(zhuǎn)換數(shù)組元素
find第一個匹配元素查找單個元素
reduce累計值累加或轉(zhuǎn)換數(shù)組

七、最佳實踐與注意事項

7.1 分號使用規(guī)則

// 必須加分號的情況

// 1. 立即執(zhí)行函數(shù)前
(function() {
  console.log("IIFE");
})();

// 安全寫法:前面加分號
;(function() {
  console.log("安全的IIFE");
})();

// 2. 以數(shù)組開頭的代碼前
const name = "JavaScript"
;[1, 2, 3].forEach(n => console.log(n))

// 3. 以模板字符串開頭的代碼前
const greeting = "Hello"
`world`.toUpperCase() // 可能出錯

7.2 作用域最佳實踐

// 1. 優(yōu)先使用const,其次是let
const PI = 3.14159;
let counter = 0;

// 2. 避免使用var
// ? 不好
var oldVar = "過時";

// ? 好
let modernLet = "現(xiàn)代";
const modernConst = "更現(xiàn)代";

// 3. 立即執(zhí)行函數(shù)創(chuàng)建私有作用域
(function() {
  const privateVar = "外部無法訪問";
  // 模塊代碼
})();

// 4. 模塊化組織代碼
const Module = (function() {
  let privateData = "私有";
  
  return {
    publicMethod: function() {
      return privateData;
    }
  };
})();

7.3 閉包使用建議

// 1. 明確閉包的目的
function createCache() {
  const cache = new Map();
  
  return {
    set: (key, value) => cache.set(key, value),
    get: (key) => cache.get(key),
    has: (key) => cache.has(key)
  };
}

// 2. 避免不必要的閉包
function avoidClosure(arr) {
  // ? 不必要的閉包
  arr.forEach(function(item) {
    console.log(item);
  });
  
  // ? 更簡潔
  arr.forEach(item => console.log(item));
}

// 3. 注意內(nèi)存管理
function createHeavyClosure() {
  const largeData = new Array(1000000);
  
  const useData = () => {
    // 使用數(shù)據(jù)
  };
  
  // 使用完畢后解除引用
  const cleanup = () => {
    largeData.length = 0;
  };
  
  return { useData, cleanup };
}

總結(jié)

JavaScript的進階特性提供了更強大、更靈活的編程能力。通過深入理解作用域、閉包、函數(shù)進階特性等概念,可以編寫出更簡潔、更高效、更易維護的代碼。關(guān)鍵是要理解這些特性背后的原理,而不是僅僅記住語法。在實際開發(fā)中,應(yīng)該根據(jù)具體場景選擇合適的技術(shù)方案,并遵循最佳實踐,避免常見的陷阱和性能問題。

到此這篇關(guān)于JS進階教程之作用域、函數(shù)、解構(gòu)賦值、數(shù)組方法的文章就介紹到這了,更多相關(guān)JS作用域、函數(shù)、解構(gòu)賦值、數(shù)組方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

沙田区| 三台县| 五原县| 临沂市| 广东省| 呼和浩特市| 武宣县| 阿克陶县| 泸州市| 石河子市| 方正县| 安阳县| 佛冈县| 吴桥县| 江阴市| 宁国市| 呈贡县| 鲁甸县| 大姚县| 亚东县| 凤庆县| 措勤县| 泗阳县| 白城市| 镇沅| 澎湖县| 唐海县| 三江| 博乐市| 勐海县| 平陆县| 吉木乃县| 新野县| 吉安县| 临猗县| 博爱县| 大丰市| 达日县| 郧西县| 三穗县| 佳木斯市|