day29-JavaScript(1)

死不悔改奇男子發表於2024-05-01

1、JavaScript 的歷史

1.1、JavaScript 的歷史

JavaScript 因為網際網路而生,緊隨著瀏覽器的出現而問世。回顧它的歷史,就要從瀏覽器的歷史講起。

1990年底,歐洲核能研究組織(CERN)科學家Tim Berners-Lee,在全世界最大的電腦網路——網際網路的基礎上,發明了全球資訊網(World Wide Web),從此可以在網上瀏覽網頁檔案。最早的網頁只能在作業系統的終端裡瀏覽,也就是說只能使用命令列操作,網頁都是在字元視窗中顯示,這當然非常不方便。

1992年底,美國國家超級電腦應用中心(NCSA)開始開發一個獨立的瀏覽器,叫做Mosaic。這是人類歷史上第一個瀏覽器,從此網頁可以在圖形介面的視窗瀏覽。

1994年10月,NCSA的一個主要程式設計師Marc Andreessen聯合風險投資家Jim Clark,成立了Mosaic通訊公司(Mosaic Communications),不久後改名為Netscape。這家公司的方向,就是在Mosaic的基礎上,開發面向普通使用者的新一代的瀏覽器Netscape Navigator。

1994年12月,Navigator釋出了1.0版,市場份額一舉超過90%。

Netscape 公司很快發現,Navigator瀏覽器需要一種可以嵌入網頁的指令碼語言,用來控制瀏覽器行為。當時,網速很慢而且上網費很貴,有些操作不宜在伺服器端完成。比如,如果使用者忘記填寫“使用者名稱”,就點了“傳送”按鈕,到伺服器再發現這一點就有點太晚了,最好能在使用者發出資料之前,就告訴使用者“請填寫使用者名稱”。這就需要在網頁中嵌入小程式,讓瀏覽器檢查每一欄是否都填寫了。

管理層對這種瀏覽器指令碼語言的設想是:功能不需要太強,語法較為簡單,容易學習和部署。那一年,正逢Sun公司的Java語言問世,市場推廣活動非常成功。Netscape公司決定與Sun公司合作,瀏覽器支援嵌入Java小程式(後來稱為Java applet)。但是,瀏覽器指令碼語言是否就選用Java,則存在爭論。後來,還是決定不使用Java,因為網頁小程式不需要Java這麼“重”的語法。但是,同時也決定指令碼語言的語法要接近Java,並且可以支援Java程式。這些設想直接排除了使用現存語言,比如Perl、Python和TCL。

1995年,Netscape公司僱傭了程式設計師Brendan Eich開發這種網頁尾本語言。Brendan Eich有很強的函數語言程式設計背景,希望以Scheme語言(函式式語言鼻祖LISP語言的一種方言)為藍本,實現這種新語言。

1995年5月,Brendan Eich只用了10天,就設計完成了這種語言的第一版。為了保持簡單,這種指令碼語言缺少一些關鍵的功能,比如塊級作用域、模組、子型別(subtyping)等等,但是可以利用現有功能找出解決辦法。這種功能的不足,直接導致了後來JavaScript的一個顯著特點:對於其他語言,你需要學習語言的各種功能,而對於JavaScript,你常常需要學習各種解決問題的模式。而且由於來源多樣,從一開始就註定,JavaScript的程式設計風格是函數語言程式設計和麵向物件程式設計的一種混合體。

Netscape 公司的這種瀏覽器指令碼語言,最初名字叫做 Mocha,1995年9月改為LiveScript。12月,Netscape公司與Sun公司(Java語言的發明者和所有者)達成協議,後者允許將這種語言叫做JavaScript。這樣一來,Netscape公司可以藉助Java語言的聲勢,而Sun公司則將自己的影響力擴充套件到了瀏覽器。

之所以起這個名字,並不是因為JavaScript本身與Java語言有多麼深的關係(事實上,兩者關係並不深),而是因為Netscape公司已經決定,使用Java語言開發網路應用程式,JavaScript可以像膠水一樣,將各個部分連線起來。當然,後來的歷史是Java語言的瀏覽器外掛失敗了,JavaScript反而發揚光大。

1995年12月4日,Netscape 公司與 Sun 公司聯合釋出了 JavaScript 語言。當時的意圖是將 JavaScript 作為 Java 的補充,用來操作網頁。

1996年3月,Navigator 2.0 瀏覽器正式內建了 JavaScript 指令碼語言。

1.2、JavaScript與ECMAScript的關係

1996年8月,微軟模仿JavaScript開發了一種相近的語言,取名為JScript(JavaScript是Netscape的註冊商標,微軟不能用),首先內建於IE 3.0。Netscape公司面臨喪失瀏覽器指令碼語言的主導權的局面。

1996年11月,Netscape公司決定將JavaScript提交給國際標準化組織ECMA(European Computer Manufacturers Association),希望JavaScript能夠成為國際標準,以此抵抗微軟。ECMA的39號技術委員會(Technical Committee 39)負責制定和稽核這個標準,成員由業內的大公司派出的工程師組成,目前共25個人。該委員會定期開會,所有的郵件討論和會議記錄,都是公開的。

1997年7月,ECMA組織釋出262號標準檔案(ECMA-262)的第一版,規定了瀏覽器指令碼語言的標準,並將這種語言稱為ECMAScript。這個版本就是ECMAScript 1.0版。之所以不叫JavaScript,一方面是由於商標的關係,Java是Sun公司的商標,根據一份授權協議,只有Netscape公司可以合法地使用JavaScript這個名字,且JavaScript已經被Netscape公司註冊為商標,另一方面也是想體現這門語言的制定者是ECMA,不是Netscape,這樣有利於保證這門語言的開放性和中立性。因此,ECMAScript和JavaScript的關係是,前者是後者的規格,後者是前者的一種實現。在日常場合,這兩個詞是可以互換的。

ECMAScript只用來標準化JavaScript這種語言的基本語法結構,與部署環境相關的標準都由其他標準規定,比如DOM的標準就是由W3C組織(World Wide Web Consortium)制定的。

一個完整的JavaScript包含三個部分:ECMAScript(標準語法),DOM以及BOM!

ECMA-262標準後來也被另一個國際標準化組織ISO(International Organization for Standardization)批准,標準號是ISO-16262。

1.3、JavaScript與Java的關係

JavaScript和Java是兩種不一樣的語言,但是它們之間存在聯絡。

JavaScript的基本語法和物件體系,是模仿Java而設計的。但是,JavaScript沒有采用Java的靜態型別。正是因為JavaScript與Java有很大的相似性,所以這門語言才從一開始的LiveScript改名為JavaScript。基本上,JavaScript這個名字的原意是“很像Java的指令碼語言”。

在JavaScript語言中,函式是一種獨立的資料型別,以及採用基於原型物件(prototype)的繼承鏈。這是它與Java語法最大的兩點區別。JavaScript語法要比Java自由得多。

另外,Java語言需要編譯,而JavaScript語言則是執行時由直譯器直接執行。

總之,JavaScript的原始設計目標是一種小型的、簡單的動態語言,與Java有足夠的相似性,使得使用者(尤其是Java程式設計師)可以快速上手。

1.4、JavaScript的版本

1997年7月,ECMAScript 1.0釋出。

1998年6月,ECMAScript 2.0版釋出。

1999年12月,ECMAScript 3.0版釋出,成為JavaScript的通行標準,得到了廣泛支援。

2007年10月,ECMAScript 4.0版草案發布,對3.0版做了大幅升級,預計次年8月釋出正式版本。草案發布後,由於4.0版的目標過於激進,各方對於是否透過這個標準,發生了嚴重分歧。以Yahoo、Microsoft、Google為首的大公司,反對JavaScript的大幅升級,主張小幅改動;以JavaScript創造者Brendan Eich為首的Mozilla公司,則堅持當前的草案。

2008年7月,由於對於下一個版本應該包括哪些功能,各方分歧太大,爭論過於激進,ECMA開會決定,中止ECMAScript 4.0的開發(即廢除了這個版本),將其中涉及現有功能改善的一小部分,釋出為ECMAScript 3.1,而將其他激進的設想擴大範圍,放入以後的版本,由於會議的氣氛,該版本的專案代號起名為Harmony(和諧)。會後不久,ECMAScript 3.1就改名為ECMAScript 5。

2009年12月,ECMAScript 5.0版正式釋出。Harmony專案則一分為二,一些較為可行的設想定名為JavaScript.next繼續開發,後來演變成ECMAScript 6;一些不是很成熟的設想,則被視為JavaScript.next.next,在更遠的將來再考慮推出。TC39的總體考慮是,ECMAScript 5與ECMAScript 3基本保持相容,較大的語法修正和新功能加入,將由JavaScript.next完成。當時,JavaScript.next指的是ECMAScript 6。第六版釋出以後,將指ECMAScript 7。TC39預計,ECMAScript 5會在2013年的年中成為JavaScript開發的主流標準,並在此後五年中一直保持這個位置。

2011年6月,ECMAscript 5.1版釋出,並且成為ISO國際標準(ISO/IEC 16262:2011)。到了2012年底,所有主要瀏覽器都支援ECMAScript 5.1版的全部功能。

2013年3月,ECMAScript 6草案凍結,不再新增新功能。新的功能設想將被放到ECMAScript 7。

2013年12月,ECMAScript 6草案發布。然後是12個月的討論期,聽取各方反饋。

2015年6月,ECMAScript 6正式釋出,並且更名為“ECMAScript 2015”。這是因為TC39委員會計劃,以後每年釋出一個ECMAScirpt的版本,下一個版本在2016年釋出,稱為“ECMAScript 2016”。

除了ECMAScript的版本,很長一段時間中,Netscape公司(以及繼承它的Mozilla基金會)在內部依然使用自己的版本號。這導致了JavaScript有自己不同於ECMAScript的版本號。1996年3月,Navigator 2.0內建了JavaScript 1.0。JavaScript 1.1版對應ECMAScript 1.0,但是直到JavaScript 1.4版才完全相容ECMAScript 1.0。JavaScript 1.5版完全相容ECMAScript 3.0。目前的JavaScript 1.8版完全相容ECMAScript 5。

參考連結

2、JS的引入方式

1 直接編寫
    <script>
        console.log('hello yuan')
    </script>
2 匯入檔案
    <script src="hello.js"></script>

3、ECMAScript基本語法

js是一門弱型別的程式語言,屬於基於物件和基於原型的指令碼語言.

  • 變數
格式:
    // 方式1 先宣告再賦值
    var 變數名;   // 宣告的變數如果沒有進行賦值,或者沒有被定義的變數,值預設是undefined
    變數名 = 變數值;
    // 方式2 宣告並賦值
    var 變數名 = 變數值;
    // 方式3 一行可以宣告多個變數.並且可以是不同型別
    var name="yuan", age=20, job="lecturer";

1、宣告變數時 可以不用var. 如果不用var 那麼它是全域性變數

2、變數命名,首字元只能是字母,下劃線,$美元符 三選一,餘下的字元可以是下劃線、美元符號或任何字母或數字字元且區分大小寫

  • 註釋
// 單行註釋

 /*
   多行註釋
 */
  • 語句分隔符
var a = 1   // 分號和換行符作為語句分隔符號
var b = 2;
console.log(a,b)

4、ECMAScript 基本資料型別

image

4.4.1、數字型別

JavaScript 沒有整型和浮點型,只有一種數字型別,即number型別。

var x = 10;
var y = 3.14;
console.log(x,typeof x);  // 10 "number"
console.log(y,typeof y);  // 3.14 "number"

4.4.2、字串

字串建立(兩種方式)

  • 變數 = “字串”
  • 字串物件名稱 = new String (字串)
var str1="hello world";
var str1= new String("hello word");
// 字串物件的操作
var str = "hello"; // 這就是字串物件
console.log(str);

// 字串物件內建屬性
// length 計算字串的長度
console.log( str.length );

// 字串物件內建方法
// toUpperCase();  字母大寫轉換
// toLowerCase();  字母小寫轉換

console.log( str.toUpperCase() );
console.log( str.toLowerCase() );

// indexOf 獲取指定字元在字串中第一次出現的索引位置
// 字串也有下標,也可以使用中括號來提取字串的指定字元
console.log(str[1]); // e
console.log( str.indexOf("e") ); // 1

// 切片,當前方法支援使用負數代表倒數下標
// slice(開始下標)   從開始位置切到最後
// slice(開始下標,結束下標)  從開始下標切到指定位置之前
var str = "helloworld";
var ret = str.slice(3,6); // 開區間,不包含結束下標的內容
console.log(ret); // low
var ret = str.slice(5);
console.log(ret); // world
var ret = str.slice(2,-1);
console.log(ret); // lloworl
var ret = str.slice(-4,-1);
console.log(ret); // orl
var ret = str.slice(-1,-4);
console.log(ret); // ""

// split   正則分割,經常用於把字串轉換成陣列
var str = "廣東-深圳-南山";
var ret = str.split("-");
console.log( ret );

// substr  擷取
var str = "hello world";
var ret = str.substr(0,3);
console.log(ret); // hel

// trim    移除字串首尾空白
var password = "    ge llo   ";
var ret = password.trim();
console.log(password.length); // 13
console.log(ret.length);  // 6

4.4.3、布林值

1、Boolean型別僅有兩個值:true和false,也代表1和0,實際運算中true=1,false=0
2、布林值也可以看作on/off、yes/no、1/0對應true/false;
3、Boolean值主要用於JavaScript的控制語句

console.log(true);
console.log(false);
console.log(typeof true);
console.log(true === 1);
console.log(true == 1);
console.log(true + 1);
console.log(false + 1);

4.4.4、空值(Undefined和Null)

  • undefined型別

undefined型別只有一個值,即 undefined。

(1)當宣告的變數未初始化時,該變數的預設值是 undefined。

(2)當函式無明確返回值時,返回的也是值 undefined;

  • null型別

另一種只有一個值的型別是 null,它只有一個專用值 null,即它的字面量。值 undefined 實際上是從值 null 派生來的,因此 ECMAScript 把它們定義為相等的。

儘管這兩個值相等,但它們的含義不同。undefined 是宣告瞭變數但未對其初始化時賦予該變數的值,null 則用於表示尚未存在的物件。如果函式或方法要返回的是物件,那麼找不到該物件時,返回的通常是 null。

4.4.5、型別轉換

js中,型別轉換有2種.一種就是強制轉換,一種就是自動轉換.

因為js是一門弱型別的指令碼語言,所以變數會在運算子的執行要求,有時候根據運算子的要求,進行自動轉換的.

  • 強制轉換
// 1. 轉換資料為數值型別
// parseInt     把資料轉換成整數
// parseFloat   把資料轉換成小數
// Number       把資料轉換成數值
var box1 = "一共100件"; // 轉換會失敗
var box1 = "100件";     // 轉換會成功
var ret = parseInt(box1);
console.log(box1);
console.log(ret);
//
var box2 = "3.14";
console.log(parseFloat(box2) ); // 3.14
//
var box3 = "3.14";   // 使用Number轉換的資料裡面必須是純數字!!!!否則都會轉換失敗
// var box3 = "3.1.4";  // 轉換失敗!
console.log( Number(box3) );

// 對於轉換數值,如果轉換失敗的話,則結果為 NaN ,是 Not a Number ,但是NaN的型別也是number型別

// 2. 轉換資料為字串
// 變數.toString()
// String(資料)
var box4 = 3.14;
var ret = box4.toString();
console.log(ret);
//
ret = String(box4);
console.log(ret);

// 3. 轉換資料成布林型別
// Boolean()

var box5 = "";
console.log( Boolean(box5) ); // false
var box6 = -1;
console.log( Boolean(box6) ); // true
var box7 = 0;
console.log( Boolean(box7) ); // false;
var box8 = "false";
console.log( Boolean(box8) ); // true
var box9 = [];
console.log( Boolean(box9) ); // true
var box10 = {};
console.log( Boolean(box10) ); // true
var box11 = "0";
console.log( Boolean(box11) ); // true
var box12 = null;
console.log( Boolean(box12) ); // false
var box13 = undefined;
console.log( Boolean(box13) ); // false
  • 自動轉換
// 所謂的自動轉換,其實弱型別中的變數會根據當前程式碼的需要,進行型別的自動隱式轉化
var box1 = 1 + true;
// true 轉換成數值,是1, false轉換成數值,是0
console.log(box1); // 2

var box2 = 1 + "200";
console.log(box2); // 1200 原因是,程式中+的含義有2種,第一: 兩邊數值相加, 第二: 兩邊字串拼接.但是在js中運算子的優先順序中, 字串拼接的優先順序要高於數值的加減乘除,所以解析器優先使用了+號作為了字串的拼接符號了,因為程式就需要+號兩邊都是字串才能完成運算操作,因此1變成字串最終的結果就是 "1" +"200"

var box3 = 1 - "200";
console.log(box3); // -199;因為-號中表示的就是左邊的數值減去右邊的數值,因此程式就會要求"200"是數值,因此內部偷偷的轉換了一下

4.4.6、原始值和引用值

根據資料型別不同,有的變數儲存在棧中,有的儲存在堆中。具體區別如下:

原始變數及他們的值儲存在棧中,當把一個原始變數傳遞給另一個原始變數時,是把一個棧房間的東西複製到另一個棧房間,且這兩個原始變數互不影響。

引用值是把 引用變數的名稱儲存在棧中,但是把其實際物件儲存在堆中,且存在一個指標由變數名指向儲存在堆中的實際物件,當把引用物件傳遞給另一個變數時,複製的其實是指向實際物件的指標, 此時 兩者指向的 是同一個資料,若透過方法改變其中一個變數的值,則訪問另一個變數時,其值也會隨之加以改變;但若不是透過方法 而是透過 重新賦值 此時 相當於 重新開了一個房間 該值的原指標改變 ,則另外一個 值不會隨他的改變而改變。

// 初始值型別
var a = "yuan";
var b = a;
a = "alvin";
console.log(a);//alvin
console.log(b);//yuan

// 物件型別
var arr1=[1,2];
arr2 = arr1;
arr1.push(3);
console.log(arr1)// [1,2,3]
console.log(arr2);//[1,2,3]

arr1=[4,5];
console.log(arr1);//[4,5]
console.log(arr2);//[1,2,3]

5、運算子

  • 運算子
/*
//算術運算子
   +   數值相加
   -   數值相減
   *   數值相乘
   /   數值相除
   %   數值求餘
   **  數值求冪
   a++ 數值後自增1   a=a+1
   ++a 數值前自增1   a=a+1
   b-- 數值後自減1   b=b-1
   --b 數值前自減1   b=b-1
   
//賦值運算子
   =
   +=
   -=
   *=
   /=
   %=
   **=

//比較運算子,比較的結果要麼是true, 要麼是false
  >   大於
  <   小於
  >=  大於或者等於
  <=  小於或者等於
  !=  不等於[計算數值]
  ==  等於[計算]

  !== 不全等[不僅判斷數值,還會判斷型別是否一致]
  === 全等[不僅判斷數值,還會判斷型別是否一致]

//邏輯運算子
  &&   並且  and    兩邊的運算結果為true,最終結果才是true
  ||   或者  or     兩邊的運算結果為false,最終結果才是false
  !    非    not    運算子的結果如果是true,則最終結果是false ,反之亦然.
 
  //邏輯運算子進階用法:
     1. 實現短路
        var a = false || 2      >>> a = 2
        var a = true && "hehe"  >>>  a = "hehe"
     
     2. 快速布林化[把資料快速轉換成布林型別]
        var a = 100
        !!a  >>> true

//條件運算子[三目運算子]
 條件?true:false
 例如:
        var age = 12;
        var ret = age>=18?"成年":"未成年"; // 相當於 python中的"成年" if age >= 18 else "未成年"
        console.log(ret);
 */

6、流程控制語句

程式語言的流程控制分為三種:

  • 順序結構(從上向下順序執行)
  • 分支結構
  • 迴圈結構

之前我們學習的方式就是順序執行,即程式碼的執行從上到下,一行行分別執行。

例如:

console.log("星期一");
console.log("星期二");
console.log("星期三");

4.6.1、分支結構

  • if 分支語句
if(條件){
 // 條件為true時,執行的程式碼
}

if(條件){
 // 條件為true時,執行的程式碼
}else{
 // 條件為false時,執行的程式碼
}

if(條件1){
 // 條件1為true時,執行的程式碼
}else if(條件2){
 // 條件2為true時,執行的程式碼

}....

}else{
 // 上述條件都不成立的時候,執行的程式碼
}
  • switch語句
switch(條件){
      case 結果1:
           滿足條件執行的結果是結果1時,執行這裡的程式碼..
           break;
      case 結果2:
           滿足條件執行的結果是結果2時,執行這裡的程式碼..
           break;
      .....
      default:
           條件和上述所有結果都不相等時,則執行這裡的程式碼
   }

1、switch比if else更為簡潔

2、執行效率更高。switch…case會生成一個跳轉表來指示實際的case分支的地址,而這個跳轉表的索引號與switch變數的值是相等的。從而,switch…case不用像if…else那樣遍歷條件分支直到命中條件,而只需訪問對應索引號的表項從而到達定位分支的目的。

3、到底使用哪一個選擇語句,程式碼環境有關,如果是範圍取值,則使用if else語句更為快捷;如果是確定取值,則使用switch是更優方案。

4.6.2、迴圈語句

  • while迴圈
   while(迴圈的條件){
      // 迴圈條件為true的時候,會執行這裡的程式碼
   }
  

迴圈案例:

 var count = 0
 while (count<10){
     console.log(count);
     count++;
 }
  • for迴圈
   
   // 迴圈三要素
   for(1.宣告迴圈的開始; 2.條件; 4. 迴圈的計數){
      // 3. 迴圈條件為true的時候,會執行這裡的程式碼
   }
   
   for(迴圈的成員下標 in 被迴圈的資料){
      // 當被迴圈的資料一直沒有執行到最後下標,都會不斷執行這裡的程式碼
   }   

迴圈案例:

// 方式1
for (var i = 0;i<10;i++){
    console.log(i)
}

// 方式2
var arr = [111,222,333]
for (var i in arr){
    console.log(i,arr[i])
}

  • 退出迴圈(break和continue)
for (var i = 0;i<100;i++){
    if (i===88){
        continue  // 退出當次迴圈
        // break  // 退出當前整個迴圈
    }
    console.log(i)
}

作業:

(1)計算1+2+3+...+100=?

(2)求20的階乘值

7、陣列物件

  • 建立陣列
建立方式1:
var arrname = [元素0,元素1,….];          // var arr=[1,2,3];

建立方式2:
var arrname = new Array(元素0,元素1,….); // var test=new Array(100,"a",true);
  • 陣列方法
var arr = ["A","B","C","D"];
// 內建屬性
console.log( arr.length );
// 獲取指定下標的成員
// console.log( arr[3] ); // D
console.log( arr[arr.length-1] ); // 最後一個成員

// (1) pop()  出棧,刪除最後一個成員作為返回值
var arr = [1,2,3,4,5];
var ret = arr.pop();
console.log(arr); // [1, 2, 3, 4]
console.log(ret); // 5


// (2) push() 入棧,給陣列後面追加成員
var arr = [1,2,3,4,5];
arr.push("a");
console.log(arr); // [1, 2, 3, 4, 5, "a"]


// (3) shift是將陣列的第一個元素刪除
var arr = [1,2,3,4,5];
arr.shift()
console.log(arr); // [2, 3, 4, 5]

// (4) unshift是將value值插入到陣列的開始
var arr = [1,2,3,4,5];
arr.unshift("yuan")
console.log(arr); // ["yuan",1,2, 3, 4, 5]


// (5) reverse() 反轉排列
var arr = [1,2,3,4,5];
arr.reverse();
console.log(arr); // [5, 4, 3, 2, 1]

// (6) slice(開始下標,結束下標)  切片,開區間


// (7) sort() 排序
var arr = [3,4,1,2,5,10];
console.log( arr ); // [3, 4, 1, 2, 5, 10]
arr.sort();
//
// // 這是字元的排序,不是數值的排序
console.log(arr);   //  [1, 10, 2, 3, 4, 5]

// 數值升序
var arr = [3,4,1,2,5,10];
arr.sort(function(a,b){
    return a-b;
});
console.log(arr);  // [1, 2, 3, 4, 5, 10]

// 數值降序
var arr = [3,4,1,2,5,10];
arr.sort(function(a,b){
    return b-a;
});
console.log(arr); // [10, 5, 4, 3, 2, 1]

// (8) splice(操作位置的下標,刪除操作的成員長度,"替換或者新增的成員1","替換或者新增的成員2")  新增/刪除指定的成員   "萬能函式"
var arr1 = [1,2,3];
arr1.splice(1,1);
console.log(arr1); // 刪除指定的1個成員  [1, 3]

var arr2 = ["a","b","c","d"];
arr2.splice(2,0,"w","x","w"); // 新增
console.log(arr2); // ["a", "b", "w", "x", "w", "c", "d"]

var arr3 = ["a","b","c"];
arr3.splice(1,1,"w");
console.log(arr3); // ["a", "w", "c"]

// (9) concat() 把2個或者多個陣列合並
var arr1 = [1,2,3];
var arr2 = [4,5,7];
var ret = arr1.concat(arr2);
console.log( ret );


// (10) join()  把陣列的每一個成員按照指定的符號進行拼接成字串
var str = "廣東-深圳-南山";
var arr = str.split("-");
console.log( arr ); // ["廣東", "深圳", "南山"];

var arr1 = ["廣東", "深圳", "南山"];
var str1 = arr1.join("-");
console.log( str1 ); // 廣東-深圳-南山


// (11) find()  高階函式, 返回符合條件的第一個成員
var arr = [4,6,5,7];
var func = (num)=>{
    if(num%2===0){
        return num;
    }
};
var ret = arr.find(func);
console.log( ret ); // 4

// (12)  filter() 高階函式, 對陣列的每一個成員進行過濾,返回符合條件的結果
var arr = [4,6,5,7];
function func(num){  // 也可以使用匿名函式或者箭頭函式
    if(num%2===0){
        return num;
    }
}
var ret = arr.filter(func);  // 所有的函式名都可以作為引數傳遞到另一個函式中被執行
console.log( ret );

// (13) map() 對陣列的每一個成員進行處理,返回處理後的每一個成員
var arr = [1,2,3,4,5];
var ret = arr.map((num)=>{
    return num**3;
});
console.log( ret  ); // [1, 8, 27, 64, 125]

// (14) 其它方法
// includes   查詢指定資料是否在陣列中存在!
// indexOf()  查詢指定資料在陣列中第一次出現的位置
// isArray()  判斷變數的值是否是陣列
       
  • 遍歷陣列
 var arr = [12,23,34]
 for (var i in arr){
       console.log(i,arr[i])
 }

8、Object物件

8.1、object物件的基本操作

Object 的例項不具備多少功能,但對於在應用程式中儲存和傳輸資料而言,它們確實是非常理想的選擇。
建立 Object 例項的方式有兩種。

var person = new Object();
person.name = "alvin";
person.age = 18;

另一種方式是使用物件字面量表示法。物件字面量是物件定義的一種簡寫形式,目的在於簡化建立包含大量屬性的物件的過程。下面這個例子就使用了物件字面量語法定義了與前面那個例子中相同的person 物件:

var person = {
    name: "alvin",
    age: 18
};
  • object可以透過. 和 []來訪問。
console.log(person["age"]);
console.log(person.age)
  • object可以透過for迴圈遍歷
for (var attr in person) {
    console.log(attr, person[attr]);
}

8.2、json序列化和反序列化

JSON:JavaScript 物件表示法(JavaScript Object Notation),是一種輕量級的資料交換格式。易於人閱讀和編寫。

# json是一種資料格式, 語法一般是{}或者[]包含起來
# 內部成員以英文逗號隔開,最後一個成員不能使用逗號!
# 可以是鍵值對,也可以是列表成員
# json中的成員如果是鍵值對,則鍵名必須是字串.而json中的字串必須使用雙引號圈起來
# json資料也可以儲存到檔案中,一般以".json"結尾.
# 前端專案中,一般使用json作為配置檔案.

{
   "name": "xiaoming",
   "age":12
}

[1,2,3,4]

{
   "name": "xiaoming",
   "age":22,
   "sex": true,
   "son": {
      "name":"xiaohuihui",
      "age": 2
   },
   "lve": ["籃球","唱","跳"]
}

js中也支援序列化和反序列化的方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // js物件,因為這種宣告的物件格式很像json,所以也叫json物件
    var data = {
        name: "xiaoming",
        age: 22,
        say: function(){
            alert(123);
        }
    };

    // 把json物件轉換成json字串
    var ret = JSON.stringify(data);
    console.log(ret ); // {"name":"xiaoming","age":22}

    // 把json字串轉換成json物件
    var str = `{"name":"xiaoming","age":22}`;
    var ret = JSON.parse(str);
    console.log(ret);
</script>
</body>
</html>

9、Date物件

  • 建立Date物件
//方法1:不指定引數
var nowd1=new Date();
console.log(nowd1.toLocaleString( ));
//方法2:引數為日期字串
var d2=new Date("2004/3/20 11:12");
console.log(d2.toLocaleString( ));
var d3=new Date("04/03/20 11:12");
console.log(d3.toLocaleString( ));
//方法3:引數為毫秒數
var d4=new Date(5000);
console.log(d4.toLocaleString( ));
console.log(d4.toUTCString());
//方法4:引數為年月日小時分鐘秒毫秒
var d5=new Date(2004,2,20,11,12,0,300);
console.log(d5.toLocaleString( ));//毫秒並不直接顯示
  • 獲取時間資訊
獲取日期和時間
getDate()                 獲取日
getDay ()                 獲取星期
getMonth ()               獲取月(0-11)
getFullYear ()            獲取完整年份
getYear ()                獲取年
getHours ()               獲取小時
getMinutes ()             獲取分鐘
getSeconds ()             獲取秒
getMilliseconds ()        獲取毫秒
getTime ()                返回累計毫秒數(從1970/1/1午夜)
  • 日期和時間的轉換
日期和時間的轉換:
// 返回國際標準時間字串
toUTCString()
// 返回本地格式時間字串
toLocalString()
// 返回累計毫秒數(從1970/1/1午夜到本地時間)
Date.parse(x)
// 返回累計毫秒數(從1970/1/1午夜到國際時間)
Date.UTC(x)

練習:以2021年03月2日 14:1:43 星期二格式化輸出當前時間

function getCurrentDate() {
    //1. 建立Date物件
    var date = new Date(); //沒有填入任何引數那麼就是當前時間
    //2. 獲得當前年份
    var year = date.getFullYear();
    //3. 獲得當前月份 js中月份是從0到11.
    var month = date.getMonth() + 1;
    //4. 獲得當前日
    var day = date.getDate();
    //5. 獲得當前小時
    var hour = date.getHours();
    //6. 獲得當前分鐘
    var min = date.getMinutes();
    //7. 獲得當前秒
    var sec = date.getSeconds();
    //8. 獲得當前星期
    var week = date.getDay(); //沒有getWeek
    // 2014年06月18日 15:40:30 星期三
    return year + "年" + changeNum(month) + "月" + day + "日 " + hour + ":" + min + ":" + sec + " " + parseWeek(week);
}

//解決 自動補齊成兩位數字的方法
function changeNum(num){
    if(num < 10){
        return "0"+num;
    }else{
        return num;
    }
}
//將數字 0~6 轉換成 星期日到星期六
function parseWeek(week){
    var arr = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
    //             0      1      2      3 .............
    return arr[week];
}

console.log(getCurrentDate());

10、Math物件

//  Number物件的內建方法
//  toFixed(x) 保留小數位
var num = 100.3;
var ret = num.toFixed(2);
console.log(num);  // 100.3
console.log(ret);  // 100.30

// Math物件的內建方法
// abs(x)  返回數值的絕對值
// var num = -10;
console.log( Math.abs(num) ); // 10

// ceil(x)  向上取整
var num = 10.3;
console.log( Math.ceil(num) ); // 11

// floor(x) 向下取整
var num = 10.3;
console.log( Math.floor(num) ); // 10

// max(x,y,z,...,n)
console.log( Math.max(3,56,3) ); // 56
// min(x,y,z,...,n)

// pow(x,y)
console.log(Math.pow(3, 2)); // 相等於 3**2
console.log( 3**2 ); // 使用這個,上面廢棄

// random()  生成0-1隨機數
console.log( Math.random() );

// 生成0-10之間的數值
console.log( Math.random() * 10 );

// round(x) 四捨五入
// 生成0-10之間的整數
console.log( Math.round( Math.random() * 10 ) );
  • 練習:獲取1-100的隨機整數,包括1和100
var num=Math.random();
num=num*100;
num=Math.round(num);
console.log(num)

11、Function 物件

函式在程式中代表的就是一段具有功能性的程式碼,可以讓我們的程式程式設計更加具有結構性和提升程式的複用性,也能讓程式碼變得更加靈活強大

11.1、宣告函式

// 函式的定義方式1
function 函式名 (引數){
    函式體;
    return 返回值;
}
功能說明:
    可以使用變數、常量或表示式作為函式呼叫的引數
    函式由關鍵字function定義
    函式名的定義規則與識別符號一致,大小寫是敏感的
    返回值必須使用return
    
//  函式的定義方式2
    
用 Function 類直接建立函式的語法如下:
var 函式名 = new Function("引數1","引數n","function_body");

雖然由於字串的關係,第二種形式寫起來有些困難,但有助於理解函式只不過是一種引用型別,它們的行為與用 Function 類明確建立的函式行為是相同的。    
   

11.2、函式呼叫

    //f(); --->OK
    function f(){
        console.log("hello")

    }
    f() //----->OK

不同於python,js程式碼在執行時,會分為兩大部分———預編譯 和 執行階段。

  • 預編譯:會先檢測程式碼的語法錯誤,進行變數、函式的宣告。
  • 執行階段:變數的賦值、函式的呼叫等,都屬於執行階段。

11.3、函式引數

(1) 引數基本使用

// 位置引數
function add(a,b){
    console.log(a);
    console.log(b);
}
add(1,2)
add(1,2,3)
add(1)

// 預設引數
function stu_info(name,gender="male"){
    console.log("姓名:"+name+" 性別:"+gender)
}

stu_info("yuan")

(2)函式中的arguments物件

function add(a, b) {

    console.log(a + b);//3
    console.log(arguments.length);//2
    console.log(arguments);//[1,2]

}

add(1, 2)

// arguments的應用1 
function add2(){
    var result=0;
    for (var num in arguments){
        result+=arguments[num]
    }
    console.log(result)

}

add2(1,2,3,4,5)

// arguments的應用2

function f(a,b,c){
    if (arguments.length!=3){
        throw new Error("function f called with "+arguments.length+" arguments,but it just need 3 arguments")
    }
    else {
        alert("success!")
    }
}

f(1,2,3,4,5)

11.4、函式返回值

在函式體內,使用 return 語句可以設定函式的返回值。一旦執行 return 語句,將停止函式的執行,並運算和返回 return 後面的表示式的值。如果函式不包含 return 語句,則執行完函式體內每條語句後,返回 undefined 值。

function add(x, y) {
    return x + y
}

var ret = add(2,5);
console.log(ret)

1、在函式體內可以包含多條 return 語句,但是僅能執行一條 return 語句

2、函式的引數沒有限制,但是返回值只能是一個;如果要輸出多個值,可以透過陣列或物件進行設計。

11.5、匿名函式

匿名函式,即沒有變數名的函式。在實際開發中使用的頻率非常高!也是學好JS的重點。

// 匿名函式賦值變數
var foo = function () {
   console.log("這是一個匿名函式!")
};

// 匿名函式的自執行
(function (x,y) {
   console.log(x+y);
})(2,3)


// 匿名函式作為一個高階函式使用
function bar() {

    return function () {
        console.log("inner函式!")
    }
}

bar()()
        

使用匿名函式表示式時,函式的呼叫語句,必須放在函式宣告語句之後!

11.6、函式作用域

作用域是JavaScript最重要的概念之一,想要學好JavaScript就需要理解JavaScript作用域和作用域鏈的工作原理。

任何程式設計語言都有作用域的概念,簡單的說,作用域就是變數可訪問範圍,即作用域控制著變數與函式的可見性和生命週期。在JavaScript中,變數的作用域有全域性作用域和區域性作用域兩種。

// 區域性變數,是在函式內部宣告,它的生命週期在當前函式被呼叫的時候, 當函式呼叫完畢以後,則記憶體中自動銷燬當前變數
// 全域性變數,是在函式外部宣告,它的生命週期在當前檔案中被宣告以後就儲存在記憶體中,直到當前檔案執行完畢以後,才會被記憶體銷燬掉

首先熟悉下var

var name = "yuan"; // 宣告一個全域性變數 name並賦值”yuan“
name = "張三";  // 對已經存在的變數name重新賦值 ”張三“
console.log(name);

age = 18   // 之前不存在age變數,這裡等同於var age = 19 即宣告全域性變數age並賦值為18

var  gender = "male"
var  gender = "female" // 原記憶體釋放與新記憶體開闢,指標指向新開闢的記憶體
console.log(gender)

作用域案例:

 var num = 10; // 在函式外部宣告的變數, 全域性變數
    function func(){
        // num = 20; // 函式內部直接使用變數,則預設呼叫了全域性的變數,
        //var num = 20; // 函式內部使用var 或者 let宣告的變數則是區域性變數
                  // 函式內部直接使用變數,則預設呼叫了全域性的變數,
                  // 使用變數的時候,直譯器會在當前花括號範圍值搜尋是否有關鍵字var 或者 let 宣告瞭變數,如果沒有,則一層一層往外查詢最近的宣告
                  // 如果最終查詢不到,則直接報錯! 變數名 is not define!
        console.log("函式內部num:",num)
    }
func();
console.log("全域性num:",num);

11.7、JS的預編譯

js執行三個階段:

  1. 語法分析
  2. 預編譯
  3. 解釋執行

語法分析就是JS引擎去檢查你的程式碼是否有語法錯誤,解釋執行就是執行你的程式碼。最重要最需要理解的就是第二個環節預編譯,簡單理解就是在記憶體中開闢一些空間,存放一些變數與函式 。

預編譯可分為全域性預編譯和區域性預編譯。

  1. 在js指令碼載入之後,會先通篇檢查是否存在低階錯誤;
  2. 在語法檢測完之後,便進行全域性預編譯;
  3. 在全域性預編譯之後,就解釋一行,執行一行;
  4. 當執行到函式呼叫那一行前一刻,會先進行函式預編譯,再往下執行。

全域性預編譯的3個步驟:

  1. 建立GO物件(Global Object)全域性物件,即window物件。
  2. 找變數宣告,將變數名作為GO屬性名,值為undefined
  3. 查詢函式宣告,作為GO屬性,值賦予函式體

區域性預編譯的4個步驟:

  1. 建立AO物件(Activation Object)執行期上下文。
  2. 找形參和變數宣告,將變數和形參名作為AO屬性名,值為undefined
  3. 將實參值和形參統一。
  4. 在函式體裡面找函式宣告,值賦予函式體。

GO物件是全域性預編譯,所以它優先於AO物件所建立和執行

案例分析:

  <script>
        var a = 10;
        console.log(a);

        function foo(a) {
          console.log(a);
          var a = 100;
          console.log(a);
          function a() {}
          console.log(a);
          var b = function(){};
          console.log(b);
          function d() {}
        }
        var c = function (){
        console.log("匿名函式C");
        };
        console.log(c);
        foo(20);

  </script>

全域性預編譯
GO/window = {
    a: undefined,
    c: undefined,
    foo: function(a) {
        console.log(a);
        var a = 123;
        console.log(a);
        function a() {}
        console.log(a);
        var b = function() {}
        console.log(b);
        function d() {}
    }
}
解釋執行程式碼(直到執行呼叫函式foo(20)語句)
GO/window = {
    a: 10,
    c: function (){
        console.log("I at C function");
    }
    test: function(a) {
        console.log(a);
        var a = 123;
        console.log(a);
        function a() {}
        console.log(a);
        var b = function() {}
        console.log(b);
        function d() {}
    }
}
呼叫函式foo(20)前發生佈局預編譯
// 區域性預編譯前兩步:
AO = {
    a:undefined,
    b:undefined,
}

// 區域性預編譯第三步:
AO = {
    a:20,
    b:undefined,
}
// 區域性預編譯第四步:
AO = {
    a:function a() {},
    b:undefined
    d:function d() {}
}

預編譯總結:

  1. 函式宣告整體提升-(具體點說,無論函式呼叫和宣告的位置是前是後,系統總會把函式宣告移到呼叫前面)
  2. 變數 宣告提升-(具體點說,無論變數呼叫和宣告的位置是前是後,系統總會把宣告移到呼叫前,注意僅僅只是宣告,所以值是undefined)

面試題:

var num3 = 10;
function func3(){
    console.log(num3); 
    var num3 = 20;       
}
func3();
console.log(num3); 


 /*

        // 全域性編譯

        GO{
           num3:undefined,
           func3: function (){
            console.log(num3);
            var num3 = 20;
        }

        // 全域性執行
        var num3 = 10;
        GO{
           num3:10,
           func3: function (){
            console.log(num3);
            var num3 = 20;
        }


        // 區域性編譯
        func3.AO{
           num3:undefined,
        }


        // 區域性執行

        func3.AO{
           num3:20,
        }

        // 全域性執行

        GO.num3 = 10
        }

*/