[記錄] JavaScript 中的函數

函數:[JavaScript 教程]
函數是一段能夠反覆調用的代碼塊。能夠傳遞參數,不一樣的參數會返回不一樣的值。

函數聲明的三種方法:
1. function 命令
function 命令聲明的代碼塊,就是一個函數。 function 命令後面是函數名,函數名後面是一對圓括號(), 裏面能夠傳入參數。函數體放在大括號裏面。javascript

function show(name) {
	// 代碼塊...
	console.log( name );
}


2. 函數表達式
除了用 function 命令聲明函數, 還能夠採用變量賦值的寫法。html

var show = function(name) { 
	// 代碼塊...
	console.log( name );
};

這種寫法是將一個匿名函數賦值給變量。由於賦值語句的等號右側只能放表達式,全部這個匿名函數稱函數表達式。

帶名的函數表達式,函數名在函數體內有效,在函數體外部無效。 java

var show = function abc() {
	// 代碼塊...
    // 命名函數表達式 
	// 每一個函數都會有一個name的屬性,這裏的區別就在於 show.name  是 abc
};

注意: 函數做爲表達式出現,則會忽略名稱。函數表達式須要在語句結尾加上分號,表示語句結束。

3. Function 構造函數數組

Function 構造函數能夠不使用new命令,返回結果徹底同樣。這種聲明方式少人使用。
var show = new Function(
	"x",
	"y",
	"return x + y"
);
// 等同於
function show(x, y) {
	return x + y;
}
Function 構造函數的最後一個參數會被看成函數體,若是隻有一個參數,該參數就是函數體。


函數調用:
有4種調用模式:函數調用模式、方法調用模式、構造器調用模式、間接調用模式;

1. 函數調用模式 閉包

function init(){
	// 代碼塊...
	// 非嚴格模式下,this指向全局對象window
	// 'use strict'
	// 嚴格模式下, this是undefined
}
init(); // window.init();
能夠經過 this 來判斷當前是不是嚴格模式
var strict = (function(){ return !this; }());


2. 方法調用模式
當一個函數被保存爲對象的一個屬性時,咱們稱它爲一個方法。當一個方法被調用時,this被綁定到該對象。 app

var myObject = {
	value: 100,
	init: function() {
		console.log( this.value );
	}
}
myObject.init(); // 100


3. 構造器調用模式,用new關鍵字來新建一個函數對象的調用; (this指向被綁定到的構造函數實例上)ide

var init = function (status) {
	this.status = status;
}
init.prototype.getStatus = function () {
	return this.status;	
};
var test = new init('構造函數');
console.log( test.getStatus() ); // 構造函數; this指向test


4. 間接調用模式 (this指向指定調用的對象)
call()、apply()是函數的兩個方法,能夠用來間接地調用函數。函數

var obj = {};
function show(x, y) {
	return x + y;
}
show.call(obj, 1, 2); //3 this => obj
show.apply(obj, [1, 2]); //3 this => obj


函數的重複聲明:性能

變量的重複聲明是無用的,不會覆蓋以前同一做用域聲明的變量。
但函數的重複聲明會覆蓋前面的聲明的同名函數或同名變量。
變量的重複聲明變量無用this

 
var num = 10;
var num;
console.log( num ); // 10 

覆蓋同名變量

 
var num;
function num() {
    console.log(1);
}
num(); // 1

覆蓋同名函數

 
function num() {
    console.log("A");
}
num(); // B

function num() {
    console.log("B");
}


JavaScript 引擎將函數名視同變量名,全部採用 function 命令聲明函數,都會被提高到代碼頭部。

// 先調用
show();
// 後聲明,因爲"變量提高", 因此代碼能夠正常執行
function show() { .. }

採用賦值語句定義函數,先調用,再聲明就會報錯。
show();
var show = function() {}
// TypeError: show is not a function
等同如下形式
var show;
show();
show = function() {};

注意: 同時採用 function 命令 和 賦值語句 聲明同一個函數,最後採用的老是賦值語句定義的。
var show = function() {
	console.log("A");	
};
function show() {
	console.log("B");
}
show();  // A


函數自己的做用域:
函數執行時所在的做用域,是定義時的做用域,而不是調用時所在的做用域。

函數的屬性和方法:
A. name 屬性

函數的 name 屬性返回函數的名字
function show() {}
show.name; // show

變量賦值定義的函數,name 屬性返回變量名。
var show = function () {
	// 匿名函數表達式
};
show.name; // show

var show = function abc() {
	// 有名函數表達式
}
show.name; // abc


B. length 屬性 (函數形參的個數)

function show(a, b){
	// a,b 是函數的形參
}
show.length;  // 2


C. toString() 方法: 返回一個字符串,內容是函數的源碼;

function show() {
	// 註釋
	hide();
}
show.toString();
結果:
"function show() {
	// 註釋
	hide();
}"


關於刪除:

function 命令聲明函數沒法刪除,和變量聲明同樣
function show() {
	console.log("A");
}
delete show; // false
show(); // A


關於函數返回值:(return 有終止程序的做用)
全部函數都有返回值,沒有return語句時,默認返回內容是undefined,return語句不會阻止finally的執行

function show() {
	try{
		return "A";
	}catch(error) {
		return "B";
	}finally{
		return "C";
	}
}
show(); // C


做爲構造函數使用時,在調用以前前面加上了new,若是有return語句,返回值不是一個對象,則會忽略,則返回值是this(new出來的新對象);

function show() { // 約定:若是一個函數要作爲構造函數的話,首字母需大寫。
	this.num = 2;
	return 1; // 返回的是一個基本類型,構造函數會忽略
}
var test = new show();
console.log( test ); // { num: 2 }
console.log( test.constructor ); // show(){ this.num = 2; return 1; }


若是返回值是一個對象,則返回該對象。

function show() {
	this.num = 2;
	return { num: 1 };
}
var test = new show();
console.log( test ); // { num: 1 }
console.log( test.constructor ); // Object(){ [native code] }


關於函數的參數:
函數運行的時候,有時須要提供外部數據,不一樣的外部數據會獲得不一樣的結果,這種外部數據就叫參數。

// 在函數聲明時圓括號內的參數,叫形式參數 - 簡稱形參;

function test(a, b, c){
	// 形參 - 在函數內部至關於新定義的變量
	// var a, b, c;
	// 獲取形參的長度: test.length;

	// arguments 是函數的實參列表, 記錄的是函數實際傳入的參數
    // [1,2,3] arguments一個類數組 

	// 形參個數 和  實參列表arguments一一對應,若有一方修改則都改,
    // 例外:當實參小於形參時,修改形參變量,對應的arguments爲undefined 
	
	return a;
}


// 函數調用的時候摻入的參數,叫作實際參數 - 簡稱實參;

test(1, 2); // 1; 實參比形參多, 則會忽略多傳入的實參
test(); // undefined; 形參比實參多, 內部用到則默認爲undefined
test( , 1); // 不能省略靠前的參數,若是要省略只有顯示傳入undefined
// 報錯 SyntaxError: Unexpected token ,(...)
test(undefined, 1); // undefined


同名形參: (在實際開發過程當中確定是不允出現的)
A. 在非嚴格模式下,函數中能夠出現同名形參,且只能訪問最後出現的該名稱的形參。

function test(a, a, a) {
	console.log(a);
}
test(1,2,3); // 3
test(1,2); // undefined


B. 在嚴格模式下,出現同名形參會拋出語法錯誤。

function test(a,a,a){
	'use strict';
	return a;
}
test(1,2,3); // SyntaxError: Duplicate parameter name not allowed in this context


以對象爲參數:
當一個函數的形參過多時,在調用的時候必須傳入和形參對應的數據。

function show(name, sex, age) {
	// 在傳入實參數是必須一一對應,name(名稱),sex(性別),age(年齡)
	console.log('姓名:' + name ',性別:' + sex + ', 年齡:' + age);
}
正常順序
show('jing', '女', '18'); // 姓名:jing,性別:女, 年齡:18
錯誤順序
show('jing', '18', '女'); // 姓名:jing,性別:18, 年齡:女

經過key/value對的形式來傳入參數,參數的順序就可有可無了。
function show(option) {
	console.log(
        '姓名:' + option.name + 
        ',性別:' + option.sex +
        ', 年齡:' + option.age
    );
}
show({age: 18, name:'jing', sex: '女'}); // 姓名:jing,性別:女, 年齡:18


以函數爲參數:
函數自己是一個對象,所以能夠將函數做爲另外一個函數的參數,實現函數回調。

function show( fn ) {
	// 判斷當前傳入的參數類型是不是函數,若是是函數則當即執行;
	if(typeof(fn) === 'function'){
		fn(); // 執行回調
	}
}
show(function(){
	console.log('A');
}); // A


arguments對象:(函數內部的實參數列表)
因爲JavaScript容許函數有不定數目的參數,因此須要一種機制,能夠在函數體內部讀取全部參數。這是就arguments對象的由來。
arguments對象包含了函數運行時的全部參數,arguments[下標]來獲取,只有在函數體內纔可使用。

function show() {
	console.log(arguments[0]);
	console.log(arguments[1]);
}
show("A", "B"); 
// A
// B


當形參和實參的格式相同時,arguments對象的值和對應的形參的值保持同步

function show(num1, num2) {
	console.log( num1, arguments[0] ); // 1 1
	arguments[0] = 7;
	console.log( num1, arguments[0] ); // 7 7
	num1 = 10;
	console.log( num1, arguments[0] ); // 10 10
}
show(1);
注意: 雖然形參和實參列表的值相同,但不是相同的命名空間。他們的命名空間是獨立的,但值是同步的。


在嚴格模式下,arguments對象的值和形參的值是 獨立的

function show(num1, num2) {
	'use strict'
	console.log( num1, arguments[0] ); // 1 1
	arguments[0] = 7;
	console.log( num1, arguments[0] ); // 1 7
	num1 = 10;
	console.log( num1, arguments[0] ); // 10 7
}
show(1);


當形參並無對應的實參時,arguments對象的值和形參的值並不對應

function show(num1, num2) {
	console.log( num1, arguments[0] ); // undefined, undefined
	num1 = 10;
	arguments[0] = 7;
	console.log( num1, arguments[0] ); // 10 7
}
show();


arguments對象的內部屬性callee
該屬性是一個指針,指向擁有這個arguments對象的函數。

var show = function() {
	console.log( arguments.callee === show );
}
show(); // true
能夠經過 arguments.callee 達到調用函數自身的目的。
注意:這個屬性在嚴格模式下是禁用的。


經過遞歸(函數調用自身)實現的階乘函數

function factorial(num) {
	if( num <= 1 ){
		return 1;
	}else{
		return num * factorial(num-1);
	}
}
factorial(5); // 120


使用arguments.callee消除函數解耦

function factorial(num) {
	if( num <= 1 ){
		return 1;
	}else{
		return num * arguments.callee(num-1);
	}
}
factorial(5); // 120

閉包:
理解閉包,必須理解變量做用域。
JS的兩種做用域: 全局做用域和函數做用域。函數內部能夠直接讀取全局變量。
// 函數內部讀取全局變量

var num = 10;
function show() {
	cosnole.log( num );
}
show(); // 10


函數外部沒法讀取函數內部聲明的變量。

function show() {
	var num = 10;
}
console.log( num );
// ReferenceError: num is not defined


鏈式做用域,子對象能夠向上尋找父對象的變量。
// 函數內部嵌套函數,內部函數能夠訪問外部的變量

function show(){
	var num = 10;
	function show2() {
		console.log( num ); // 10
	}
	return show2;
}


函數 show 的返回值是函數 show2, 因爲 show2 能夠讀取 show 的內部變量, 因此就能夠在外部得到 show 的內部變量了。 show2 函數就是閉包, 可以讀取其餘函數內部變量的函數。
只有函數內部的子函數才能讀取內部變量,簡單理解成,定義在一個函數內部的函數。
閉包最大的特色,就是它能夠記住誕生的環境,好比 show2 記住了它誕生的環境 show,因此從show2能夠獲得show的內部變量。 閉包就是將函數內部和函數外部鏈接起來的一座橋樑。

閉包的最大的兩個用處,1. 能夠讀取函數內部變量。 2. 讓這些變量始終保持在內存中。

function count(start) {
	return function () {
		return start++;
	}
}
var init = count(5);
init(); // 5;
init(); // 6;
init(); // 7
經過閉包,start的狀態被保留了,
每一次調用都是在上一次調用的基礎上進行計算。


閉包的另外一個用處: 是封裝對象的私有屬性和私有方法。

function Person(name) {
	var _age;
	function setAge(n) {
		_age = n;
	}
	function getAge() {
		return _age;
	}

	return {
		name: name,
		getAge: getAge,
		setAge: setAge
	};
}
var p1 = Person('yuxi');
p1.setAge(18);
p1.getAge(); // 18
函數 Person 的內部變量 _age, 經過閉包 getAge 和 setAge,變成了返回對象 p1 的私有變量。

注意: 外層函數每次運行,都會生成一個新的閉包,而這個閉包又會保留外層函數的內部變量,因此會消耗很大內存。所以不能濫用閉包,不然會形成網頁的性能問題。

相關文章
相關標籤/搜索