- 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 (*)
- 解析器會(huì)率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可以訪問(wèn);函數(shù)表達(dá)式則必須等到解析器執(zhí)行到它所在的代碼行才會(huì)被執(zhí)行。
- 函數(shù)聲明:
console.log(sum(10,10));
function sum(num1, num2) {
return num1 + num2;
}
上述代碼完全可以運(yùn)行。因?yàn)樵诖a執(zhí)行之前,解析器就已經(jīng)通過(guò)一個(gè)名為函數(shù)聲明提升的過(guò)程,讀取并將函數(shù)聲明添加到執(zhí)行環(huán)境中。在執(zhí)行sum(10,10)時(shí)JavaScript引擎會(huì)聲明函數(shù)并將它們放到源代碼樹的頂部。等價(jià)于:
function sum(num1, num2) {
return num1 + num2;
}//函數(shù)放置在頂部被聲明
console.log(sum(10,10));
- 函數(shù)表達(dá)式:
console.log(sum(10,10));
var sum = function(num1, num2) {
return num1 + num2;
}
上述代碼運(yùn)行將會(huì)出錯(cuò),因?yàn)楹瘮?shù)處在一個(gè)var初始化語(yǔ)句中,而不是一個(gè)函數(shù)聲明。等價(jià)于
var sum;
console.log(sum(10,10));
sum = function(num1, num2) {
return num1 + num2;
在執(zhí)行到函數(shù)所在的語(yǔ)句前,變量sum中不會(huì)保存有對(duì)函數(shù)的引用,這時(shí)var sum = undefined;那么alert(undefined(10,10))是沒(méi)有任何意義的,所以會(huì)出錯(cuò)。
- 什么是變量的聲明前置?什么是函數(shù)的聲明前置 (**)
- 變量的聲明前置:
JavaScript引擎的工作方式是先解析代碼,獲取所有被聲明的變量,然后再一行一行地運(yùn)行。所有的變量聲明語(yǔ)句都會(huì)被提升到代碼頂部。
- 變量的聲明前置:
console.log(a);
var a = 123;
上述代碼輸出為undefined,因?yàn)樵趫?zhí)行時(shí)先解析了var a,a聲明后沒(méi)有賦值所以為undefined,然后再執(zhí)行console語(yǔ)句,最后給a賦值為3??梢詫懗?
var a;
console.log(a);
a = 123;
- 函數(shù)的聲明前置:
JavaScript引擎將函數(shù)名視同變量名,所以采用function
命令聲明函數(shù)時(shí),整個(gè)函數(shù)會(huì)像變量聲明一樣,被提升到代碼頂部。
fn();
function fn() {}
上述代碼不會(huì)報(bào)錯(cuò),因?yàn)樵趂n()執(zhí)行前,函數(shù)function fn() {}就被提升到代碼的頂部,在執(zhí)行fn()前就被聲明了??梢詫懗?
function fn() {}
fn();
如果采用賦值語(yǔ)句(函數(shù)表達(dá)式)定義函數(shù)就會(huì)報(bào)錯(cuò):
fn();
var fn = function (){};
//error
上述代碼在執(zhí)行時(shí)變量fn只是被聲明了并沒(méi)有被賦值為函數(shù),可以寫成:
var fn;
fn();
fn = function (){}
這里的fn被聲明后為undefined,執(zhí)行到fn()時(shí)其實(shí)是undefined(),這個(gè)是沒(méi)有意義的所以會(huì)報(bào)錯(cuò)。
- arguments 是什么 (*)
- arguments對(duì)象:
它與數(shù)組類似但不是Array實(shí)例。arguments可以使用類似數(shù)組的語(yǔ)法訪問(wèn)它每一個(gè)元素(第一個(gè)元素為arguments[0],第二個(gè)元素為arguments[1]),也可以用length屬性來(lái)確定傳遞了多少參數(shù)。
- arguments對(duì)象:
function say() {
console.log("Hello " + arguments[0] + ", " + arguments[1]);
}
say("world", "morning"); //Hello world, morning
- arguments對(duì)象的length屬性:
通過(guò)訪問(wèn)arguments對(duì)象的length屬性可以知道有多少個(gè)參數(shù)傳遞給了函數(shù)。
function howManyArgs() {
console.log(arguments.length);
}
howManyArgs("str", 123);//2
howManyArgs();//0
howManyArgs(123);//1
howManyArgs("str", 321, "str1");//3
- 我們可以利用arguments對(duì)象的length屬性讓函數(shù)能夠接受不同個(gè)數(shù)的參數(shù)并分別實(shí)現(xiàn)不同的功能。
function add() {
if(arguments.length == 1) {
console.log(arguments[0] + 10);
} else if (arguments.length == 2){
console.log(arguments[0] + arguments[1]);
}
}
add(10); //20
add(20, 30); //50
- 上述代碼中當(dāng)只傳遞給函數(shù)一個(gè)參數(shù)時(shí)給這個(gè)參數(shù)加上10,當(dāng)傳遞給函數(shù)兩個(gè)參數(shù)時(shí)則這兩個(gè)參數(shù)相加。arguments對(duì)象可以與命名的參數(shù)一起使用,所以我們還可以寫成下面這種形式
function add(num1, num2) {
if(arguments.length == 1) {
console.log(num1 + 10);
} else if (arguments.length == 2){
console.log(arguments[0] +num2);
}
}
add(10); //20
add(20, 30); //50
- 這里我們可以知道num1與arguments[0]的值相同,它們可以互換。上例代碼也告訴我們,不管你給函數(shù)命名了多少參數(shù),JavaScript是不會(huì)管你給函數(shù)傳遞了多少參數(shù)的。
- arguments的值與對(duì)應(yīng)函數(shù)中命名參數(shù)的值保持同步:
function add(num1, num2) {
arguments[1] = 10;
console.log(arguments[0] + num2);
}
add(20, 30); //30
- 上述代碼每次執(zhí)行到add()函數(shù)都會(huì)重寫第二個(gè)參數(shù),將第二個(gè)參數(shù)改寫為10。arguments對(duì)象的值會(huì)對(duì)應(yīng)到命名參數(shù)(arguments[1]與num2的值相同)所以修改了arguments[1]也就修改了num2,結(jié)果就是它們都變成了10。這里arguments[1]與num2的值雖然相同,但它們的內(nèi)存空間是不同并且獨(dú)立的,只是它們的值會(huì)同步。
-
如果我們?cè)谶@里只傳遞了一個(gè)參數(shù),那么arguments[1]設(shè)置的值不會(huì)反映到命名參數(shù)里。因?yàn)閍rguments對(duì)象的長(zhǎng)度只是由傳遞到函數(shù)的參數(shù)個(gè)數(shù)決定(add(20, 30)),不是由定義函數(shù)時(shí)命名的參數(shù)個(gè)數(shù)決定(function add(num1, num2) {})。
只傳遞一個(gè)參數(shù)但賦值arguments[1] - 這個(gè)圖里我們可以發(fā)現(xiàn),執(zhí)行函數(shù)時(shí)只傳遞了一個(gè)參數(shù),在這樣的情況下沒(méi)有傳遞值的命名參數(shù)(num2)被自動(dòng)賦為undefined值。與只聲明了變量但又沒(méi)有初始化賦值一樣。即使我們給arguments[1]賦值為10,num2的值仍然還是undefined(只是值同步訪問(wèn)的空間是獨(dú)立的)。
- 函數(shù)的重載怎樣實(shí)現(xiàn) (**)
- 我們可以利用檢查傳入函數(shù)中參數(shù)的類型和數(shù)量來(lái)做出不同的反應(yīng),用來(lái)模仿重載
- 以通過(guò)處理傳入的參數(shù)來(lái)實(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');
- 通過(guò)遍歷 arguments來(lái)實(shí)現(xiàn)類似重載的效果
function sum(){
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum = sum + arguments[i];
}
return sum;
}
console.log(sum(1,3,5));
- 根據(jù)傳入?yún)?shù)的不同類型來(lái)實(shí)現(xiàn)重載
function addOrSay(num1, num2) {
for (var i = 0; i < arguments.length; i++) {
if(arguments.length == 1 && !isNaN(arguments[i])) {
console.log(num1 + 10);
} else if (arguments.length == 2 && !isNaN(arguments[i])) {
console.log(arguments[0] +num2);
} else {
console.log(arguments[i]) ;
}
}
}
addOrSay(10);//10
addOrSay(20, 30);//50
addOrSay("Hello");//Hello

- 立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用 (***)
- 立即執(zhí)行函數(shù)表達(dá)式(IIFE),是指定義函數(shù)后立即執(zhí)行函數(shù);為了避免歧義,javascript規(guī)定,如果function關(guān)鍵字出現(xiàn)在行首,一律解釋為語(yǔ)句,下面是兩種立即執(zhí)行函數(shù)表達(dá)式的寫法,它們都是以圓括號(hào)開頭的,javascript引擎就會(huì)認(rèn)為后面是一個(gè)表達(dá)式而不是函數(shù)定義的語(yǔ)句
- 立即執(zhí)行函數(shù)的語(yǔ)法錯(cuò)誤
function() {}()//這里出錯(cuò)是因?yàn)闆](méi)把明確告訴圓括號(hào)運(yùn)算符這里是一個(gè)表達(dá)式。
解析器把這段語(yǔ)句當(dāng)成了一個(gè)函數(shù)聲明。
函數(shù)聲明又必須要有標(biāo)識(shí)符作為函數(shù)名稱。
function fn() {}()//加上標(biāo)識(shí)符結(jié)果成了聲明了函數(shù)fn 結(jié)果出錯(cuò)。
function fn() {}(1) //1 末尾的括號(hào)作為運(yùn)算符,又必須要提供表達(dá)式做為參數(shù)。
- 寫成下列方式直接調(diào)用時(shí)可以的
var fn = function() {}()//這樣調(diào)用函數(shù)是可以的
- 那么當(dāng)要使用立即執(zhí)行函數(shù)時(shí)可以使用括號(hào)來(lái)引導(dǎo)解析器,指明括號(hào)運(yùn)算符附近是一個(gè)表達(dá)式
( function() {}() );
[ function() {}() ];//當(dāng)括號(hào)出現(xiàn)在匿名函數(shù)的末尾想要調(diào)用函數(shù)時(shí),
它會(huì)默認(rèn)將函數(shù)當(dāng)成是函數(shù)聲明。 function fn() {}
( function() {} )();//默認(rèn)為函數(shù)表達(dá)式 var fn = function() {}
- 也可以使用一元運(yùn)算符來(lái)寫立即執(zhí)行函數(shù)引導(dǎo)解析器
~ function() {}();
! function() {}();
+ function() {}();
- function() {}();
- 甚至可以寫成這樣
delete function() {}();
typeof function() {}();
void function() {}();
new function() {}();
new function() {};
var f = function() {}();
--
1, function() {}();
1 ^ function() {}();
1 > function() {}();
- 什么是函數(shù)的作用域鏈 (****)
- 全局:在web瀏覽器中,全局執(zhí)行環(huán)境就是window對(duì)象,所有的全局變量和函數(shù)都是作為window對(duì)象的屬性(變量)和方法(函數(shù))創(chuàng)建的。
- 函數(shù):每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境,由{}與全局的window對(duì)象分開。
- 作用域鏈:當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)。
var color = "blue";
function changeColor() {
if (color === "blue") {
color = "red";
} else {
color = "blue";
}
}
changeColor();
console.log("Color is now " + color);//Color is now red
在上述代碼中,changeColor()函數(shù)的使用了全局環(huán)境下的變量color。函數(shù)可以在內(nèi)部訪問(wèn)color就是因?yàn)榭梢栽谶@個(gè)作用域鏈中找到它。
var color = "blue";
function changeColor() {
var anotherColor = "red";
console.log(anotherColor, color);
function swapColor() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//這里可以訪問(wèn)color, anotherColor, tempColor
console.log(tempColor, anotherColor, color);
}
//這里可以訪問(wèn)color, anotherColor,
swapColor();
}
//這里只能訪問(wèn)color
console.log( color);
changeColor();

-
以上代碼共有三個(gè)執(zhí)行環(huán)境:
1 window全局環(huán)境:有一個(gè)變量color和一個(gè)函數(shù)changeColor()
2 changeColor()的局部環(huán)境:有一個(gè)變量anotherColor和一個(gè)swapColor()函數(shù),但也可以訪問(wèn)全局環(huán)境中的變量color
3 swapColor()的局部環(huán)境:只有一個(gè)變量tempColor,在這個(gè)函數(shù)內(nèi)可以訪問(wèn)到全局環(huán)境下的變量color,也可以訪問(wèn)changeColor()局部環(huán)境的anotherColor,因?yàn)槠渌麅蓚€(gè)環(huán)境是它的父執(zhí)行環(huán)境。
作用域鏈?zhǔn)疽鈭D 參考:作用域鏈
代碼
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('hunger', 28, '男');
getInfo('hunger', 28);
getInfo('男');
--
name: hunger
age: 28
sex: 男
["hunger", 28, "男"]
name valley
--
name: hunger
age: 28
sex: undefined
["hunger", 28]
name valley
--
name: 男
age: undefined
sex: undefined
["男"]
name valley
2 寫一個(gè)函數(shù),返回參數(shù)的平方和?如 (難度**)
function sumOfSquares(){
var result = 0;
for (var i = 0; i < arguments.length; i++) {
result = result + arguments[i] * arguments[i];
}
return result;
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
3 如下代碼的輸出?為什么 (難度*)
console.log(a);
var a = 1;
console.log(b);
//undefined 在瀏覽器中變量a被提升至頂部先被聲明,執(zhí)行到console.log(a)語(yǔ)句時(shí)變量a還沒(méi)有被賦值。
//error 在整個(gè)全局中b并沒(méi)有被賦值也沒(méi)有被聲明。
4 如下代碼的輸出?為什么 (難度*)
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
//hello world 聲明函數(shù)function sayName(name)被提升到頂部進(jìn)行聲明
所以執(zhí)行sayName('world');時(shí)可以正常運(yùn)行。
//error 函數(shù)表達(dá)式var sayAge = function(age){}中var sayAge被提升到頂部進(jìn)行聲明
這時(shí)候sayAge為undefined,執(zhí)行到sayAge(10);時(shí)為undefined(10)沒(méi)有意義。
5 如下代碼的輸出?為什么 (難度**)
function fn(){}
var fn = 3;
console.log(fn);//3 聲明完變量fn,又聲明函數(shù)fn,最后賦值3給變量fn覆蓋了
6 如下代碼的輸出?為什么 (難度**)
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);//3;
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
/* function fn2(){
console.log('fnnn2');
} */ 執(zhí)行fn(10)時(shí),參數(shù)10被傳遞到函數(shù)fn中并賦值給了fn2,
//在函數(shù)fn中提升聲明變量fn2,并且提升聲明函數(shù)fn2(){},這時(shí)候參數(shù)fn2的值被聲明函數(shù)fn2覆蓋,所以輸出的是fn2函數(shù)
//3 執(zhí)行完console.log(fn2); fn2變成變量并賦值為3
/* function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);//3;
console.log(fn);
function fn2(){
console.log('fnnn2');
}
} */ 執(zhí)行console.log(fn);輸出函數(shù)fn
7 如下代碼的輸出?為什么 (難度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));//error
typeof(fn);//number
在JavaScript中上述代碼的變量和函數(shù)聲明會(huì)提升到最頂部最先聲明,然后再賦值,所以可以寫成:
var fn;
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn));//這個(gè)時(shí)候fn是一個(gè)number,所以不能調(diào)用函數(shù)fn了。
8 如下代碼的輸出?為什么 (難度**)
//作用域
console.log(j);//undefined //for循環(huán)不處在函數(shù)中聲明的變量i和變量j仍然是全局變量,但是沒(méi)賦值。
console.log(i);//undefined //同上
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);//10 //變量i在for循環(huán)中的i<10;i++遞加到10。
console.log(j);//100 //變量j在for循環(huán)中被賦值為100。
9 如下代碼的輸出?為什么 (難度****)
fn();
var i = 10;
var fn = 20;
console.log(i);
function fn(){
console.log(i);
var i = 99;
fn2();
console.log(i);
function fn2(){
i = 100;
}
}
//undefined
//100
//10
上述代碼可以寫成下列形式
var i;
var fn;
function fn(){
/*statement*/
}
fn()//在這里執(zhí)行fn(),下面看fn函數(shù)中是什么樣的
function fn(){
var i;
function fn2(){
i = 100;
}
}
console.log(i);//變量i只被聲明輸出undefined
i = 99;
fn2(); //這里執(zhí)行函數(shù)fn2,變量i在函數(shù)fn2中賦值為100并返回
console.log(i); // 輸出100
}
i = 10;
fn = 20;
console.log(i); //i被賦值為10,輸出10
10 如下代碼的輸出?為什么 (難度5*****)
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
/* 10
9
8
7
6
5
4
3
2
0 */
上述代碼可以寫成
var say;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));//立即執(zhí)行函數(shù),在聲明時(shí)就開始給函數(shù)say傳入?yún)?shù)10并執(zhí)行say函數(shù)
當(dāng)執(zhí)行多次say(n-1)后n=2時(shí)被return返回并跳出函數(shù)say不在執(zhí)行say(n-1)
say = 0//say變量被賦值0并輸出。
console.log(say);
/* 10
9
8
7
6
5
4
3
2
0 */
本博客版權(quán)歸 本人和饑人谷所有,轉(zhuǎn)載需說(shuō)明來(lái)源

