1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別
函數(shù)聲明和函數(shù)表達(dá)式是聲明函數(shù)的兩種不同的方式,形式如下:
- 函數(shù)聲明:即使用function關(guān)鍵字聲明一個(gè)函數(shù)
//函數(shù)聲明
function sayHello(){
console.log('hello')
}
//函數(shù)調(diào)用
sayHello()
聲明不必放到調(diào)用的前面,函數(shù)調(diào)用可以發(fā)生在函數(shù)聲明之前,例如下面這種情況下不會(huì)報(bào)錯(cuò)
printName();//輸出console.log('1')
function printName(){
console.log('1');
}
//正常,因?yàn)樘嵘撕瘮?shù)聲明悲酷,函數(shù)調(diào)用可在函數(shù)聲明之前
即在一個(gè)作用域下鼻疮,var 聲明的變量和function 聲明的函數(shù)會(huì)前置
- 函數(shù)表達(dá)式
var sayHello = function(){
console.log('hello');
}
sayHello()
聲明必須放到調(diào)用的前面,例如
printName();//瀏覽器提示Uncaught TypeError: printName is not a function(…)
var printName = function(){
console.log('1');
};
//報(bào)錯(cuò)忌愚,函數(shù)表達(dá)式和 var 一個(gè)變量沒(méi)什么區(qū)別筛婉,變量printName還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后
瀏覽器提示Uncaught TypeError: printName is not a function(…).原因:類(lèi)似變量提升,函數(shù)作為一個(gè)變量賦值給printName,等價(jià)于
var printName; //此時(shí)printName為undefined
printName();
printName = function(){
console.log('1');
};
- 因此剔猿,函數(shù)聲明和函數(shù)表達(dá)式不同之處在于:
- Javascript引擎在解析javascript代碼時(shí)會(huì)‘函數(shù)聲明提升’(Function declaration Hoisting)當(dāng)前執(zhí)行環(huán)境(作用域)上的函數(shù)聲明视译,而函數(shù)表達(dá)式必須等到Javascirtp引擎執(zhí)行到它所在行時(shí),才會(huì)從上而下一行一行地解析函數(shù)表達(dá)式归敬,
- 函數(shù)聲明后面可以加括號(hào)立即調(diào)用該函數(shù)酷含,函數(shù)表達(dá)式不可以,只能以fnName()形式調(diào)用
2.什么是變量的聲明前置汪茧?什么是函數(shù)的聲明前置
- 變量聲明前置
- JavaScript引擎的工作方式是椅亚,先解析代碼,獲取所有被聲明的變量舱污,然后再一行一行地運(yùn)行呀舔。這造成的結(jié)果,就是所有的變量的聲明語(yǔ)句扩灯,都會(huì)被提升到當(dāng)前作用域的頭部媚赖,這就叫做變量的聲明前置,也叫變量提升(hoisting)珠插。
- 函數(shù)聲明前置
- JavaScript引擎將函數(shù)名視同變量名惧磺,所以采用function命令聲明函數(shù)時(shí),函數(shù)聲明會(huì)像變量聲明一樣丧失,被提升到代碼頭部豺妓。因此,函數(shù)的調(diào)用可以寫(xiě)在聲明前面布讹,函數(shù)可以被執(zhí)行琳拭。(注意:如下函數(shù)表達(dá)式,變量fn還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后)描验。
console.log(fn); //undefined fn(); //瀏覽器提示Uncaught TypeError: fn is not a function(…) var fn = function(){} //報(bào)錯(cuò)白嘁,變量fn還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后
3.arguments 是什么
-
arguments是一個(gè)類(lèi)數(shù)組對(duì)象膘流,類(lèi)似數(shù)組的方式絮缅,可以通過(guò)下標(biāo)的方式去獲取值,但它本身不是數(shù)組呼股,沒(méi)有數(shù)組的一些特性耕魄,所以叫類(lèi)數(shù)組對(duì)象。在函數(shù)內(nèi)部彭谁,可以使用arguments對(duì)象獲取到該函數(shù)的所有傳入?yún)?shù)吸奴。
- 例如如下函數(shù)
function printPersonInfo(name, age, sex){ console.log(name); console.log(age); console.log(sex); console.log(arguments); }
- 執(zhí)行
printPersonInfo('liu', 21, 'boy')
后,輸出:
name : liu age : 22 sex : boy ["liu", 22, "boy"]
- 執(zhí)行
printPersonInfo('liu', 21,)
后,輸出:
name : liu age : boy sex : undefined ["liu", "boy"]
4.函數(shù)的"重載"怎樣實(shí)現(xiàn)
- 重載是指不同的函數(shù)使用相同的函數(shù)名则奥,但是函數(shù)的參數(shù)個(gè)數(shù)或類(lèi)型不同考润。調(diào)用的時(shí)候根據(jù)函數(shù)的參數(shù)來(lái)區(qū)別不同的函數(shù)。
- JS并不像其他強(qiáng)類(lèi)型語(yǔ)言一樣可以聲明重載函數(shù)读处,若在原先聲明的函數(shù)后再聲明一個(gè)不同參數(shù)數(shù)量的函數(shù)(JS是弱語(yǔ)言糊治,聲明的函數(shù)不需要指明參數(shù)類(lèi)型),解析器會(huì)用后面聲明的函數(shù)覆蓋前面聲明的函數(shù)罚舱。
JS種沒(méi)有重載! 同名函數(shù)會(huì)覆蓋井辜。但可以在函數(shù)體針對(duì)不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯,如下
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á)式是什么馆匿?有什么作用
(function(){ var a=1; })();
(function(){ var a=1; }());
-
作用:
- 作用:隔離作用域抑胎。
6.遞歸
- 遞歸實(shí)現(xiàn)一個(gè)函數(shù),計(jì)算 n渐北!
function factor(n) {
if(n===1){
return n
}
return n*factor(n-1)
}
幾個(gè)代碼小題目
1.下面代碼輸出什么
function getInfo(name, age, sex){
console.log('name:',name);
console.log('age:', age);
console.log('sex:', sex);
console.log(arguments);
arguments[0] = 'valley';
console.log('name', name);
}
getInfo('饑人谷', 2, '男');
getInfo('小谷', 3);
getInfo('男');
-
getInfo('饑人谷', 2, '男');
輸出:
name:饑人谷
age:2
sex:男
["饑人谷", 2, "男"]
name valley
-
getInfo('小谷', 3);
輸出:
name:小谷
age:3
sex:undefined
["小谷", 3]
name valley
-
getInfo('男');
輸出:
name:男
age:undefined
sex:undefined
["男"]
name valley
2.寫(xiě)一個(gè)函數(shù)阿逃,返回參數(shù)的平方和
function sumOfSquares(){
var sum = 0
for (var i = 0; i < arguments.length; i ++)
sum += arguments[i] * arguments[i]
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
3.如下代碼的輸出?為什么
console.log(a);//undefined
var a = 1;
console.log(b);//報(bào)錯(cuò)Uncaught ReferenceError: b is not defined(…)
a輸出undefined是因?yàn)樽兞刻嵘?b沒(méi)有定義,類(lèi)似如下代碼
var a;
console.log(a); //undefined
a = 1;
console.log(b);
4.如下代碼的輸出赃蛛?為什么
sayName('world'); //輸出 'hello ', 'world' 使用函數(shù)聲明做出的聲明前置
sayAge(10); //報(bào)錯(cuò) Uncaught TypeError: sayAge is not a function(…) sayAge此時(shí)還為被賦值為函數(shù)
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
作用域鏈
5.如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
// 輸出10
var x = 10
bar() //輸出10
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
作用域鏈查找過(guò)程偽代碼
/*
1.
globalContext = {
AO:{
x: 10
foo:funcation(){}
bar:funcation(){}
}
Scope: null
}
foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x: 30
}
scope: bar.[[scope]] // globalContext.AO
}
3.
fooContext = {
AO:{}
scope: foo.[[scope]] // globalContext.AO
}
*/
6.如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
// 輸出30
var x = 10;
bar() //輸出30
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
作用域鏈查找過(guò)程偽代碼
/*
1.
globalContext = {
AO:{
x: 10
bar:funcation(){}
}
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x: 30
foo:funcation(){}
}
Scope: bar.[[scope]] // globalContext.AO
}
foo.[[scope]] = barContext.AO
3.
fooContext = {
AO:{}
Scope: foo.[[scope]] // barContext.AO
}
*/
7.以下代碼輸出什么? 寫(xiě)出作用域鏈的查找過(guò)程偽代碼
輸出30
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x) // 30
})()
}
/*
globalContext = {
AO:{
x: 10
bar:funcation(){}
}
Scope: null
}
bar.[[scope]] // globalContext.AO
2.
barContext = {
AO:{
x: 30
匿名函數(shù)A:funcation(){}
}
Scope: bar.[[scope]] = globalContext.AO
}
匿名函數(shù)A.[[scope]] // barContext.AO
3.
匿名函數(shù)AContext = {
AO:{}
Scope: 匿名函數(shù)A.[[scope]] // barContext.AO
}
*/
8.以下代碼輸出什么恃锉? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
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() //輸出: undefined 5 1 6 20
console.log(a) //輸出: 200
/*
1.
globalContext = {
AO:{
a: 1 -> 200
fn:funcation(){}
fn3:funcation(){}
}
Scope: null
}
fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO
2.
fnContext = {
AO:{
a: 5 -> 6 -> 20
fn2:funcation(){}
}
Scope: fn.[[scope]] // globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn3Context = {
AO:{}
Scope: foo.[[scope]] // globalContext.AO
}
4.
fn2Context = {
A:{}
Scope: fn2.[[scope]] // fnContext.AO
}
*/
注意:在JavaScript里,每個(gè)函數(shù)呕臂,當(dāng)被調(diào)用時(shí)破托,都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。因?yàn)樵诤瘮?shù)里定義的變量和函數(shù)只能在函數(shù)內(nèi)部被訪問(wèn)歧蒋,外部無(wú)法獲韧辽啊;當(dāng)調(diào)用函數(shù)時(shí)谜洽,函數(shù)提供的上下文就提供了一個(gè)非常簡(jiǎn)單的方法創(chuàng)建私有變量萝映。