1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別
函數(shù)聲明
使用 function 關(guān)鍵字聲明一個(gè)函數(shù)
//函數(shù)聲明
function sayHello(){
console.log('hello')
}
//函數(shù)調(diào)用
sayHello()
聲明不必放到調(diào)用的前面,但是不要放到其它函數(shù)中谈况。
函數(shù)表達(dá)式
var sayHello = function(){
console.log('hello');
}
sayHello()
<span style="color:red">用函數(shù)表達(dá)式聲明函數(shù)必須放到調(diào)用的前面鼎姐。</span>函數(shù)表達(dá)式是內(nèi)容為函數(shù)聲明的表達(dá)式寄疏,類似 a++ 為內(nèi)容為變量運(yùn)算的表達(dá)式尝蠕,而 a++ 需要事先聲明 a 且確定 a 的值华蜒,因此同理函數(shù)表達(dá)式聲明必須放置在調(diào)用的前面叭喜。
函數(shù)也是一種引用類型捂蕴,函數(shù)表達(dá)式的寫法就是把函數(shù)指針賦值給了 sayHello 變量闪幽。
為什么叫函數(shù)表達(dá)式?
因?yàn)楸磉_(dá)式其本質(zhì)是一個(gè)值委可,是一坨東西最終計(jì)算出一個(gè)結(jié)果即一個(gè)值着倾;而這種寫法就是將一坨東西賦值給變量卡者,就是將一個(gè)值賦值給變量崇决,所以稱為函數(shù)表達(dá)式恒傻。
函數(shù)聲明的寫法就不是表達(dá)式盈厘,而是單純函數(shù)的語句。
2. 什么是變量的聲明前置外遇?什么是函數(shù)的聲明前置
var 和 function 的聲明前置
在一個(gè)作用域下契吉,var 聲明的變量和 function 聲明的函數(shù)會(huì)前置.
首先將所有 var variableName
前置(不包括變量賦值部分)跳仿;
其次將所有 function functionName(){}
前置(包括函數(shù)定義部分)
例如:
console.log(a); //undefined
var a = 3;
console.log(a); //3
sayHello();
function sayHello(){
console.log('hello');
}
代碼的實(shí)際的執(zhí)行順序?yàn)椋?/p>
var a;
function sayHello() { console.log('hello'); }
console.log(a);
a = 3
console.log(a);
sayHello()
3. arguments 是什么
在函數(shù)內(nèi)部,你可以使用arguments對象獲取到該函數(shù)的所有傳入?yún)?shù)
function printPersonInfo(name, age, sex){
console.log(name);
console.log(age);
console.log(sex);
console.log(arguments);
}
arguments 是一個(gè)類數(shù)組對象捐晶,可用 arguments[0] 等來標(biāo)識(shí)各個(gè)參數(shù)菲语,也可以調(diào)用 length 屬性來顯示對象中屬性的數(shù)量,但是也沒有了一些數(shù)組的屬性惑灵。
4. 函數(shù)的"重載"怎樣實(shí)現(xiàn)
C語言重載范例
int sum(int num1, int num2){
return num1 + num2;
}
float sum(float num1, float num2){
return num1 + num2;
}
sum(1, 2);
sum(1.5, 2.4);
在 JS 中
沒有重載! 同名函數(shù)會(huì)覆蓋山上。 但可以在函數(shù)體針對不同的參數(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;
})()
console.log(a); //undefined
作用: <span style="color:red">隔離作用域</span>泣棋,避免和全局變量發(fā)生沖突胶哲。
包裹函數(shù)定義語句的括號(hào)使得 JS 解析器認(rèn)為括號(hào)內(nèi)是一個(gè)表達(dá)式,而不是一個(gè)函數(shù)聲明語句鸯屿。
其他寫法
(function fn1() {});
// 在數(shù)組初始化器內(nèi)只能是表達(dá)式
[function fn2() {}];
// 逗號(hào)也只能操作表達(dá)式修赞,逗號(hào)兩邊也被認(rèn)為是表達(dá)式
1, function fn3() {};
// 勾邦!號(hào)也可以寫成立即表達(dá)式的方法
!function(){
var a = 1;
}()
6. 求n!,用遞歸來實(shí)現(xiàn)
function recursive(n) {
if (n == 1) {
return 1;
} else {
return n * (n-1);
}
}
7. 以下代碼輸出什么虐杯?
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('男');
輸出:
name: 饑人谷
age: 2
sex: 男
["饑人谷",2,"男"] // 別漏了作字符串標(biāo)記的引號(hào)
name valley
name: 小谷
age: 3
["小谷",3]
name valley
name: 男
[男]
name valley
8. 寫一個(gè)函數(shù),返回參數(shù)的平方和?
function sumOfSquares(){
var sumOfSquares = 0;
for(var i = 0; i < arguments.length; i++){
sumOfSquares += arguments[i] * arguments[i];
}
return sumOfSquares;
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result) //10
9. 如下代碼的輸出吞歼?為什么
console.log(a); // undefined
var a = 1;
console.log(b); // Uncaught ReferenceError
代碼的執(zhí)行順序?yàn)椋?/p>
var a;
console.log(a); //undefined稽坤,此時(shí)變量 a 已經(jīng)聲明,但是并未賦值,所以顯示 undefined摆马,表示未定義惩淳,此處目前沒有任何值
a = 1;
console.log(b); //Uncaught ReferenceError,此時(shí)出現(xiàn)了錯(cuò)誤棉磨,因?yàn)樽兞?b 并未被聲明频敛,所以 JS 解析器將會(huì)顯示出現(xiàn)了搓球
10. 如下代碼的輸出差油?為什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
代碼實(shí)際執(zhí)行順序:
function sayName(name){
console.log('hello ', name);
}
sayName('world');
// hello world
// 由于函數(shù)聲明會(huì)被前置到代碼首部发侵,所以此語句正常運(yùn)行
sayAge(10);
// Uncaught TypeError
// 用函數(shù)表達(dá)式的方式來聲明函數(shù),JS 解析器會(huì)認(rèn)為這是一個(gè)表達(dá)式而非函數(shù)聲明
// 所以這樣聲明的函數(shù)不會(huì)被聲明前置
// 所以在調(diào)用 sayAge 時(shí) JS 解析器會(huì)認(rèn)為函數(shù)未被聲明,顯示錯(cuò)誤
var sayAge = function(age){
console.log(age);
};
輸出:
hello world
Uncaught TypeError
11. 如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
12. 如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
作用域鏈如下:
globalContext = {
AO: {
x: 10,
bar: function
}
Scope: null
}
// 聲明 bar 函數(shù)時(shí)解取,生成其作用域鏈如下
bar.[[scope]] = globalContext.AO
barContext = {
AO: {
x: 30,
foo: function
}
scope: globalContext
}
// 聲明 foo 函數(shù)時(shí),生成其作用域鏈如下
foo.[[scope]] = barContext.AO
fooContext = {
AO: {}
Scope: barContext.AO
}
查找過程:
Step1 聲明變量 x 并且賦值為10
Step2 聲明前置 bar 函數(shù),在其內(nèi)聲明 bar 域內(nèi)變量x 并賦值30,
聲明域內(nèi) foo 函數(shù)旧噪,并隨后執(zhí)行 foo 函數(shù)
Step3 代碼執(zhí)行 bar 函數(shù)陪毡,然后執(zhí)行 foo 函數(shù)妙色,foo 函數(shù)活動(dòng)對象 AO 中沒有 x,
通過其作用域鏈找到 bar 函數(shù)的活動(dòng)對象 AO,在其中找到 x 值為30定庵,然后輸出值為30
13. 以下代碼輸出什么? 寫出作用域鏈的查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
作用域鏈如下:
globalContext = {
AO: {
x: 10,
bar: function
}
Scope: null
}
bar.[[scope]] = globalContext.AO
barContext = {
AO: {
x: 30,
immediatelyFunction
}
Scope: globalContext.AO
}
immediatelyFunctionContext = {
AO:{}
Scope: barContext.AO
}
執(zhí)行流程:
Step1 聲明變量 x 并賦值為10
Step2 前置聲明函數(shù) bar,在 bar 域內(nèi)聲明變量 x 并賦值30,聲明立即執(zhí)行函數(shù)
Step3 執(zhí)行 bar 函數(shù),然后執(zhí)行 bar 域內(nèi)立即執(zhí)行函數(shù)擎勘,其找到其作用域
為 barContext.AO, 然后輸出值為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)
作用域鏈如下:
globalContext = {
AO: {
a: 1,200(代碼執(zhí)行 fn3() 時(shí))
fn: function,
fn3: function
}
Scope: null
}
fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO
fnContext = {
AO: {
a: undefined(被聲明前置時(shí)),5(被賦值為5時(shí))且蓬,
6(執(zhí)行++操作)豹障,20(被 fn2() 函數(shù)修改)
fn2: function
}
Scope: globalContext.AO
}
fn2.[[scope]] = fnContext.AO
fn2Context = {
AO: {
a: 20
}
Scope = fnContext.AO
}
fn3Context = {
AO: {
a: 200
}
Scope = globalContext.AO
}
解答
var a = 1;
function fn(){
console.log(a)
// 輸出第1行:undefined坞笙,此時(shí)輸出 a 的值是函數(shù) fn 作用域中的值梯澜,
// fn 函數(shù)已經(jīng)前置聲明變量 a,但是還未賦值,固輸出 undefined
var a = 5
console.log(a)
// 輸出第2行:5,此時(shí)輸出 a 的值是函數(shù) fn 作用域中 a 的值衙熔,
// 該作用域中變量 a 剛剛被賦值為5
a++
var a
fn3()
// 輸出第3行:1脖隶,此時(shí)輸出 a 的值是是函數(shù) fn3 在其作用域鏈
// globalContext.AO 即全局上下文中找到的婉称,在該作用域中 a 的值為1
fn2()
// 輸出第4行:6科汗,此時(shí)輸出 a 的值是函數(shù) fn 作用域中 a 的值,剛剛執(zhí)行++操作
console.log(a)
// 輸出第5行:20,此時(shí)輸出 a 的值是函數(shù) fn 作用域中 a 的值,剛剛在 fn2 中
// 被改變?yōu)?0
function fn2(){
console.log(a)
a = 20
}
}
function fn3(){
console.log(a)
a = 200
}
fn()
console.log(a)
// 輸出第6行:200谱姓,此時(shí)輸出 a 的值是全局上下文中 a 的值,在 fn3 中被改變?yōu)?00