如題,本文介紹函數(shù)與作用域的相關(guān)知識(shí)
1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別
-
函數(shù)聲明:使用function關(guān)鍵字可以聲明一個(gè)函數(shù)
例:
function foo(){}
-
函數(shù)表達(dá)式:意即將一個(gè)匿名函數(shù)賦值給一個(gè)變量
例:
var foo=function(){}
-
區(qū)別:
(1)函數(shù)聲明必須有標(biāo)識(shí)符,也就是常說的函數(shù)名;函數(shù)表達(dá)式可以省略函數(shù)名
(2)函數(shù)的聲明不必放到調(diào)用的前面,函數(shù)調(diào)用的時(shí)候才會(huì)被執(zhí)行。函數(shù)的表達(dá)式則必須放到調(diào)用的前面。
(3)函數(shù)聲明在JS解析時(shí)進(jìn)行函數(shù)提升,因此在同一個(gè)作用域內(nèi),不管函數(shù)聲明在哪里定義,該函數(shù)都可以進(jìn)行調(diào)用。而函數(shù)表達(dá)式的值是在JS運(yùn)行時(shí)確定,并且在表達(dá)式賦值完成后,該函數(shù)才能調(diào)用。
在程序執(zhí)行前,會(huì)先獲取函數(shù)聲明聲明的函數(shù),獲取變量的聲明,這里變量的聲明只是先開辟一個(gè)空間,然后給了個(gè)名字,之后到該變量名賦值的時(shí)候,才有值,也就是說,在未得到該變量的賦值前,使用該變量會(huì)得到undefined。
2.什么是變量的聲明前置?什么是函數(shù)的聲明前置
javascript的變量聲明具有hoisting機(jī)制,JavaScript引擎在執(zhí)行的時(shí)候,會(huì)把所有的聲明都提升到當(dāng)前作用域的最前面
-
變量聲明前置就是在一個(gè)作用域塊中,所有的變量都被放在塊的開始處聲明
變量的聲明前置,在程序運(yùn)行前,先獲取變量的名字,到該變量的賦值語句,才為該變量賦值,再此前都是undefined
函數(shù)的聲明前置:function 函數(shù)名(){}聲明了一個(gè)函數(shù),在程序運(yùn)行前,提取所有函數(shù)聲明的函數(shù),之后才正式開始運(yùn)行函數(shù),所以,無論函數(shù)聲明的函數(shù)執(zhí)行語句放在程序的最前面,或者程序的最后面,都能夠正常執(zhí)行
3.arguments 是什么
arguments是函數(shù)內(nèi)部的一個(gè)對(duì)象,對(duì)應(yīng)著傳進(jìn)來的參數(shù),是一個(gè)類數(shù)組對(duì)象,沒有數(shù)組的操作方法。
- 特點(diǎn):
(1)對(duì)象有l(wèi)ength屬性且length的值就是對(duì)象里屬性的數(shù)量
(2)每個(gè)傳進(jìn)來的參數(shù)對(duì)應(yīng)著arguments[0],arguments[1],有先后順序之分,通過下標(biāo)獲取到對(duì)應(yīng)值
比如
function fn(name,age){
console.log(arguments[0];);
console.log(arguments[1];);
}
4.函數(shù)的"重載"怎樣實(shí)現(xiàn)
定義:重載函數(shù)就是在同一作用域內(nèi),可以有一組具有相同函數(shù)名,不同參數(shù)列表的函數(shù),執(zhí)行相應(yīng)的功能
在JS中沒有函數(shù)重載。相同的函數(shù)名字,如果定義的參數(shù)個(gè)數(shù)不一樣,后出現(xiàn)的會(huì)覆蓋先出現(xiàn)的 同名函數(shù)后面的會(huì)覆蓋前面的。
-
但我們可以通過在函數(shù)體內(nèi)針對(duì)不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯,也就類似實(shí)現(xiàn)了“重載”的效果。
例子 function printPeopleInfo(name, age, sex){ if(name){ console.log(name); } if(age){ console.log(age); } if(sex){ console.log(sex); } } printPeopleInfo('Byron', 26); printPeopleInfo('Byron', 26, 'male');
5.立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用
- 寫法
(1)(function(){ /* code */ }());
// Crockford 推薦這個(gè)
(2)(function(){ /* code */ })();
// 這個(gè)同樣運(yùn)行正常
- 作用
(1) 函數(shù)會(huì)立即執(zhí)行。
(2) 隔離作用域(這個(gè)匿名函數(shù)中的變量與外部環(huán)境的變量隔離,防止了變量的命名沖突,形成了一個(gè)獨(dú)立的空間,有助于代碼模塊化。)
6.求n!,用遞歸來實(shí)現(xiàn)
- 遞歸4個(gè)特點(diǎn):
(1)自己調(diào)用自己
(2)設(shè)定終止條件
(3)優(yōu)點(diǎn):算法簡單
(4)缺點(diǎn):效率低
-
例子
function factor(n){ if(n===1){ console.log(n); return n; } var nn=n*factor(n-1) console.log(nn) return nn } factor(5)````
//1*2*3*4*5 結(jié)果:1 2 6 24 120
7.以下代碼輸出什么?(PS: //注釋就是結(jié)果)
function getInfo(name, age, sex){
console.log('name:',name); //饑人谷-->小谷-->男
console.log('age:', age); //2-->3-->undefined
console.log('sex:', sex); //男-->undefined-->undefined
console.log(arguments); //空(沒有指定下標(biāo))
arguments[0] = 'valley';
console.log('name', name); //name valley
}
getInfo('饑人谷', 2, '男');
getInfo('小谷', 3);
getInfo('男');
8. 寫一個(gè)函數(shù),返回參數(shù)的平方和?
<script>
function sumOfSquares(){
var result = 0;
if(arguments.length>0){
for(var i=0;i<arguments.length;i++){
result += arguments[i]*arguments[i];
}
}
return result
}
var result =sumOfSquares(2,3,4)
var result2 =sumOfSquares(1,3)
console.log(result)
console.log(result2)
</script>
9.如下代碼的輸出?為什么
console.log(a);
var a = 1; //undefined
console.log(b); //報(bào)錯(cuò)
- 結(jié)果:undefined和報(bào)錯(cuò)
- 原因:
(1)變量a的定義var a = 1;
應(yīng)該放到console.log(a);
的前面,變量應(yīng)該先定義后輸出。
(2)b是沒有被聲明的,肯定不能直接輸出。
10.如下代碼的輸出?為什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
}
輸出 :hello,world以及報(bào)錯(cuò)
原因:
function sayName(name){}
是一個(gè)函數(shù),它可以直接調(diào)用賦值,聲明和調(diào)用不分先后,沒有順序
var sayAge = function(age){}
是一個(gè)函數(shù)表達(dá)式,想要賦值必須先要聲明,應(yīng)該放到function(age){}
后面
11.如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
/*
1.第一步:執(zhí)行上下文
globalContext{
AO:{
x=10;
foo:function();
bar:function();
},
Scope:null
}
foo.[[scope]]=globalContext.AO
bar.[[scope]]=globalContext.AO
2.第二步:調(diào)用bar()
barContext={
AO:{
x:30
foo:function
},
Scope:bar.[[scope]]//globalContext.AO
}
bar() //結(jié)果 x:30
3.第三步:調(diào)用foo()
fooContext={
AO:{}
Scope:foo.[[scope]]=globalContext.AO
}
foo() //結(jié)果 x:10
//最終結(jié)果: x:10
*/
12. 如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
/*
第一步:執(zhí)行上下文
globalContext{
AO:{
x:10
bar:function
}
Scope:null
}
bar.[[scope]]=globalContext.AO
第二步:調(diào)用bar()
barContext={
AO:{
x:30
foo:function
}
Scope:bar.[[scope]]=globalContext.AO
}
bar() //結(jié)果 x:30
第三步:調(diào)用foo()
fooContext={
A0:{ }
Scope:foo.[[scope]]=barContext.AO
}
foo() //結(jié)果 x:30
// bar(){
// foo()
// }
// 最終結(jié)果 x:30
*/
13. 以下代碼輸出什么? 寫出作用域鏈的查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
/*
第一步:執(zhí)行上下文
globalContext{
AO:{
x:10;
bar:function
}
Scope:null
}
bar.[[scope]]=globalContext.AO
第二步:調(diào)用bar()
barContext={
AO:{
x=30
:function
}
Scope:bar.[[scope]]=globalContext.AO
}
第三步:立即執(zhí)行function ()
functionContext={
AO:{ }
Scope:function.[[scope]]//barContext.AO
}
// 最終結(jié)果 x:30
*/
14. 以下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var a = 1;
function fn(){
console.log(a)
var a = 5
console.log(a)
a++
var a
fn3()
fn2()
console.log(a)
function fn2(){
console.log(a)
a = 20
}
}
function fn3(){
console.log(a)
a = 200
}
fn()
console.log(a)
/*
//第一步:執(zhí)行上下文
globalContext:{
AO:{
a:1
fn:function
fn3:function
}
Scope:null
}
fn.[[scope]]=globalContext.A0
fn3.[[scope]]=globalContext.AO
//調(diào)用 fn()
fnContext:{
AO:{
a:6
fn3:function
fn2:function
}
Scope:null
}
fn3[[scope]]=fnContext.AO
fn2[[scope]]=fnContext.AO
//調(diào)用 fn2()
fn2Context:{
AO:{
}
Scope:fn2.[[scope]]=fnContext.AO
}
//調(diào)用 fn3()
fn3Context:{
AO:{
}
Scope:fn3.[[scope]]=globalContext.AO
}
// 最終結(jié)果 a:undefined 5 1 6 20 200
*/
本文部分內(nèi)容來自于饑人谷,版權(quán)歸饑人谷_海瀚和饑人谷所有,轉(zhuǎn)載需說明來源