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