日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JS 总结之函数、作用域链

發布時間:2025/3/21 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JS 总结之函数、作用域链 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 JavaScript 中,函數實際上是一個對象。

? 聲明

JavaScript 用 function 關鍵字來聲明一個函數:

function fn () {} 復制代碼

變體:函數表達式:

var fn = function () {} 復制代碼

這種沒有函數名的函數被稱為匿名函數表達式。

?? return

函數可以有返回值

function fn () {return true } 復制代碼

位于 return 之后的任何代碼都不會執行:

function fn () {return trueconsole.log(false) // 永遠不會執行 } fn() // true 復制代碼

沒有 return 或者只寫 return,函數將返回 undefined:

function fn () { } fn() // undefined // 或者 function fn () {return } fn() // undefined 復制代碼

? 參數

函數可以帶有限個數或者不限個數的參數

// 參數有限 function fn (a, b) {console.log(a, b) } // 參數不限 function fn (a, b, ..., argN) {console.log(a, b, ..., argN) } 復制代碼

沒有傳值的命名參數,會被自動設置為 undefined

// 參數有限 function fn (a, b) {console.log(b) // undefined } fn(1) 復制代碼

? arguments

函數可以通過內部屬性 arguments 這個類數組的對象來訪問參數,即便沒有命名參數

// 有命名參數 function fn (a, b) {console.log(arguments.length) // 2 } fn(1, 2)// 無命名參數 function fn () {console.log(arguments[0], arguments[1], arguments[2]) // 1, 2, 3 } fn(1, 2, 3) 復制代碼

?? 長度

arguments 的長度由傳入的參數決定,并不是定義函數時決定的。

function fn () {console.log(arguments.length) // 3 } fn(1, 2, 3) 復制代碼

如果按定義函數是決定個的,那么此時的 arguments.length 應該為 0 而不為 3。

? 同步

arguments 對象中的值會自動反應到對應的命名參數,可以理解為同步,不過并不是因為它們讀取了相同的內存空間,而只是保持值同步而已

function fn (a) {console.log(arguments[0]) // 1a = 2console.log(arguments[0]) // 2arguments[0] = 3console.log(a) // 3 } fn(1) 復制代碼

嚴格模式下,重寫 arguments 的值會導致錯誤。

? callee

通過 callee 這個指針訪問擁有這個 arguments 對象的函數

function fn () {console.log(arguments.callee) // fn } fn() 復制代碼

? 類數組

長的跟數組一樣,可以通過下標訪問,如 arguments[0],卻無法使用數組的內置方法,如 forEach 等:

function fn () {console.log(arguments[0], arguments[1]) // 1, 2console.log(arguments.forEach) // undefined } fn(1, 2) 復制代碼

通過對象那章知道,可以用 call 或者 apply 借用函數,所以 arguments 可以借用數組的內置方法:

function fn () {Array.prototype.forEach.call(arguments, function (item) {console.log(item)}) } fn(1, 2) // 1 // 2 復制代碼

對于如此詭異的 arguments,我覺得還是少用為好。

? this、 prototype

具體查看總結:

  • 《關于 this 應該知道的幾個點》
  • 《原型》

? 按值傳遞

引用《JavaScript 高級程序設計》4.1.3 的一句話:

ECMAScript 中所有函數的參數都是按值傳遞的,也就是說,把函數外部的值復制給函數內部的參數,就和把一個變量復制到另一個變量一樣。

? 基本類型的參數傳遞

基本類型的傳遞很好理解,就是把變量復制給函數的參數,變量和參數是完全獨立的兩個個體:

var name = 'jon' function fn (a) {a = 'karon'console.log('a: ', a) // a: karon } fn(name) console.log('name: ', name) // name: jon 復制代碼

用表格模擬過程:

棧內存堆內存
name, ajon

將 a 復制為其他值后:

棧內存堆內存
namejon
akaron

? 引用類型的參數傳遞

var obj = {name: 'jon' } function fn (a) {a.name = 'karon'console.log('a: ', a) // a: { name: 'karon' } } fn(obj) console.log(obj) // name: { name: 'karon' } 復制代碼

嗯?說好的按值傳遞呢?我們嘗試把 a 賦值為其他值,看看會不會改變了 obj 的值:

var obj = {name: 'jon' } function fn (a) {a = 'karon'console.log('a: ', a) // a: karon } fn(obj) console.log(obj) // name: { name: 'jon' } 復制代碼

? 真相浮出水面

參數 a 只是復制了 obj 的引用,所以 a 能找到對象 obj,自然能對其進行操作。一旦 a 賦值為其他屬性了,obj 也不會改變什么。

用表格模擬過程:

棧內存堆內存
obj, a引用值{ name: 'jon' }

參數 a 只是 復制了 obj 的引用,所以 a 能找到存在堆內存中的對象,所以 a 能對堆內存中的對象進行修改后:

棧內存堆內存
obj, a引用值{ name: 'karon' }

將 a 復制為其他值后:

棧內存堆內存
obj引用值{ name: 'karon' }
a'karon'

因此,基本類型和引用類型的參數傳遞也是按值傳遞的

? 作用域鏈

理解作用域鏈之前,我們需要理解執行環境變量對象

? 執行環境

執行環境定義了變量或者函數有權訪問的其它數據,可以把執行環境理解為一個大管家。

執行環境分為全局執行環境函數執行環境,全局執行環境被認為是 window 對象。而函數的執行環境則是由函數創建的。

每當一個函數被執行,就會被推入一個環境棧中,執行完就會被推出,環境棧最底下一直是全局執行環境,只有當關閉網頁或者推出瀏覽器,全局執行環境才會被摧毀。

? 變量對象

每個執行環境都有一個變量對象,存放著環境中定義的所有變量和函數,是作用域鏈形成的前置條件。但我們無法直接使用這個變量對象,該對象主要是給 JS 引擎使用的。具體可以查看《JS 總結之變量對象》。

? 作用域鏈的作用

作用域鏈屬于執行環境的一個變量,作用域鏈收集著所有有序的變量對象,函數執行環境中函數自身的變量對象(此時稱為活動對象)放置在作用域鏈的最前端,如:

scope: [函數自身的變量對象,變量對象1,變量對象2,..., 全局執行環境的變量對象] 復制代碼

作用域鏈保證了對執行環境有權訪問的所有變量和函數的有序訪問

var a = 1 function fn1 () {var b = 2console.log(a,b) // 1, 2function fn2 () {var c = 3console.log(a, b, c) // 1, 2, 3}fn2() } fn1() 復制代碼

對于 fn2 來說,作用域鏈為: fn2 執行環境fn1 執行環境全局執行環境 的變量對象(所有變量和函數)。

對于 fn1 來說,作用域鏈為: fn1 執行環境全局執行環境 的變量對象(所有變量和函數)。

總結為一句:函數內部能訪問到函數外部的值,函數外部無法范圍到函數內部的值。引出了閉包的概念,查看總結:《JS 總結之閉包》

? 箭頭函數

ES6 新語法,使用 => 定義一個函數:

let fn = () => {} 復制代碼

當只有一個參數的時候,可以省略括號:

let fn = a => {} 復制代碼

當只有一個返回值沒有其他語句時,可以省略大括號:

let fn = a => a// 等同于 let fn = function (a) {return a } 復制代碼

返回對象并且沒有其他語句的時候,大括號需要括號包裹起來,因為 js 引擎認為大括號是代碼塊

let fn = a => ({ name: a })// 等同于 let fn = function (a) {return { name: a } } 復制代碼

箭頭函數的特點:

  • 沒有 this,函數體內的 this 是定義時外部的 this
  • 不能被 new,因為沒有 this
  • 不可以使用 arguments,可以使用 rest 代替
  • 不可以使用 yield 命令,因此箭頭函數不能用作 Generator 函數。
  • ? 參考

    • 《JavaScript 深入之參數按值傳遞》 by 冴羽
    • 《ECMAScript 6 入門》函數的擴展 - 箭頭函數 by 阮一峰
    • 《JavaScript 高級程序設計》3 基本概念、4.1.3 傳遞參數、4.2 執行環境及作用域

    轉載于:https://juejin.im/post/5c2075f7e51d451611220c45

    總結

    以上是生活随笔為你收集整理的JS 总结之函数、作用域链的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。