基本概念
函數(shù)的定義就是一段可以反復(fù)調(diào)用的代碼塊痪欲,可以接受參數(shù),并且可以根據(jù)不同參數(shù)返回不同的返回值攻礼。js的函數(shù)屬于復(fù)雜類型业踢,屬于Object類型,但是它有自己的構(gòu)造函數(shù)礁扮,且能夠使用typeof
運(yùn)算符返回function
知举。js中不指定函數(shù)返回值,默認(rèn)返回undefined
太伊。函數(shù)的原型鏈經(jīng)過Function.prototype
雇锡。
函數(shù)聲明
函數(shù)聲明的方式目前已知的有五種。
-
function fn(a,b){return a+b}
function命令聲明具名函數(shù) -
var fn = function(a,b){return a+b}
function命令聲明匿名函數(shù)僚焦,賦值給變量fn -
var fn = function f(a,b){return a+b}
function命令聲明具名函數(shù)锰提,且賦值給fn -
var fn = new Function('a','b','return a+b')
Function構(gòu)造函數(shù)聲明匿名函數(shù),賦值給fn -
var fn = (a,b)=>{return a+b}
箭頭函數(shù)聲明一個匿名函數(shù),賦值給fn
比較常用的聲明方式是第一立肘,第二边坤,第五種。js的函數(shù)聲明方面還存在著一個比較反常規(guī)的地方谅年,那就是函數(shù)的name
屬性茧痒。請看以下示例:
//具名函數(shù)
function fn(a,b){return a+b}
console.log(fn.name) //'fn'
//匿名函數(shù)賦值給變量fn
var fn = function(a,b){return a+b}
console.log(fn.name) //'fn'
//具名函數(shù)賦值給變量fn
var fn = function f(a,b){return a+b}
console.log(fn.name) //'f'
//Function構(gòu)造函數(shù)聲明函數(shù),賦值給fn
var fn = new Function('a','b','return a+b')
console.log(fn.name) //'anonymous'(匿名)
//箭頭函數(shù)聲明一個匿名函數(shù)融蹂,賦值給fn
var fn = (a,b)=>{return a+b}
console.log(fn.name) //'fn'
由此看出旺订,js函數(shù)的聲明與name
屬性也有很強(qiáng)的不一致性,容易讓人產(chǎn)生誤解超燃。需要刻意的去記憶区拳。
函數(shù)提升
函數(shù)可以向變量一樣提升到代碼頭部。
fn(); //undefined
var a = 0;
function fn(){
console.log(a);
}
之所以以上代碼沒有報錯意乓,是因為樱调,整個function
被提升到代碼頭部了。以上代碼相當(dāng)于:
var a;
function fn(){
console.log(a);
}
fn(); //undefined
a = 0;
函數(shù)的調(diào)用
函數(shù)的調(diào)用方式一般有三種直接調(diào)用洽瞬,使用call
,使用apply
;
直接調(diào)用
函數(shù)直接調(diào)用,傳入?yún)?shù)业汰,函數(shù)內(nèi)部執(zhí)行伙窃,返回需要返回的值。
function fn(){console.log('直接調(diào)用')}
fn()//直接調(diào)用
call方式調(diào)用
call
方式調(diào)用以也是比較常見的調(diào)用方式样漆,它接收的第一個參數(shù)內(nèi)部this
指定的值为障,若不傳即為undefined
,內(nèi)部this
自動轉(zhuǎn)為window
對象放祟,若使用嚴(yán)格模式use strict
鳍怨,則內(nèi)部this
為undefined
。從第二個參數(shù)開始為函數(shù)所要接收的參數(shù)跪妥。示例:
function fn(a,b){
console.log(this,a,b);
}
fn.call(); //window undefined undefined
fn.call(undefined,1,2); //window 1 2
fn.call({ff:'f'},1,2); //{ff:'f'} 1 2
apply方式調(diào)用
apply
方式調(diào)用與call
基本一致鞋喇,只是接收的參數(shù)有一點區(qū)別,第一個參數(shù)沒差別眉撵,都是指定內(nèi)部this
變量侦香,第二個參數(shù),apply
接收一個數(shù)組或者偽數(shù)組對象纽疟。示例:
function fn(a,b){
console.log(this,a,b);
}
fn.apply(); //window undefined undefined
fn.apply(undefined,[1,2]); //window 1 2
fn.apply({ff:'f'},[1,2]); //{ff:'f'} 1 2
this
關(guān)于this
是js
語言設(shè)計者布蘭登·艾克為了滿足當(dāng)時的需求(長得像Java
)而加入的一個屬性罐韩,this
指代了屬性和方法'當(dāng)前'所在的對象。剛剛提過污朽,使用call
或者apply
可以指代調(diào)用函數(shù)時內(nèi)部的this
對象散吵。
構(gòu)造函數(shù)中的this
構(gòu)造函數(shù)中的this
指代了當(dāng)前的實例。示例:
function F(){
this.name = 'ff';
this.t = this;
}
函數(shù)中的this
函數(shù)中的this
只有在函數(shù)被調(diào)用時,才有意義矾睦。當(dāng)調(diào)用函數(shù)時晦款,我們把調(diào)用方式改為call
的形式調(diào)用,可以輕松確定顷锰。示例:
var obj = {
a: 1,
b: function(){
console.log(this)
}
}
obj.b() // {a:1,b:function(){console.log(this)}}
//等價于 obj.b.call(obj) .call前面的第二個參數(shù)開始往前柬赐,即為函數(shù)內(nèi)部的this
其他情況下的this
在其他情況下,this指向當(dāng)前作用域的對象官紫。示例:
var obj = {
a: '0',
b: this
}
obj.b // window
var obj = {
a: '0',
b: {
t:this
}
}
obj.b.t // window
arguments
arguments
可以接收傳給函數(shù)的所有參數(shù)肛宋。arguments
本身是一個偽數(shù)組。示例:
function fn(){console.log(arguments)}
fn(1,2,3,'d') // [1,2,3,'d']
調(diào)用棧(call stack)
call stack
是指函數(shù)在執(zhí)行時束世,內(nèi)部的處理方式酝陈。可以幫助理解毁涉,函數(shù)在執(zhí)行過程中沉帮,怎么一步一步執(zhí)行,并且返回到之前的位置的贫堰。請看簡單的示例
作用域
作用域是一個比較難理解的概念穆壕,但是如果結(jié)合數(shù)據(jù)結(jié)構(gòu)去理解,也就直觀了很多其屏。目前喇勋,js中的作用域只有全局作用域window
和函數(shù)作用域。
- 作用域是一個樹形結(jié)構(gòu)
- 若當(dāng)前作用域無變量名所對應(yīng)的變量偎行,則去父作用域中尋找川背,直到根節(jié)點(全局作用域)。若存在則去值蛤袒,若不存在熄云,則取
undefined
。這就是作用域的就近原則
- 在作用域中確定某個變量的值妙真,第一步要做的就是
變量提升
缴允,然后再去分析變量的值。
典型例題
第一題
var a = 1
function f1(){
console.log(a)
var a = 2
}
f1.call() //undefined
分析:
- 變量提升,聲明提升
var a
function f1(){
var a
console.log(a)
a = 2
}
a = 1
f1.call()
- 分析作用域
因為在f1()
中console.log(a)
的作用域是f1
珍德,所以在內(nèi)部console.log(a)
之前的代碼中尋找a
癌椿,發(fā)現(xiàn)a
是undefined
第二題
var a = 1
function f1(){
var a = 2
f2.call()
}
function f2(){
console.log(a)
}
f1.call() //1
分析:
- 變量提升,提升聲明
var a
function f1(){
var a
a = 2
f2.call()
}
function f2(){
console.log(a)
}
a = 1
f1.call()
- 分析作用域
因為f1()
執(zhí)行時菱阵,內(nèi)部執(zhí)行f2()
,而f2()
在全局作用域中踢俄。這時f2()
中的console.log(a)
之前沒有變量a
,沿著作用域鏈向上尋找,在全局作用域中有變量a
晴及,那么f2()
中的console.log(a)
即為全局的變量a
都办。在執(zhí)行f2()
之前,a
已經(jīng)被賦值為1
,所以打印出1
琳钉。
第三題
//有6個li
var liTags = document.querySelectorAll('li')
for(var i = 0; i<liTags.length; i++){
liTags[i].onclick = function(){
console.log(i) // 點擊第3個 li 時势木,打印 2 還是打印 6?
}
}
分析:
- 變量提升歌懒,提升聲明
var liTags
var i
liTags = document.querySelectorAll('li')
for(i=0;i<liTags.length;i++){
liTags[i].onclick = function(){
console.log(i)
}
}
- 當(dāng)點擊第三li的時候啦桌,會執(zhí)行函數(shù)
function(){console.log(i)}
。因為i
變量在當(dāng)前函數(shù)中并不存在及皂,所以要去父作用域(全局作用域)尋找甫男,找到了全局的i
,這時循環(huán)已經(jīng)結(jié)束,而i
的值已經(jīng)變?yōu)?code>liTags.length验烧。所以打印出6
閉包
閉包就是一個函數(shù)是用來它范圍外的變量板驳,那么 這個函數(shù)
+ 這個變量
就組成了 閉包
。示例:
function a(){
var aa = 'aa'
function b(){
bb = aa;
}
}
總結(jié)
函數(shù)是整個js的核心碍拆,必須完全弄明白若治。這對以后的工作和提升會有很大的影響。再者就是數(shù)據(jù)結(jié)構(gòu)是可以幫助理解抽象化知識感混,使抽象化的知識在大腦中形成一個更加明確的結(jié)構(gòu)端幼。
本文僅供交流與學(xué)習(xí)使用,轉(zhuǎn)載請注明出處弧满。