函數(shù)
- 函數(shù)也是一個(gè)對象;
- 函數(shù):把一個(gè)或者多功能通過
函數(shù)的方式封裝起來
嘉汰,對外只提供一個(gè)簡單的函數(shù)接口丹禀;
- 聲明定義函數(shù)的三種方式:
- 利用函數(shù)關(guān)鍵字
function
自定義函數(shù);
-
函數(shù)表達(dá)式
鞋怀,用一個(gè)變量接收函數(shù)表達(dá)式双泪;
-
箭頭函數(shù)
,用一個(gè)變量接收定義的箭頭函數(shù)密似;
<script>
///1.function關(guān)鍵字
///addNum是一個(gè)函數(shù)
function addNum(a,b) {
return a + b
}
addNum(100,200)
///2.函數(shù)表達(dá)式
///func是變量名 不是函數(shù)名 后面的表達(dá)式才是函數(shù) 屬于匿名函數(shù)
var func = function(x) {
console.log(x)
}
func('yanzi')
///3.箭頭函數(shù)
var func2 = () => {
console.log('我是一個(gè)箭頭函數(shù)');
}
func2();
</script>
<script>
//定義聲明函數(shù)
function getSum() {
var sum = 0
for (var i = 0; i < 100; i++) {
sum += i
}
console.log(sum);
}
//調(diào)用函數(shù)
getSum()
</script>
函數(shù)的參數(shù)
-
行參
:形式上的參數(shù)焙矛,函數(shù)定義的時(shí)候傳遞的參數(shù) 當(dāng)前并不知道是什么;
-
實(shí)參
:實(shí)際上的參數(shù)残腌,函數(shù)調(diào)用的時(shí)候傳遞的參數(shù) 實(shí)參是傳遞給行參的村斟;
- 函數(shù)的參數(shù)可以有贫导,也可以沒有個(gè)數(shù)不限;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
//定義聲明函數(shù)
function getSum(num1,num2) {
console.log(num1 + num2)
}
//調(diào)用函數(shù)
getSum(100,200)
</script>
</head>
<body>
</body>
</html>
- 函數(shù)形參實(shí)參的個(gè)數(shù)匹配問題:
- 如果實(shí)參的個(gè)數(shù)和形參的個(gè)數(shù)一致蟆盹,則正常輸出結(jié)果孩灯;
- 如果實(shí)參的個(gè)數(shù)多于形參的個(gè)數(shù),會取到形參的個(gè)數(shù)逾滥;
- 如果實(shí)參的個(gè)數(shù)小于形參的個(gè)數(shù)钱反,多于的形參定義為undefined,最終的結(jié)果就是NaN匣距;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
//定義聲明函數(shù)
function getSum(num1,num2) {
console.log(num1 + num2)
}
getSum(100,200) //300
getSum(100,200,300) //300
getSum(100) //NaN
</script>
</head>
<body>
</body>
</html>
箭頭函數(shù)的參數(shù)與參數(shù)默認(rèn)值
- 箭頭函數(shù)中只有一個(gè)參數(shù)時(shí)面哥,可以省略();
- 箭頭函數(shù)中代碼只有一行時(shí)毅待,可以省略{}尚卫;
- 參數(shù)沒有傳入實(shí)參時(shí),參數(shù)默認(rèn)值才會生效尸红;
<script>
const fn = (a,b) => {
console.log('a =',a);
console.log('b =',b);
}
//省略() 與 {}
const fn1 = a => console.log('a =',a);
//參數(shù)默認(rèn)值
const fn2 = (a,b,c = 30) => {
console.log('a =',a);
console.log('b =',b);
console.log('c =',c);
}
</script>
Object對象作為函數(shù)參數(shù)
- Object對象可以作為函數(shù)的參數(shù)袁稽;
<script>
function fn(a) {
console.log('a =', a);
}
let obj = { name: 'yanzi' };
fn(obj);
</script>
- Object對象字面量作為函數(shù)參數(shù)默認(rèn)值旁赊,函數(shù)多次調(diào)用,會多次創(chuàng)建新的Object對象字面量;
- 自定義Object對象作為函數(shù)參數(shù)默認(rèn)值洗做,函數(shù)多次調(diào)用,都使用的同一個(gè)自定義Object對象巷怜;
<script>
function func(a = { name: '大神' }) {
console.log(a.name);
a.name = '小神';
console.log(a.name);
}
func(); // 大神 小神
func(); // 大神 小神
let obj = { name: '大神' }
function func1(a = obj) {
console.log(a.name);
a.name = '小神';
console.log(a.name);
}
func1(); // 大神 小神
func1(); // 小神 小神
</script>
函數(shù)作為函數(shù)參數(shù)
- 自定義函數(shù)呢袱,匿名函數(shù)表達(dá)式,箭頭函數(shù)作為函數(shù)的參數(shù)墩莫;
<script>
function func(a) {
console.log('a =', a);
}
//自定義函數(shù)
function func1() {
console.log('自定義函數(shù)');
}
func(func1);
//匿名函數(shù)表達(dá)式
func(function () {
console.log('匿名函數(shù)表達(dá)式');
})
//箭頭函數(shù)
func(() => {
console.log('箭頭函數(shù)');
})
</script>
arguments的使用
- 當(dāng)我們不確定有多少個(gè)參數(shù)傳遞的時(shí)候芙委,可以用arguments來獲取,在JavaScript中狂秦,arguments實(shí)際上它是 當(dāng)前函數(shù)的一個(gè)
內(nèi)置對象
灌侣,所有函數(shù)都內(nèi)置了一個(gè)arguments對象,arguments對象中存儲了傳遞的所有實(shí)參
裂问;
- arguments展示形式是一個(gè)
偽數(shù)組
侧啼,因此可以遍歷,偽數(shù)組具有以下特點(diǎn):
- 具有l(wèi)ength屬性堪簿;
- 按索引方式儲存數(shù)據(jù)痊乾;
- 不具有數(shù)組的push,pop等方法戴甩;
<script>
function fn() {
console.log(arguments)
console.log(arguments.length)
console.log(arguments[2])
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]); //10,2,33
}
}
fn(10,2,33)
</script>
函數(shù)返回值
-
函數(shù)的返回值
:只要函數(shù)遇到return
就把后面的結(jié)果 返回給函數(shù)的調(diào)用者符喝,且終止之后的代碼執(zhí)行,且只能返回一個(gè)值甜孤,是沒有返回值類型的协饲;
- return什么都不返回 或者 不寫return畏腕,那么函數(shù)的返回值為undefined;
<script>
//定義聲明函數(shù)
function getArrMax(arr) {
var max = arr[0]
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
return max
}
var max = getArrMax([100,200,30,40,50,333,234])
console.log(max);
</script>
<script>
function getResult(num1,num2) {
//返回值為數(shù)組
return [num1+num2,num1-num2]
}
var result = getResult(100,30)
console.log(result);
</script>
- 箭頭函數(shù)只有一行代碼茉稠,且有返回值描馅,可省略return關(guān)鍵字 和 {};
- 箭頭函數(shù)只有一行代碼而线,且返回值為Object對象字面量铭污,那么需要加上(Object對象字面量);
<script>
const func = (a, b) => {
return a + b;
}
const func1 = (a, b) => a + b;
const func2 = (a, b) => ({ name: 'yanzi' });
</script>
JavaScript作用域
-
全局作用域
:整個(gè)script標(biāo)簽 或者是一個(gè)單獨(dú)的js文件膀篮;
-
局部作用域
:存在兩種分別為塊級作用域和函數(shù)作用域嘹狞;
-
塊級作用域
:在代碼塊執(zhí)行時(shí)創(chuàng)建,在代碼塊執(zhí)行完畢時(shí)銷毀誓竿;
-
函數(shù)作用域
:在函數(shù)調(diào)用時(shí)創(chuàng)建磅网,在函數(shù)調(diào)用完成時(shí)銷毀;
-
全局變量
:在全局作用域中的變量筷屡,注意在函數(shù)內(nèi)部 沒有聲明直接賦值的變量 也屬于全局變量
涧偷,全局變量只有在瀏覽器關(guān)閉的時(shí)候才會被銷毀,比較占內(nèi)存資源毙死;
-
局部變量
:在局部作用域中的變量燎潮,當(dāng)函數(shù)執(zhí)行完成時(shí)就會被銷毀,比較節(jié)約內(nèi)存資源扼倘;
<script>
//全局作用域 全局變量
var num = 100
function addNum(a,b) {
//局部作用域 局部變量
var sum = 0
sum = a + b
//sum1沒有聲明直接賦值 屬于全局變量
sum1 = 1000
return sum
}
addNum(100,200)
</script>
-
作用域鏈
:內(nèi)部函數(shù)訪問外部函數(shù)變量确封,JS解釋器會優(yōu)先在當(dāng)前對象中尋找目標(biāo)變量,若找到直接使用唉锌,若沒有找到隅肥,則去上一層作用域中去尋找,依次類推袄简,若找到全局作用域都沒有找到目標(biāo)變量,就會報(bào)錯(cuò)xxx is not defined
泛啸,在作用域中尋找目標(biāo)變量绿语,遵循就近原則
;
<script>
var num = 10
function func() { //外部函數(shù)
var num = 20
function func1() { //內(nèi)部函數(shù)
console.log(num); // 20
}
}
let a = '全局作用域中的a';
{
let a = '第一個(gè)塊級作用域中的a';
{
let a = '第二個(gè)塊級作用域中的a';
console.log('a =', a);
}
}
</script>
JavaScript預(yù)解析
- JavaScript代碼是由瀏覽器中的JavaScript解析器來執(zhí)行的候址,JavaScript解析器在運(yùn)行JavaScript代碼的時(shí)候分為兩個(gè)步驟吕粹,分別為:
預(yù)解析
與代碼執(zhí)行
;
- 預(yù)解析:js引擎會將js里面
所有的var和function
提升到當(dāng)前作用域的最前面
岗仑;
- 代碼執(zhí)行:按照代碼書寫的順序從上到下依次執(zhí)行匹耕;
- 變量var的提升:將所有
變量var聲明提升
到當(dāng)前作用域的最前面,不會提升賦值操作
荠雕;
- 函數(shù)的提升:將所有
函數(shù)聲明(定義)提升
到當(dāng)前作用域的最前面稳其,不調(diào)用函數(shù)驶赏;
- 變量let的提升:會將所有
變量let聲明提升
到當(dāng)前作用域的最前面,但是在賦值之前解釋器禁止對該變量的訪問既鞠;
<script>
console.log(a); //a is not defined
a = 100;
console.log(b); //undefined
var b = 100;
console.log(c); //Cannot access 'c' before initialization
let c = 100;
</script>
<script>
//第一種:
console.log(num) //報(bào)錯(cuò)
//第二種:
console.log(num) //undefine
var num = 10
//第三種
func() //111
function func() {
console.log(111)
}
//第四種
fun() //報(bào)錯(cuò)
var fun = function() {
console.log(222)
}
</script>
<script>
var a = 18
f1()
function f1() {
var b = 9
console.log(a)
console.log(b)
var a = '123'
}
//上述代碼等價(jià)于
var a
function f1() {
var b
var a
b = 9
console.log(a) //undefine
console.log(b) //9
a = '123'
}
a = 18
f1()
</script>
<script>
f1()
console.log(c)
console.log(b)
console.log(a)
function f1() {
// var a = 9; b = 9;c = 9 b與c沒有聲明 直接賦值 屬于全局變量
// var a = b = c = 9 與 var a = 9,b = 9, c = 9 是不同的
var a = b = c = 9
console.log(a)
console.log(b)
console.log(c)
}
//以上代碼等價(jià)于
function f1() {
var a
a = b = c = 9
console.log(a) //9
console.log(b) //9
console.log(c) //9
}
f1()
console.log(c) //9
console.log(b) //9
console.log(a) //報(bào)錯(cuò) a是局部變量
</script>
立即執(zhí)行函數(shù)
- 在開發(fā)中應(yīng)該盡量減少直接在全局作用域中編寫代碼煤傍,盡量在局部作用域中編寫代碼;
- 立即執(zhí)行函數(shù):
(function(){})()
嘱蛋,是一個(gè)匿名函數(shù)蚯姆,直接執(zhí)行,且只會執(zhí)行一次洒敏;
<script>
(function(){
console.log('立即執(zhí)行函數(shù)');
})()
<script>
函數(shù)中的this關(guān)鍵字
- 函數(shù)在執(zhí)行時(shí)龄恋,JS解析器每次都會傳遞一個(gè)隱含參數(shù),此參數(shù)叫做this凶伙;
- this會指向一個(gè)對象郭毕,其所指的對象會根據(jù)函數(shù)調(diào)用方式的不同而不同;
- 以函數(shù)形式調(diào)用時(shí)镊靴,this指向的是window铣卡;
- 以方法形式調(diào)用時(shí),this指向的是調(diào)用方法的對象偏竟;
<script>
function func() {
console.log(this);
}
//以函數(shù)的形式調(diào)用時(shí)煮落,this指向window
func(); //window
//以方法的形式調(diào)用時(shí),this指向調(diào)用方法的對象
const obj = { name: '小神' };
obj.test = func;
obj.test(); //obj
const obj2 = { name: '大神', test: func };
obj2.test(); //obj2
</script>
- 箭頭函數(shù)沒有自己的this踊谋,內(nèi)部打印的this是由外層作用域決定的蝉仇,等于外層作用域的this;
- 箭頭函數(shù)的this和它的調(diào)用方式無關(guān)殖蚕;
<script>
function func() {
console.log(this);
}
//以函數(shù)的形式調(diào)用時(shí)轿衔,this指向window
func(); //window
//箭頭函數(shù)
const func3 = (a, b) => {
var num = a + b;
console.log(this);
return num;
}
func3(); //外層作用域?yàn)槿肿饔糜?this指向window
const obj3 = {
name: '如來',
fn: func,
fn2: func3,
sayHello() {
console.log(this.name);
function t() {
console.log('t -->', this);
}
t(); //window
const t2 = () => {
console.log('t2 -->', this);
}
t2(); //obj3
}
}
obj3.fn(); //obj3
obj3.fn2(); //window
obj3.sayHello();
</script>
高階函數(shù)
- 高階函數(shù):若一個(gè)函數(shù)的參數(shù)或者返回值為函數(shù),那么這個(gè)函數(shù)就是高階函數(shù)睦疫;
- 將函數(shù)作為參數(shù)害驹,就意味著可以對另一個(gè)函數(shù)動(dòng)態(tài)的傳遞代碼;
<script>
//高階函數(shù) -- 參數(shù)為一個(gè)函數(shù)
function filter(arr, cb) {
const newArr = [];
for (let index = 0; index < arr.length; index++) {
const element = arr[index];
if (cb(arr[index])) {
newArr.push(arr[index]);
}
}
return newArr;
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var result = filter(arr, a => a % 2 === 0);
console.log(result);
</script>
閉包
- 閉包:能訪問到外部函數(shù)作用域中變量的函數(shù)蛤育;
- 構(gòu)成閉包的必要條件:
- 存在函數(shù)的嵌套宛官;
- 內(nèi)部函數(shù)要引用外部函數(shù)的變量;
- 內(nèi)部函數(shù)要作為返回值瓦糕;
<script>
function outer() {
let num = 0;
return () => {
num++;
console.log(num);
}
}
const newFn = outer;
newFn();
</script>
- 函數(shù)的外層作用域(詞法作用域)底洗,在函數(shù)創(chuàng)建時(shí)就已經(jīng)確定了;
- 閉包的實(shí)現(xiàn)原理就是利用了詞法作用域咕娄;
<script>
let a = '全局變量a';
function fn() {
console.log(a);
}
function fn2() {
let a = 'fn2中的a';
fn();
}
fn2(); //fn() --> 打印的是全局變量a
function fn3() {
let a = 'fn3中的a';
function fn4() {
console.log(a);
}
return fn4;
}
let result = fn3();
fn4(); //fn4() --> 打印的是fn3中的a
</script>
- 閉包的生命周期:
- 閉包在外部函數(shù)調(diào)用時(shí)產(chǎn)生亥揖,外部函數(shù)每次調(diào)用都會產(chǎn)生一個(gè)全新的閉包;
- 閉包在內(nèi)部函數(shù)回收時(shí)銷毀圣勒;
- 閉包的注意事項(xiàng):
- 閉包住要用來隱藏一些不希望外部訪問的內(nèi)容费变,這就意味著閉包需要占用一定的內(nèi)存空間摧扇;
- 相比較于類Class來說,閉包比較浪費(fèi)內(nèi)存空間胡控,因?yàn)轭惪梢允褂迷投]包不可以扳剿;
<script>
function outer() {
let someValue = 'some';
return function () {
console.log(someValue);
}
}
</script>