函式
函式, function
, 是一個關鍵字, 表示宣告一個函式, 是一個變數的特例.
每一個函式,都必須有一個return
, 如果不寫return
, 預設會新增return undefined
.
函式本質
函式就是將做一件相同事情的程式碼整合到一起,作為程式碼塊, 可以被反覆呼叫,可以執行程式碼的物件就是函式.
函式的五種宣告方式
- 具名函式
function x(input1, input2){ return }
複製程式碼
- 匿名函式
var x = function(input1, input2){ return }
// 匿名函式必須給一個變數
複製程式碼
- 具名函式賦值
var x = function y(input1, input2){ return }
複製程式碼
- Window.function
var x = new Function('x', 'y', 'return x + y')
複製程式碼
- 箭頭函式
var x = (x, y) => { return x + y}
var x = (x, y) => { let n = x * 3; let m = y * 4; return n + m;}
複製程式碼
具名函式和具名函式賦值兩種宣告的區別:
不一致性, 作用域不同, 垃圾特性.
分別宣告具名函式和具名函式賦值, 並console.log(函式)
:
console.log()原理
console.log = function(a){
alert(a)
return undefined
}
// 由此可見, console.log 永遠返回undefined, 返回什麼和列印什麼 一點關係沒有
複製程式碼
函式的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" // 匿名的意思
複製程式碼
如何呼叫函式
function f(x,y){ return x + y }
f(1,2) // 3
// 骨灰級寫法, 真正的函式呼叫
f.call(undefined, 1, 2)
複製程式碼
call()原理:
var f = new Function('x','y','console.log(1)')
f.call() // 1
// Function建構函式, 除了最後一個引數是函式體, 其他的都是引數
// 分解如下
var f = {};
f.parmas = ['x','y'];
f.functionBody = 'console.log(1)';
f.call = function(){
window.eval(f.functionBody)
}
f.call() // 1
複製程式碼
由上可得出: 為什麼說函式是物件, 因為呼叫的過程就是執行eval函式體的過程, 可以執行程式碼的物件就是函式
f和f.call()的區別:
f 是物件, f.call 是執行物件的函式體
this 和 arguments
call()
的第一個引數可以用this
得到.後面的引數可以用arguments
得到.
this
上述程式碼中f.call(undefined, 1, 2)
, undefined
就是this
, 當時要求JS必須長的像java, 所有強行加了this
arguments
上述程式碼中f.call(undefined, 1, 2)
, [1,2]
就是arguments
.
arguments
也是一個偽陣列, 因為長的像陣列, 原型鏈中沒有Array.prototype
, 或者是____proto____沒有指向Array.prototype
.
function f(){
console.log(this)
}
f.call(1) // Number 物件 1
function f(){
'use strict' // 嚴格模式
console.log(this)
}
f.call(1) // 1
複製程式碼
作用域
作用域(scope)指的是變數存在的範圍。在 ES5 的規範中,Javascript 只有兩種作用域:一種是全域性作用域,變數在整個程式中一直存在,所有地方都可以讀取;另一種是函式作用域,變數只在函式內部存在.只要有函式就有作用域
作用域對應的資料結構是樹:
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()
// 程式碼作用域如下圖:
複製程式碼
-
函式外部宣告的變數就是全域性變數(global variable),它可以在函式內部讀取。
-
在函式內部定義的變數,外部無法讀取,稱為“區域性變數”(local variable)。
函式內部的變數提升
與全域性作用域一樣,函式作用域內部也會產生“變數提升”現象。var
命令宣告的變數,不管在什麼位置,變數宣告都會被提升到函式體的頭部。
function f(){
console.log(a);
var a = 1;
}
// 等同於
function f(){
var a;
console.log(a);
var a = 1;
}
// a 列印出undefined
複製程式碼
函式本身的作用域
函式本身也是一個值,也有自己的作用域。它的作用域與變數一樣,就是其宣告時所在的作用域,與其執行時所在的作用域無關。
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();
複製程式碼
上面程式碼中,函式x
是在函式f
的外部宣告的,所以它的作用域繫結外層,內部變數a
不會到函式f
體內取值,所以輸出1
,而不是2
。
總之,函式執行時所在的作用域,是定義時的作用域,而不是呼叫時所在的作用域.
閉包
如果一個函式, 使用了它範圍外的值, 那麼(這個函式+這個變數)就叫做閉包.