JavaScript 函數(shù)重中之重
- 函數(shù)的五種聲明方式
- 如何調(diào)用函數(shù)
- 什么是call stack
- this 和 arguments
- 作用域
- 閉包
function 函數(shù)名(參數(shù),參數(shù)2){執(zhí)行代碼,return 結(jié)果} 就像一個過濾器,放進參數(shù),輸出結(jié)果.
如何聲明一個函數(shù)?
- 具名函數(shù)
function x(輸入1,輸入2){
return undefined
}
注意: 變量可以是7種(null undefined boolean string number symbol object)類
型,但是函數(shù)變量是變量的特例.
- console.log() 括號里面只接受字符串,如果不是將會調(diào)用對象的toString方法
- 因為函數(shù)永遠有一個return返回值,所以即使是console.log()返回undefined因為console的源代碼也是用一個函數(shù)來實現(xiàn)的,函數(shù)自帶一個默認return undefined.
- console只是打印一個對象,并不是返回一個對象.undefined是來自于他的源代碼函數(shù)自帶的默認返回值
- 匿名函數(shù) var a = function (){} //這是第二種聲明方法 var a = function b(){}//這是第三種聲明方法
- 具名函數(shù)function v(){ } 與 匿名函數(shù) var a = function v(){} 的區(qū)別在于? 變態(tài)之的地方在于具名函數(shù) console.log(v) 返回函數(shù);匿名函數(shù) 報錯. 同樣的語法擺在不同的位置,意義不一樣,這就是不一致,就是垃圾
- 函數(shù)的第四種聲明是window.Function a = new Function('x','y','return + x+y')
函數(shù)第五種聲明就是箭頭函數(shù)
- sam = (x,y)=>{return x+y} 如果只有一個語句,可以去掉return和花括號,這是一起去掉的; 所以上面的函數(shù)等價于 sam= (x,y)=>x+y
- 如果參數(shù)只有一個,(括號)也可以省掉sam=x=>x*x
- 箭頭函數(shù)是匿名函數(shù),沒有名字的
- 如果花括號里面要放多個語句,那么就用分號
函數(shù)的name屬性
- 所有函數(shù)都有一個屬性,如果是具名函數(shù)name就是名字;如果是匿名函數(shù),name就是變量,如果兩邊都有的話:var f3 = function f4(){} ;f3的name是'f4'
- 如果是用new產(chǎn)生的,那么他的name就是匿名(anonymous)
數(shù)據(jù)是可以直接用的,但是函數(shù)是要調(diào)用的
函數(shù)到底是什么?
- 阮一峰說的 函數(shù)就是一堆代碼的組合,說了等于沒說.函數(shù)就是代碼塊,什么意思呢? 函數(shù)就是一段可以反復調(diào)用的代碼塊/函數(shù)還能接受輸入不同的參數(shù)返回不同的值.
函數(shù)內(nèi)存
- 函數(shù)代碼塊里面的內(nèi)容是被當成字符串存在內(nèi)存里面.
- 兩種不同聲明函數(shù)的方式,不同的內(nèi)存方式:
- 具名函數(shù): function f (x,y){return x+y} f 存一個地址,指向heap一塊內(nèi)存,這塊內(nèi)存存有2個東西.一個是params[x,y] 另外一個就是fbody{return x + y};這塊內(nèi)存又指向Function 函數(shù)對象,使用他的call方法
用內(nèi)存實現(xiàn)一個函數(shù)是怎樣的呢?//eval 這是一個把字符串當成代碼執(zhí)行的函數(shù)
利用內(nèi)存原理,寫出函數(shù)的代碼.
var f = {}
f.name = 'f'
f.params= ['x','y']//這個是假設(shè)的的屬性
f.functionBody='console.log('xxxx')'
f.call = function(){return window.eval(f.functionBody)}
//這里穿插一個知識點,為什么要用函數(shù)包裝Window......這些代碼給f.call而不是直接用? 個人認為應(yīng)該是用函數(shù)有利于擴展,日后更改代碼可以直接在函數(shù)內(nèi)增刪改查代碼,而直接賦值就沒有這個功能,而且直接賦值直接給一個值,而不是一個函數(shù)地址,兩者的內(nèi)存不一樣.
// 這樣就清楚了f 與f.call的區(qū)別了,f只是一個函數(shù),f.call是執(zhí)行這個函數(shù)體
那么函數(shù)的本質(zhì)我們就知道了:可以執(zhí)行代碼的對象就是函數(shù)
- 那么f.call(硬核hardcore) 為什么不用f(糖sweet)? // 實際上f.call才是JavaScript真正的用法,f()是給小白用的,這對于領(lǐng)悟this有很大的意義
- f.call()怎么用???
什么是this? f.call(undefined,1,2) undefined就是this;[1,2]就是arguments
// call的第一個參數(shù)可以用this得到,call后面的參數(shù)可以用arguments得到
- 在普通模式下,f.call(undefined)這個undefined//也就是this,默認是Window
- 也就是說f.call(第一個參數(shù)就是this)// JavaScript中的this 就是因為當年JavaScript研發(fā)的時候,老板要求開發(fā)的語言像java,這個歷史原因造成的
- arguments是一個偽數(shù)組: 偽數(shù)組就是具有數(shù)組鍵值對的形式,還有l(wèi)engh,但是沒有指向Array原型,也就是說一個對象的proto(甚至整個原型鏈)沒有指向Array.prototype,所以它缺失了真正數(shù)組所具備的一些屬性,比如push.
- 一句話總結(jié),call的第一個屬性就是this,其他后面的就是arguments,因為歷史原因本來一個argument搞定,但是強行搞了一個this來.
JavaScript函數(shù)調(diào)用,stack內(nèi)存與heap內(nèi)存的過程
- 多層函數(shù)調(diào)用(穿越時空)就相當于有存檔的走迷宮
- 遞歸函數(shù)的調(diào)用,就像有存檔的樓梯探險
- 當stack內(nèi)存被爆掉
JavaScript中的作用域就是樹
-注意一句很錯誤的話:沒有用var 聲明的變量,a =3 聲明的就是全局作用域的變量a,這是很錯誤的話. 首先a=3是一個賦值語句,他是賦值給a,但是當前作用域沒有,就會沿著作用域鏈網(wǎng)上找,直到找到a賦值給他.如果沒有a,也就是說整個作用域都沒有,就會聲明一個a并賦值給他,這時候才是叫 聲明了一個全局作用域a,并賦值3給他.
- 穿越時空,就近原則.