函數(shù)
函數(shù), function, 是一個(gè)關(guān)鍵字, 表示聲明一個(gè)函數(shù), 是一個(gè)變量的特例.
每一個(gè)函數(shù),都必須有一個(gè)return, 如果不寫return, 默認(rèn)會(huì)添加return undefined.
函數(shù)本質(zhì)
函數(shù)就是將做一件相同事情的代碼整合到一起,作為代碼塊, 可以被反復(fù)調(diào)用,可以執(zhí)行代碼的對(duì)象就是函數(shù).
函數(shù)的五種聲明方式
- 具名函數(shù)
function x(input1, input2){ return }
- 匿名函數(shù)
var x = function(input1, input2){ return }
// 匿名函數(shù)必須給一個(gè)變量
- 具名函數(shù)賦值
var x = function y(input1, input2){ return }
- Window.function
var x = new Function('x', 'y', 'return x + y')
- 箭頭函數(shù)
var x = (x, y) => { return x + y}
var x = (x, y) => { let n = x * 3; let m = y * 4; return n + m;}
具名函數(shù)和具名函數(shù)賦值兩種聲明的區(qū)別:
不一致性, 作用域不同, 垃圾特性.
分別聲明具名函數(shù)和具名函數(shù)賦值, 并console.log(函數(shù)):
console.log()原理
console.log = function(a){
alert(a)
return undefined
}
// 由此可見(jiàn), console.log 永遠(yuǎn)返回undefined, 返回什么和打印什么 一點(diǎn)關(guān)系沒(méi)有
函數(shù)的name屬性
var f2 = function(){}
f2.name = "f2";
var f3 = function f4(){}
f3.name = "f4"
var f5 = new Function('x','y','return x + y')
f5.name = "anonymous" // 匿名的意思
如何調(diào)用函數(shù)
function f(x,y){ return x + y }
f(1,2) // 3
// 骨灰級(jí)寫法, 真正的函數(shù)調(diào)用
f.call(undefined, 1, 2)
call()原理:
var f = new Function('x','y','console.log(1)')
f.call() // 1
// Function構(gòu)造函數(shù), 除了最后一個(gè)參數(shù)是函數(shù)體, 其他的都是參數(shù)
// 分解如下
var f = {};
f.parmas = ['x','y'];
f.functionBody = 'console.log(1)';
f.call = function(){
window.eval(f.functionBody)
}
f.call() // 1
由上可得出: 為什么說(shuō)函數(shù)是對(duì)象, 因?yàn)檎{(diào)用的過(guò)程就是執(zhí)行eval函數(shù)體的過(guò)程, 可以執(zhí)行代碼的對(duì)象就是函數(shù)
f和f.call()的區(qū)別:
f 是對(duì)象, f.call 是執(zhí)行對(duì)象的函數(shù)體
this 和 arguments
call()的第一個(gè)參數(shù)可以用this得到.后面的參數(shù)可以用arguments得到.
this
上述代碼中f.call(undefined, 1, 2), undefined就是this, 當(dāng)時(shí)要求JS必須長(zhǎng)的像java, 所有強(qiáng)行加了this
arguments
上述代碼中f.call(undefined, 1, 2), [1,2]就是arguments.
-
arguments也是一個(gè)偽數(shù)組, 因?yàn)殚L(zhǎng)的像數(shù)組, 原型鏈中沒(méi)有Array.prototype, 或者是____proto____沒(méi)有指向Array.prototype.
function f(){
console.log(this)
}
f.call(1) // Number 對(duì)象 1
function f(){
'use strict' // 嚴(yán)格模式
console.log(this)
}
f.call(1) // 1
作用域
作用域(scope)指的是變量存在的范圍。在 ES5 的規(guī)范中,Javascript 只有兩種作用域:一種是全局作用域,變量在整個(gè)程序中一直存在,所有地方都可以讀?。涣硪环N是函數(shù)作用域,變量只在函數(shù)內(nèi)部存在.只要有函數(shù)就有作用域
作用域?qū)?yīng)的數(shù)據(jù)結(jié)構(gòu)是樹(shù):
var a = 1;
function f(){
console.log(a)
var a = 2;
function f2(){
console.log(a)
}
}
function f1(){
var a = 3;
console.log(a)
}
console.log(a)
f()
// 代碼作用域如下圖:

函數(shù)外部聲明的變量就是全局變量(global variable),它可以在函數(shù)內(nèi)部讀取。
在函數(shù)內(nèi)部定義的變量,外部無(wú)法讀取,稱為“局部變量”(local variable)。
函數(shù)內(nèi)部的變量提升
與全局作用域一樣,函數(shù)作用域內(nèi)部也會(huì)產(chǎn)生“變量提升”現(xiàn)象。var命令聲明的變量,不管在什么位置,變量聲明都會(huì)被提升到函數(shù)體的頭部。
function f(){
console.log(a);
var a = 1;
}
// 等同于
function f(){
var a;
console.log(a);
var a = 1;
}
// a 打印出undefined
函數(shù)本身的作用域
函數(shù)本身也是一個(gè)值,也有自己的作用域。它的作用域與變量一樣,就是其聲明時(shí)所在的作用域,與其運(yùn)行時(shí)所在的作用域無(wú)關(guān)。
var a = 1;
function x(){
console.log(a);
}
function f(){
var a = 2;
x();
}
f(); // 1
// 等同于
var a;
a = 1;
function x(){
console.log(a); // 1
}
function f(){
var a;
a = 2;
x();
}
f();
上面代碼中,函數(shù)x是在函數(shù)f的外部聲明的,所以它的作用域綁定外層,內(nèi)部變量a不會(huì)到函數(shù)f體內(nèi)取值,所以輸出1,而不是2。
總之,函數(shù)執(zhí)行時(shí)所在的作用域,是定義時(shí)的作用域,而不是調(diào)用時(shí)所在的作用域.
閉包
如果一個(gè)函數(shù), 使用了它范圍外的值, 那么(這個(gè)函數(shù)+這個(gè)變量)就叫做閉包.
