1. 程式人生 > >Web前端學習筆記——JavaScript之陣列、函式、作用域

Web前端學習筆記——JavaScript之陣列、函式、作用域

陣列

為什麼要學習陣列

之前學習的資料型別,只能儲存一個值(比如:Number/String。我們想儲存班級中所有學生的姓名,此時該如何儲存?

陣列的概念

所謂陣列,就是將多個元素(通常是同一型別)按一定順序排列放到一個集合中,那麼這個集合我們就稱之為陣列。

陣列的定義

陣列是一個有序的列表,可以在陣列中存放任意的資料,並且陣列的長度可以動態的調整。

通過陣列字面量建立陣列

// 建立一個空陣列
var arr1 = []; 
// 建立一個包含3個數值的陣列,多個數組項以逗號隔開
var arr2 = [1, 3, 4]; 
// 建立一個包含2個字串的陣列
var arr3 = ['a', 'c'
]; // 可以通過陣列的length屬性獲取陣列的長度 console.log(arr3.length); // 可以設定length屬性改變陣列中元素的個數 arr3.length = 0;

獲取陣列元素

陣列的取值

// 格式:陣列名[下標]	下標又稱索引
// 功能:獲取陣列對應下標的那個值,如果下標不存在,則返回undefined。
var arr = ['red',, 'green', 'blue'];
arr[0];	// red
arr[2]; // blue
arr[3]; // 這個陣列的最大下標為2,因此返回undefined

遍歷陣列

遍歷:遍及所有,對陣列的每一個元素都訪問一次就叫遍歷。

陣列遍歷的基本語法:

for(var i = 0; i < arr.length; i++) {
	// 陣列遍歷的固定結構
}

陣列中新增元素

陣列的賦值

// 格式:陣列名[下標/索引] = 值;
// 如果下標有對應的值,會把原來的值覆蓋,如果下標不存在,會給陣列新增一個元素。
var arr = ["red", "green", "blue"];
// 把red替換成了yellow
arr[0] = "yellow";
// 給陣列新增加了一個pink的值
arr[3] = "pink";

氣泡排序,從小到大

<!DOCTYPE html>
<html lang="en">
<
head> <meta charset="UTF-8"> <title>$永遠的24k純帥$</title> <script> //氣泡排序:把所有的資料按照一定的順序進行排列(從小到大,從大到下) var arr = [10, 0, 100, 20, 60, 30]; //迴圈控制比較的輪數 for (var i = 0; i < arr.length - 1; i++) { //控制每一輪的比較的次數 for (var j = 0; j < arr.length - 1 - i; j++) { if (arr[j] < arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log(arr); </script> </head> <body> </body> </html>

函式

為什麼要有函式

如果要在多個地方求1-100之間所有數的和,應該怎麼做?

什麼是函式

把一段相對獨立的具有特定功能的程式碼塊封裝起來,形成一個獨立實體,就是函式,起個名字(函式名),在後續開發中可以反覆呼叫

函式的作用就是封裝一段程式碼,將來可以重複使用

函式的定義

  • 函式宣告
function 函式名(){
  // 函式體
}
  • 函式表示式
var fn = function() {
  // 函式體
}
  • 特點:

    函式宣告的時候,函式體並不會執行,只要當函式被呼叫的時候才會執行。 函式一般都用來幹一件事情,需用使用動詞+名詞,表示做一件事情 tellStory sayHello

函式的呼叫

  • 呼叫函式的語法:
函式名();
  • 特點:

    函式體只有在呼叫的時候才會執行,呼叫需要()進行呼叫。 可以呼叫多次(重複使用)

程式碼示例:

// 宣告函式
function sayHi() {
  console.log("吃了沒?");
}
// 呼叫函式
sayHi();

// 求1-100之間所有數的和
function getSum() {
  var sum = 0;
  for (var  i = 0; i < 100; i++) {
    sum += i;
  }
  console.log(sum);
}
// 呼叫
getSum();

函式的引數

  • 為什麼要有引數
function getSum() {
  var sum = 0;
  for (var i = 1; i <= 100; i++) {
    sum += i;
  }
  console.log();
}

// 雖然上面程式碼可以重複呼叫,但是隻能計算1-100之間的值
// 如果想要計算n-m之間所有數的和,應該怎麼辦呢?
  • 語法:
// 函式內部是一個封閉的環境,可以通過引數的方式,把外部的值傳遞給函式內部
// 帶引數的函式宣告
function 函式名(形參1, 形參2, 形參...){
  // 函式體
}

// 帶引數的函式呼叫
函式名(實參1, 實參2, 實參3);
  • 形參和實參

    1. 形式引數:在宣告一個函式的時候,為了函式的功能更加靈活,有些值是固定不了的,對於這些固定不了的值。我們可以給函式設定引數。這個引數沒有具體的值,僅僅起到一個佔位置的作用,我們通常稱之為形式引數,也叫形參。
    2. 實際引數:如果函式在宣告時,設定了形參,那麼在函式呼叫的時候就需要傳入對應的引數,我們把傳入的引數叫做實際引數,也叫實參。
var x = 5, y = 6;
fn(x,y); 
function fn(a, b) {
  console.log(a + b);
}
//x,y實參,有具體的值。函式執行的時候會把x,y複製一份給函式內部的a和b,函式內部的值是複製的新值,無法修改外部的x,y

函式的返回值

當函式執行完的時候,並不是所有時候都要把結果列印。我們期望函式給我一些反饋(比如計算的結果返回進行後續的運算),這個時候可以讓函式返回一些東西。也就是返回值。函式通過return返回一個返回值

返回值語法:

//宣告一個帶返回值的函式
function 函式名(形參1, 形參2, 形參...){
  //函式體
  return 返回值;
}

//可以通過變數來接收這個返回值
var 變數 = 函式名(實參1, 實參2, 實參3);

函式的呼叫結果就是返回值,因此我們可以直接對函式呼叫結果進行操作。

返回值詳解: 如果函式沒有顯示的使用 return語句 ,那麼函式有預設的返回值:undefined 如果函式使用 return語句,那麼跟再return後面的值,就成了函式的返回值 如果函式使用 return語句,但是return後面沒有任何值,那麼函式的返回值也是:undefined 函式使用return語句後,這個函式會在執行完 return 語句之後停止並立即退出,也就是說return後面的所有其他程式碼都不會再執行。

推薦的做法是要麼讓函式始終都返回一個值,要麼永遠都不要返回值。

arguments的使用

JavaScript中,arguments物件是比較特別的一個物件,實際上是當前函式的一個內建屬性。也就是說所有函式都內建了一個arguments物件,arguments物件中儲存了傳遞的所有的實參。arguments是一個偽陣列,因此及可以進行遍歷

案例

求斐波那契數列Fibonacci中的第n個數是多少? 1 1 2 3 5 8 13 21…

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>$永遠的24k純帥$</title>
  <script>

    //求斐波那契數列,12---144
    //1 1 2 3 5 8 13 21 34 55 89 144
    function getFib(num) {
      var num1=1;
      var num2=1;
      var sum=0;
      for(var i=3;i<=num;i++){
        sum=num1+num2;
        num1=num2;
        num2=sum;
      }
      return sum;
    }
    console.log(getFib(12));
  </script>
</head>
<body>


</body>
</html>

輸入某年某月某日,判斷這一天是這一年的第幾天?

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>$永遠的24k純帥$</title>
  <script>
    //輸入,年月日,獲取這個日期是這一年的第多少天

    //判斷這個年份是不是閏年
    function isLeapYear(year) {
      return year%4==0&&year%100!=0||year%400==0;
    }
    //年---月---日:2017年4月28日
    function getDays(year, month, day) {
      //定義變數儲存對應的天數
      var days = day;
      //如果使用者輸入的是一月份,沒必要向後算天數,直接返回天數
      if (month == 1) {
        return days;
      }
      //程式碼執行到這裡-----說明使用者輸入的不是1月份
      //使用者輸入的是7月份23日----1,2,3  +23
      //定義一個數組,儲存每個月份的天數
      var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
      //小於的是輸入的月份-1
      for (var i = 0; i < month - 1; i++) {
        days += months[i];
      }
      //需要判斷這個年份是不是閏年
      if(isLeapYear(year)&&month>2){
        days++;
      }
      return days;
    }

     console.log(getDays(2000,3,2));





    //函式裡面可以呼叫其他的函式
//    function f1() {
//      console.log("我是一個函式");
//      f2();//函式的呼叫
//    }
//    function f2() {
//      console.log("我也是一個函式");
//    }
//
//    f1();



  </script>
</head>
<body>


</body>
</html>

函式其它

匿名函式

匿名函式:沒有名字的函式

匿名函式如何使用:

將匿名函式賦值給一個變數,這樣就可以通過變數進行呼叫
匿名函式自呼叫

關於自執行函式(匿名函式自呼叫)的作用:防止全域性變數汙染。

自呼叫函式

匿名函式不能通過直接呼叫來執行,因此可以通過匿名函式的自呼叫的方式來執行

(function () {
  alert(123);
})();

函式是一種資料型別

function fn() {}
console.log(typeof fn);
  • 函式作為引數

因為函式也是一種型別,可以把函式作為兩一個函式的引數,在兩一個函式中呼叫

  • 函式做為返回值

因為函式是一種型別,所以可以把函式可以作為返回值從函式內部返回,這種用法在後面很常見。

function fn(b) {
  var a = 10;
  return function () {
    alert(a+b);
  }
}
fn(15)();

程式碼規範

1.命名規範	
2.變數規範   
	var name = 'zs';	
3.註釋規範
	// 這裡是註釋
4.空格規範
5.換行規範
	var arr = [1, 2, 3, 4];
	if (a > b) {
      
	}
	for(var i = 0; i < 10; i++) {
      
	}
	function fn() {
      
	}

作用域

作用域:變數可以起作用的範圍

全域性變數和區域性變數

  • 全域性變數

    ​在任何地方都可以訪問到的變數就是全域性變數,對應全域性作用域

  • 區域性變數

    ​只在固定的程式碼片段內可訪問到的變數,最常見的例如函式內部。對應區域性作用域(函式作用域)

不使用var宣告的變數是全域性變數,不推薦使用。
變數退出作用域之後會銷燬,全域性變數關閉網頁或瀏覽器才會銷燬

塊級作用域

任何一對花括號({和})中的語句集都屬於一個塊,在這之中定義的所有變數在程式碼塊外都是不可見的,我們稱之為塊級作用域。 在es5之前沒有塊級作用域的的概念,只有函式作用域,現階段可以認為JavaScript沒有塊級作用域

詞法作用域

變數的作用域是在定義時決定而不是執行時決定,也就是說詞法作用域取決於原始碼,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域。

在 js 中詞法作用域規則:

  • 函式允許訪問函式外的資料.
  • 整個程式碼結構中只有函式可以限定作用域.
  • 作用域規則首先使用提升規則分析
  • 如果當前作用規則中有名字了, 就不考慮外面的名字
var num = 123;
function foo() {
  console.log( num );
}
foo();

if ( false ) {
    var num = 123;
}
console.log( num ); // undefiend

作用域鏈

只有函式可以製造作用域結構, 那麼只要是程式碼,就至少有一個作用域, 即全域性作用域。凡是程式碼中有函式,那麼這個函式就構成另一個作用域。如果函式中還有函式,那麼在這個作用域中就又可以誕生一個作用域。

將這樣的所有的作用域列出來,可以有一個結構: 函式內指向函式外的鏈式結構。就稱作作用域鏈。
// 案例1:
function f1() {
    function f2() {
    }
}

var num = 456;
function f3() {
    function f4() {    
    }
}

在這裡插入圖片描述

// 案例2
function f1() {
    var num = 123;
    function f2() {
        console.log( num );
    }
    f2();
}
var num = 456;
f1();

在這裡插入圖片描述

預解析

JavaScript程式碼的執行是由瀏覽器中的JavaScript解析器來執行的。JavaScript解析器執行JavaScript程式碼的時候,分為兩個過程:預解析過程和程式碼執行過程

預解析過程:

  1. 把變數的宣告提升到當前作用域的最前面,只會提升宣告,不會提升賦值。
  2. 把函式的宣告提升到當前作用域的最前面,只會提升宣告,不會提升呼叫。
  3. 先提升var,在提升function

JavaScript的執行過程

var a = 25;
function abc (){
  alert(a);//undefined
  var a = 10;
}
abc();
// 如果變數和函式同名的話,函式優先
console.log(a);
function a() {
  console.log('aaaaa');
}
var a = 1;
console.log(a);

全域性解析規則

函式內部解析規則

變數提升

  • 變數提升

    定義變數的時候,變數的宣告會被提升到作用域的最上面,變數的賦值不會提升。

  • 函式提升

    JavaScript解析器首先會把當前作用域的函式宣告提前到整個作用域的最前面

// 1、-----------------------------------
var num = 10;
fun();
function fun() {
  console.log(num);
  var num = 20;
}
//2、-----------------------------------
var a = 18;
f1();
function f1() {
  var b = 9;
  console.log(a);
  console.log(b);
  var a = '123';
}
// 3、-----------------------------------
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
  var a = b = c = 9;
  console.log(a);
  console.log(b);
  console.log(c);
}