前端 — 初級階段5(13)

ECMAScript核心語法結構之函數詳解

1、函數的概念

函數是一段能夠反覆調用的代碼塊。接受輸入的參數,不一樣的參數會返回不一樣的值。javascript

1.函數的定義

ECMAScript中是使用function關鍵字來聲明,後面跟一組參數以及函數體。html

1) 聲明函數前端

function sum (num1,num2){
     return num1 + num2;
 }
//能夠先使用後定義。 解析器在執行環境中加載數據時,會率先讀取函數聲明。

2) 函數表達式:將一個匿名函數賦值給一個變量java

var sum = function (num1,num2){
       return num1 + num2;
  }
  //必須定義後使用(不然會報錯)。到解析器執行到它所在的代碼行,纔會真正被解析執行

3) 構造函數(幾乎不用)es6

var sum = new Funtion(
    'num1',
    'num2',
    'return num1 + num2'
) 
//等同於上面的聲明函數

2.函數的調用(括號運算符)

函數能夠經過其函數名調用,後面還要加上一對圓括號和參數(若是有多個參數,用逗號隔開)ajax

function sum(num1, num2) {
    return num1 + num2;
}
sum(1, 1)  //2

3.函數的參數

1.什麼是參數數組

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

function sum (num1,num2){
     return num1 + num2;
}
sum(1,2); //3
sum(1)    //NAN  num2 undefind;

2.參數的傳遞方式 - 按值傳遞app

(1) 基本類型值的傳遞如同基本類型變量的複製同樣。
(2) 引用類型值的傳遞則如同引用類型變量的複製同樣(將這個值在內存中的地址複製給了一個局部變量,所以這個局部變量的變化就會反在函數的外部)ide

//基本類型的傳遞
function addTen(num) { 
     num += 10; 
     return num; 
}
var count = 20; 
var result = addTen(count); 
alert(count); //20,沒有變化
alert(result); //30

//引用類型的傳遞
var person = new Object(); 
function setName(obj) { 
    alert(person.name); //undefined
    obj.name = "Nicholas"; 
    alert(person.name)  //"Nicholas"
} 
setName(person); 
alert(person.name); //"Nicholas"

3.arguments對象(讀取函數體內部全部參數)

  • arguments對象是一個類數組(有length屬性,但沒有數組的任何方法)
  • 能夠編寫無需指定參數個數的函數
var sum=function(){
    var sum=0;
    for(var i=0;i<arguments.length;i++){
        sum += arguments[i];
    }
    return sum;
}
sum(1,2,3)

4. 函數的返回值(return)

函數能夠經過return語句跟要返回的值來實現返回值。函數在執行完return語句以後中止並當即退出。所以,在return語句以後的任何代碼永遠都會回執行。

function sum(num1, num2) {
    return num1 + num2;  
    alert('Hello world') //永遠不會執行
}

return語句不是必須的,若是沒有的話,該函數就不會返回任何值,或者說返undefined;

function sum(num1, num2) {
    alert (num1 + num2)  
}     //undefined

5.函數沒有重載(函數的重複聲明)

函數重載:在其餘語言(Java)中能夠爲一個函數體編寫兩個定義,只要定義的簽名(接收的參數的類型和參數不一樣便可))
在ECMASctipt中若是同一個函數被屢次聲明,後面的聲明就會覆蓋前面的聲明。

function addSomeNumber(num){
    return num + 100;
}
function addSomeNumber(num) { 
   return num + 200; 
}
var result = addSomeNumber(100); //300

6.函數的屬性和方法

(1) name屬性(返回name的名字)

function sum (){}
 sum.name // 'sum'

(2) length 屬性

函數的length屬性返回函數預期傳入的參數個數,即函數定義之中的參數個數。

function f(a,b){
    return f.length 
}
f(1,2,3,4)  //2 ==> length屬性就是定義是的參數個數。無論調用時傳入了多少參數,length屬性的始終等於2

(3)apply()和call()
每一個函數都包含兩個非繼承而來的方法:apply()和和call()。這兩個方法的用途都是在特定的做用域中調用函數,實際上等於設置函數體內this對象的值。
apply()方法接收兩個參數,一個是在其運行函數體內的做用域,另外一個是參數數組。其中第二個參數是數組或者arguments對象。
call()方法接收兩個參數,第一個參數是this值沒有變化,變化的是其他參數都是直接傳遞給函數(的參數必須逐個列舉出來)

1.傳參

function sum(num,num2){
      return num1 + num2;
  }
  function callSum(){
      return sum.call(this,num1,num2);
  }
  alert(callSum(10,10)); //20

2.擴充函數賴以運行的做用域 (改變this指向的值)

var color = 'red';
var o = {
    color: 'blue'
}
sayColor.call();       //red(默認傳遞參數this)
sayColor.call(this);   //red
sayColor.call(window); //red 
sayColor.call(o);      //blue

2、函數的做用域和做用域鏈

1.做用域

(1)什麼是做用域
做用域(scope)指的是變量存在的範圍。 在es5中的,Javascript只有兩種做用域:一種是全局做用域,變量在整個程序中一直存在,全部地方均可以讀取;另外一種是函數做用域,變量只在函數內部存在。(es6中又新增了塊級做用域)
全局做用域能夠在代碼的任何地方都能被訪問

var color1 = 'blue';     
function colorFn (){
    var color2 = 'red';  
    color3 = 'yellow';  
    console.log(color1); 
    console.log(color2);
    console.log(color3);
}
colorFn();
console.log(color1);   //'blue' 
console.log(color2);   // error
console.log(color3);   //'yellow'

//函數changeColor()的做用域鏈包含兩個對象:它本身的變量對象(定義着arguments對象)和全局的變量的對象

2.做用域鏈

1.當代碼在一個執行環境中執行時,就會建立一個做用域鏈。

2.做用域鏈的用途,是爲了保證對執行環境有權訪問的全部變量和函數的有序的訪問。即全局做用域和局部做用域的變量的訪問權限是由做用域鏈決定的。

3.做用域鏈的是從當前的執行環境的變量對象在這個環境中能夠訪問的變量對象開始,到全局執行環境的變量對象結束。(在這個環境中能夠訪問的變量)

4.內部環境能夠經過做用域鏈訪問全部外部環境,可是外部環境不能訪問內部環境中的任何變量和函數。

5.標示符(變量、函數、屬性的名字,或者函數的參數)的解析是沿着做用域鏈一級一級的搜索標示符的過程。搜索過程始終從做用域鏈的前端開始,而後逐級的向後回溯,直到找到標識符(若是找不到標識符,一般會報錯)。

//在局部做用域中定義的變量能夠在局部環境中與全局變量中互換使用
var color = 'blue';
function changeColor(){
     var anotherColor = 'red';
     function swapColors(){
          var tempColor = anotherColor;
          anotherColor = color;
          color = tempColor;
          //這裏能夠訪問color、anotherColor和tempColor
      }
      //這裏能夠訪問color和anotherColor,但不能訪問tempColor
      swapColors();
}
//這個只能訪問color;
changeColor();

圖片描述

3、函數的其餘知識點

1.閉包函數

(1) 什麼是閉包函數
閉包函數是指有權訪問另外一個函數做用域中的變量的函數
(2) 建立閉包的常見方式,就是在一個函數內部建立另外一個函數

//在函數內部能夠讀取函數外部的變量
var localVal = 30;
function outer(){
    return localVal;
}
outer();//30

//在函數外部天然沒法讀取函數內的局部變量
function outer(){
     var localVal = 30;
}
alert(localVal);//error

//經過閉包函數來讀取讀取outer函數內部的變量
function outer(){
     var localVal = 30;
        function inner(){   
          alert(localVal);
     }
     return inner; 
}
var func = outer();
func();//30

(3) 閉包的優缺點

1) 使用閉包的好處是不會污染全局環境,方便進行模塊化開發,減小形參個數,延長了形參的生命週期
2) 閉包會把函數中變量的值保存下來,供其餘函數使用,這些變量會一直保存在內存當中佔用大量的內存,使用不當會形成內存泄漏

2.回調函數

(1) 什麼是回調函數(做爲值的函數)

回調函數也被稱做高階函數,是一個被做爲參數傳遞給另外一個函數的函數。
function someFunction(someNum,callback){
    return callback(someNum);
}
function add10(){
    return num + 10;
}
someFunction()

(2) 應用場景實例:

1.數組中的一些方法 sort()、reverse()、forEach()、filter()等
     var friends = ["Mike", "Stacy", "Andy", "Rick"];
     friends.forEach(function (eachName, index){
        console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
     }); 
       
2.事件的綁定
     //原生綁定事件
    document.body.addEventListener("cilck",function(){
        alert("body Clicked");
    });
    
    //jq
    $("#btn_1").click(function() {
      alert("Btn 1 Clicked");
    }); 
     
3.ajax的請求
    function getAjax (options, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('get', options.url);
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){
                if(xhr.status == 200){
                    callback && callback(xhr.responseText);
                }
            }
        };
        xhr.send();
    }

3.遞歸函數

1.什麼是遞歸函數
遞歸函數是一個在函數經過名字調用自身的狀況下構成的。遞歸函數必需要有邊界條件

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

2.arguments.calls()是一個指向正在執行的函數指針 (嚴格模式下不支持)
//當函數名發生改變時會致使代碼出錯

var anotherFactorial = factorial; 
factorial = null; 
alert(anotherFactorial(4)); //出錯!

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

3.嚴格和非嚴格都有效

var factorial = (function f (num){
        if(num == 1){
            return 1;
        } else {
            return f(num-1)
        }
   })

4.自執行函數

(1)什麼是執行函數
當即執行函數,就是在定義的時候當即執行的函數,執行完之後釋放,包括函數內部的全部變量(執行完畢,當即銷燬其做用域鏈)

(2)當即執行函數的寫法

1.經常使用寫法
    (function(){
    })();

2.w3c建議方式
    (function(){
    }());

3.錯誤寫法
    function(){
    }();
  • 函數聲明後面不能跟花括號(js會把function關鍵字當作一個函數聲明的開始)
  • 函數表達式的後能夠跟圓括號。要將函數聲明轉變成函數表達式
  • 除圓括號外,!、+、-、=等運算符,都將函數聲明轉換成函數表達式

    !function(){}();
      +function(){}();
      -function(){}();
      var fn=function(){}();

(3) 模仿塊級做用域

function outputNumbers (count){
    for(var i=0; i<count;i++){
        alert(i)
    }
    var i;
    alert(i);
    //計數 i從有定義開始,在函數內部就能夠訪問它,即便從新聲明一個變量也不會改變它的值(不會改變?)。
}
function outputNumbers (count){
    (function(){
        for(var i=0; i<count;i++){
            alert(i)
        }
    })(); //變量會在當即執行結束後會被當即銷燬
    alert(i);
}
//能夠避免過多全局變量和函數致使命名衝突;這個做用域裏面的變量,外面訪問不到(即避免「變量污染」)。

3.應用場景

<ul class="box">
    <li>0</li>
    <li>1</li>
    <li>3</li>
    <li>4</li>
</ul>
var list = document.getElementsByTagName('li');
for (var i = 0, len = list.length; i < len; i++) { 
    (function(i){   //爲了得到不一樣的i值,使用當即調用函數
        list [i].onmouseover = function() {
            console.log('index is :' + i);
        }
    })(i);
}

參考:
JavaScript高級程序設計(第3版)
https://wangdoc.com/javascrip...
https://www.cnblogs.com/caoru... JavaScript函數之做用域 / 做用鏈域 / 預解析
https://www.cnblogs.com/bucho... JavaScript中做用域和做用域鏈的簡單理解(變量提高)
https://www.cnblogs.com/jingw... 閉包
https://www.cnblogs.com/langq...
https://www.cnblogs.com/mengf...
https://www.cnblogs.com/mafei... 當即執行函數
https://baijiahao.baidu.com/s... 當即執行函數

相關文章
相關標籤/搜索