超長長長 ECMAScript 筆記

ECMAScript

什麼是Javascript?

  • Javascript簡稱JS,是前端開發的一門腳本語言(解釋性語言)
  • 解釋型語言:程序執行以前,不須要對代碼進行編譯,在運行時邊解析邊執行的語言。
  • 瀏覽器工做原理:

img

瀏覽器的主要組件爲 (1.1):javascript

  1. 用戶界面 - 包括地址欄、前進/後退按鈕、書籤菜單等。除了瀏覽器主窗口顯示的您請求的頁面外,其餘顯示的各個部分都屬於用戶界面。
  2. 瀏覽器引擎 - 在用戶界面和呈現引擎之間傳送指令。
  3. 呈現引擎 - 負責顯示請求的內容。若是請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,並將解析後的內容顯示在屏幕上。
  4. 網絡 - 用於網絡調用,好比 HTTP 請求。其接口與平臺無關,併爲全部平臺提供底層實現。
  5. 用戶界面後端 - 用於繪製基本的窗口小部件,好比組合框和窗口。其公開了與平臺無關的通用接口,而在底層使用操做系統的用戶界面方法。
  6. JavaScript 解釋器。用於解析和執行 JavaScript 代碼。
  7. 數據存儲。這是持久層。瀏覽器須要在硬盤上保存各類數據,例如 Cookie。新的 HTML 規範 (HTML5) 定義了「網絡數據庫」,這是一個完整(可是輕便)的瀏覽器內數據庫。
  • 編譯型語言:程序執行以前,須要一個專門的編譯過程,把程序編譯成機器語言的文件,好比exe文件

做用

  • HTML:提供網頁上顯示的內容(結構)
  • CSS:美化網頁(樣式)
  • Javascript:控制網頁行爲(行爲)

發展史

  • Javascript起源於Netscape公司的LiveScript語言
    • 1994年網景公司發佈歷史上第一個比較成熟的瀏覽器(Navigator 0.9),可是隻能瀏覽不能交互
    • 1995年爲了解決表單有效性驗證就要與服務器進行屢次地往返交互問題,網景公司錄用Brendan Eich(布蘭登·艾奇),他在10天內開發出LiveScript語言
    • 在Netscape Navigator 2.0即將正式發佈前,Netscape將LiveScript改名爲Javascript,目的是爲了蹭Java的熱度
    • 因此Java和Javascript之間的關係就像老婆和老婆餅同樣

參考文獻css

組成

  • ECMAScript:JavaScript的語法標準html

    • ECMA是European Computer Manufacturers Association的縮寫,即歐洲計算機制造商協會前端

    • ECMAScript是ECMA制定的腳本語言的標準,規定了一種腳本語言實現應該包含的基本內容html5

    • Javascript是腳本語言的一種,因此Javascript也必須遵照ECMAScript標準,包含ECMAScript標準中規定的基本內容java

  • DOM(Document Object Model):JavaScript操做網頁上的元素(標籤)的APInode

  • BOM(Browser Object Model):JavaScript操做瀏覽器的部分功能的API程序員

img

書寫格式

1.CSS書寫格式web

  • 行內樣式: 寫在標籤內部數據庫

  • 內嵌樣式(內聯樣式) : 寫在一對head-style標籤中

  • 外鏈樣式: 寫在一個單獨的.css文件中, 再導入進來

2.JavaScript書寫格式

  • 行內樣式: 寫在標籤內部

  • 內嵌樣式(內聯樣式) : 寫在一對head-script標籤中

  • 外鏈樣式: 寫在一個單獨的.js文件中, 再導入進來

3.JavaScript書寫格式注意點

  • 不推薦直接將JavaScript代碼書寫到標籤內部

  • 默認狀況下瀏覽器會從上至下的解析網頁, 因此若是將JavaScript寫到一對head標籤中, 而且須要經過JavaScript代碼操做界面上的元素, 那麼就不能直接書寫JavaScript代碼, 不然無效

    • 若是想將JavaScript寫到一對head標籤中, 而且須要在JavaScript代碼中操做界面上的元素, 那麼必須加上window.onload = function(){操做界面元素的JavaScript}
    window.onload = function () {
        var oDiv = document.querySelector("div");
        var text = oDiv.innerText;
        alert(text);
    }
    複製代碼
    • window.onload的含義: 等到界面上全部的內容都加載完畢再執行{}中的代碼
    • 因爲默認狀況下瀏覽器會從上至下的解析網頁, 因此若是想經過JavaScript操做界面上的元素只須要等到元素被加載解析以後操做就能夠了, 因此咱們還能夠將JavaScript代碼寫到body結束標籤的前面
  • 若是經過外鏈式導入.js文件, 而且須要在.js文件中操做界面上的元素, 並且是在head標籤中導入的, 必須在.js文件中加上window.onload. 若是是在body結束標籤前面導入的, 那麼就不用添加window.onload

  • 若是須要在一對script標籤中編寫JavaScript代碼, 那麼就不能同時經過script標籤再導入其它的.js文件, 不然書寫的JavaScript代碼無效

常見輸出方式

1.經過彈窗的形式來輸出

  • alert(須要輸出的內容);

  • confirm(須要輸出的內容);

  • prompt(須要輸出的內容);

注意點:

若是須要輸出的內容不是數字, 那麼就必須經過單引號或者雙引號括起來

在JavaScript中是嚴格區分大小寫的, alert()和ALERT()不是一回事

在編寫JavaScript代碼的時候, 必定要記住每一句代碼後面都須要添加一個分號, 而且這個分號必須是英文的分號

咱們會發現有時候不寫分號程序也可以運行, 這裏並非由於不須要分號, 而是瀏覽器自動幫助咱們添加了分號, 瀏覽器自動添加會消耗必定的性能, 而且有可能會添加錯誤

2.經過網頁內容區域的形式來輸出

  • document.write(須要輸出的內容);

注意點:

若是須要輸出的內容不是數字, 那麼就必須經過單引號或者雙引號括起來

3.經過開發者工具控制檯的形式來輸出

  • console.log(須要輸出的內容); // 普通輸出

  • console.warn(須要輸出的內容); // 警告輸出

  • console.error(須要輸出的內容); // 錯誤輸出

注意點:

若是須要輸出的內容不是數字, 那麼就必須經過單引號或者雙引號括起來

常量

1.什麼是常量?

常量表示一些固定不變的數據

現實生活中人的性別其實就能夠看作是常量, 生下來是男孩一生都是男孩, 生下來是女孩一生都是女孩

2.JavaScript中常量的分類

  • 整型常量

    • 整型常量其實就是正數, 在JavaScript中隨便寫一個整數都是整型常量,如1 / 666 / 99
  • 實型常量

    • 實型常量其實就是小數, 在JavaScript中隨便寫一個小數都是實型常量,如3.14 6.66
  • 字符串常量

    • 字符串常量其實就是用單引號或者雙引號括起來的內容, 咱們就稱之爲字符串常量,如 'a' / ‘abc' / "1" / "知播漁教育"
    • 注意點: 不管用單引號或者雙引號括起來了多少個字符, 在JavaScript中都是字符串常量
  • 布爾常量

    • 布爾常量其實就是真或者假, 在JavaScript中經過true和false來表達
    • 在JavaScript中布爾常量只有兩個取值, 真(true)或者假(false)
  • 自定義常量

    • 在ES6中新增的
    • const 常量名稱 = 常量取值;

常量

1.什麼是變量?

變量表示一些能夠被修改的數據

在現實生活中超市的儲物格就是變量, 在不一樣的時間段裏面, 儲物格中存儲的數據也不同

2.如何定義一個變量

在JavaScript中能夠經過定義變量的方式來生成儲物格, 也就是告訴瀏覽器, 咱們須要一塊內存空間

var 變量名稱;
複製代碼

3.如何使用變量

使用變量就是往申請的那塊內存空間中存儲數據, 和獲取存儲的數據

3.1如何存儲數據

變量名稱 = 須要存儲的數據;

能夠將等號右邊須要存儲的數據放到等號左邊變量申請的那塊存儲空間中

3.2如何獲取存儲在變量中的數據

變量名稱

// 定義一個變量
var num;
// 往變量中存儲數據
num = 123;
// 從變量中獲取存儲的數據
console.log(num);
複製代碼

4.如何修改變量中存儲的數據

在JavaScript中想要修改變量中存儲的數據, 只須要再次給變量直接賦值便可

num = 666;
console.log(num);
複製代碼

5.在JavaScript中第一次給變量賦值, 咱們稱之爲"變量的初始化"

var num;
num = 321; // "變量的初始化"
num = 888; // 不是"變量的初始化"
複製代碼

6.若是一個變量沒有進行初始化, 那麼變量中存儲的是什麼呢?

在JavaScript中若是定義了一個變量,可是沒有進行初始化, 那麼變量中存儲的是undefined

var num;
console.log(num);
複製代碼

7.給變量初始化有不少種形式

  • 先定義變量, 再對變量進行初始化
var num;
複製代碼
  • 能夠在定義變量的同時對變量進行初始化
var value = 666; // 定義的同時初始化
複製代碼

8.定義變量的其它格式

同時定義多個變量的格式,格式: var 變量名稱1, 變量名稱2, .... ;

var num, value; // 同時定義兩個變量
複製代碼

9.初始化變量的其它格式

若是在企業開發中對多個變量初始化的值都是同樣的, 那麼咱們能夠經過:變量名稱1 = 變量名稱2 = 變量名稱... = 初始化值;

num = value = 123; 
// 同時對num和value進行初始化, num和value中存儲的數據都是123
複製代碼

10.定義多個變量的同時給多個變量分別初始化

var num = 123, value = 666;
複製代碼

11.注意點

  • 在JavaScript中變量之間是能夠相互賦值的
var num;
var value;
num = 123;
value = num; // 將Num中的值拷貝一份給value
console.log(num);
console.log(value);
複製代碼
  • 在JavaScript中若是定義了同名的變量, 那麼後定義的變量會覆蓋先定義的變量
var num = 666;
num = 888; 
// 若是num前面沒有var, 那麼就是修改變量中存儲的值
var num = 888;
// 若是num前面有var, 那麼就不是修改變量中存儲的值, 而是從新定義一個新的變量
複製代碼
  • 在老版本的標準的(ES6以前)JavaScript中能夠先使用變量, 再定義變量, 並不會報錯
  • 因爲JavaScript是一門解釋型的語言, 會邊解析邊執行, 瀏覽器在解析JavaScript代碼以前還會進行一個操做"預解析(預處理)":將當前JavaScript代碼中全部變量的定義和函數的定義放到全部代碼的最前面
console.log(num);
var num = 123;

預處理以後的代碼
var num;
console.log(num); // undefined
num = 123;
複製代碼

12.ES6變量定義

爲了解決老版本標準的的兩個注意點:

  • 在JavaScript中若是定義了同名的變量, 那麼後定義的變量會覆蓋先定義的變量
  • 在老版本的標準的(ES6以前)JavaScript中能夠先使用變量, 再定義變量, 並不會報錯在ES6中就推出了一種新的定義變量的方式
格式:
ES6以前: var 變量名稱;
ES6開始: let 變量名稱;

// 定義一個變量
let num;
// 給變量初始化
num = 666;
// 取出存儲的數據
console.log(num);
// 修改變量中存儲的數據
num = 888;
// 取出存儲的數據
console.log(num);

// var num = 123;
// var num = 888;
let num = 123;
let num = 888; //會明確的報錯
console.log(num);

console.log(num);
var num = 123;   undefined
console.log(num);
let num = 123;   報錯
複製代碼

關鍵字和保留字

什麼是關鍵字?

  • 被JavaScript語言賦予了特殊含義的單詞
  • 關鍵字在開發工具中會顯示特殊顏色
  • 關鍵字不能用做變量名、函數名等
  • 關鍵字嚴格區分大小寫, var和Var前者是關鍵字, 後者不是
  • 只須要記住一點: 在JavaScript中全部的關鍵字都是小寫的
關鍵字
break do instanceof typeof
case else new var
catch finally return void
continue for switch while
default if throw delete
in try function this
with debugger false true
null

什麼是保留字?

  • JavaScript預留的關鍵字,他們雖然如今沒有做爲關鍵字,但在之後的升級版本中有可能做爲關鍵字
保留字
class enum extends super
const export import implements
let private public yield
interface package protected static

標識符

什麼是標識符?

  • 從字面上理解就是用來標識某些東西的符號,標識的目的就是爲了將這些東西區分開來
  • 其實標識符的做用就跟人的名字差很少,爲了區分每一個人,就在每一個人出生時起了名字
    • 平常生活中喬丹、劉德華、吳京這些都是標識符
    • 在編程中標識符就是程序員本身在程序中起的一些名字
    • 例如定義變量時的變量名就是一個標識符

命名規則(必須遵照):

  • 只能由26個英文字母的大小寫、10個阿拉伯數字0~九、下劃線_、美圓符號$組成
  • 不能以數字開頭
  • 嚴格區分大小寫,好比test和Test是2個不一樣的標識符
  • 不可使用關鍵字、保留字做爲標識符
  • JS底層保存標識符其實是採用Unicode編碼,因此理論上講,全部的utf-8中含有的內容均可以做爲標識符

命名規範(建議遵照):

  • 見名知意,變量的名稱要有意義(有利於提升閱讀性)
  • 駝峯命名法,首字母小寫,第二個單詞的首字母大寫(有利於提升閱讀性)

註釋

1.什麼是JS的註釋?

和HTML/CSS的註釋同樣, 都是用來註解解釋某一段程序的含義的, 都是用來提高代碼的閱讀性的, 都是爲了方便程序員之間溝通的

2.JS註釋的格式

  • 單行註釋: // 被註釋的內容
    • 注意點: 單行註釋的有效範圍是從第二個斜槓開始一直直到這一行的末尾, 也就是被註釋的內容不能換行
  • 多行註釋: /* 被註釋的內容 */
    • 注意點: 多行註釋的有效範圍是從第一顆星開始直到第二顆星結束, 也就是說被註釋的內容能夠換行的

3.JS中註釋的嵌套規則

  • 單行註釋能夠嵌套單行註釋 / 多行註釋, 可是必須在一行(多餘)
  • 多行註釋能夠嵌套單行註釋(多餘)
  • 多行註釋不能夠嵌套多行註釋
/*
被註釋的內容
/*
*/
*/
複製代碼

數據類型

1.什麼是數據?

  • 生活中無時無刻都在跟數據打交道,如人的體重、身高、收入、性別等數據
  • 在咱們使用計算機的過程當中,也會接觸到各類各樣的數據,如文檔數據、圖片數據、視頻數據等

2.數據分類

  • 靜態數據

    • 靜態數據是指一些永久性的數據,通常存儲在硬盤中。硬盤的存儲空間通常都比較大,如今普通計算機的硬盤都有500G左右,所以硬盤中能夠存放一些比較大的文件
    • 存儲時長:計算機關閉以後再開啓,這些數據都還在,只要你不主動刪掉或硬盤沒壞,這些數據永遠都在
    • 哪些是靜態數據:通常是以文件形式存儲在硬盤上,好比文檔、圖片、視頻等
  • 動態數據

    • 動態數據指在程序運行過程當中,動態產生的臨時數據,通常存儲在內存中,內存的存儲空間通常比較小,如今普通計算機的內存只有8G左右,所以要謹慎使用內存,不要佔用太多的內存空間
    • 存儲時長:計算機關閉後,這些臨時數據就會被清除
    • 哪些是動態數據:當運行某個程序(軟件)時,整個程序就會被加載到內存中,在程序運行過程當中,會產生各類各樣的臨時數據,這些臨時數據都是存儲在內存中的,當程序中止運行或者計算機被強制關閉時,這個程序產生的全部臨時數據都會被清除
  • 既然硬盤的存儲空間這麼大,爲什麼不把全部的應用程序加載到硬盤中去執行呢?

    • 主要緣由是內存的訪問速度比硬盤快N倍
  • 靜態數據和動態數據的相互轉換:也就是從磁盤加載到內存

  • 動態數據和靜態數據的相互轉換:也就是從內存保存到磁盤

  • 數據的計量單位

    • 無論是靜態仍是動態數據,都是0和1組成
    • 數據越大,包含的0和1越多

3.數據類型概述

做爲程序員最關心的是內存中的動態數據,由於咱們寫的程序就是在內存中的,程序在運行過程當中會產生各類各樣的臨時數據,爲了方便數據的運算和操做,JavaScript對這些數據進行了分類,提供了豐富的數據類型

  • 基本數據類型

    • Number 數值類型

      在JavaScript中不管是整數仍是小數都是屬於數值類型的

    • String 字符串類型

      在JavaScript中不管是經過單引號仍是經過雙引號括起來的內容都是屬於字符串類型的

    • Boolean 布爾類型

      在JavaScript中布爾類型比較特殊, 只有兩個取值true/false

    • Undefined 未定義類型

      在JavaScript中未定義類型比較特殊, 只有一個取值undefined

    • Null 空類型

  • 引用數據類型

    • Object 對象類型
  • 在JavaScript中爲了方便咱們檢測某一種數據是屬於哪種數據類型的, JavaScript提供了一個名稱叫作typeof的操做符

    • 格式: typeof 須要檢測的數據;
let res = typeof 123;
console.log(res);   >>number

let num;
console.log(num);
// 以上代碼的含義是輸出num變量中保存的數據
// 因爲沒有給num這個變量進行初始化, 因此這個變量中保存的數據默認是undefined(取值)

let res = typeof num;
console.log(res);
// 利用typeof檢查num中保存的數據是什麼類型的
// 也就是說利用typeof檢查undefined是屬於什麼類型的(數據類型)
複製代碼

強制類型轉換

1.轉換爲字符串類型

  • toString()
    • Number和Boolean能夠用這個轉換
    • 不會影響原變量
    • null和undefined這兩個值沒有toString()方法
    • 變量名稱.toString()的方式前面不能是常量, 由於常量不能被改變
let value = 123;
console.log(value); 
// 在谷歌瀏覽器的控制檯中若是是Number類型是藍色的

// 如下代碼的含義: 將value變量中存儲的數據拷貝一份, 而後將拷貝的數據轉換爲字符串以後返回
let str = value.toString();
console.log(str); 
// 在谷歌瀏覽器的控制檯中若是是String類型是灰色的

// 注意點: 不能使用常量直接調用toString方法, 由於常量是不能改變的
let str2 = 123.toString();
複製代碼
  • String(常量or變量);
    • 可是對於null和undefined,它會將 null 直接轉換爲 "null"。將 undefined 直接轉換爲 "undefined"
    • 對於Number和Boolean而言,實際上就是調用toString()方法
    • 能夠用常量由於是根據傳入的值從新生成一個新的值, 並非修改原有的值
  • 變量or常量 + "" / 變量or常量 + ''
    • 本質其實就是調用String()函數
let str = 123 + '';
console.log(str);  >>'123'
console.log(typeof str);   >>String
複製代碼

2.轉換爲數值類型

  • String >> 數值

    • 若是字符串中都是數值, 那麼就正常轉換
    • 若是字符串是一個空串" " / "", 那麼轉換以後是0
    • 若是字符串中不只僅是數字, 那麼轉換以後是NaN
  • Boolean >> 數值

    • true轉換以後是1
    • false轉換以後是0
  • undefined >> 數值

  • 轉換以後是NaN

  • null >> 數值

  • 轉換以後是0

  • 空字符串/false/null轉換以後都是0,字符串中不只僅是數字/undefined轉換以後是NaN,其它的正常轉換

  • 轉換方法

    • Number(常量or變量);
    let str = "123";
    console.log(str);   >>"123"
    console.log(typeof str);   >>String
    let num = Number(str);   
    console.log(num);   >>123
    console.log(typeof num);   >>Number
    複製代碼
    • 經過數學運算中的+號和-號來轉換

      雖然經過+/-均可以將其它類型轉換爲數值類型, 可是-會改變數值的正負性

      底層本質上就是調用了Number函數

    let str = "123";
    let num = +str;
    let num = -str;
    複製代碼
    • parseInt(須要轉換的字符串)/parseFloat(須要轉換的字符串)

      從左至右的提取數值, 一旦遇到非數值就會當即中止,若是尚未提取到數值, 那麼就返回NaN

      parseInt/parseFloat都會將傳入的數據當作字符串來處理

    let str = "3.14px";
    let num = parseFloat(str);
    console.log(num);   >>3.14
    console.log(typeof num);   >>Number
    
    let value = true;
    let num = parseInt(value); // parseInt("true");
    console.log(num);   >>NaN
    console.log(typeof num);   >>Number
    複製代碼

3.轉換爲布爾類型

  • String >> 布爾
    • 字符串中有內容爲true, 字符串中沒有內容爲false
  • Number >> 布爾
    • 只有數值是0纔會轉換爲false, 其它的都會轉換爲true
    • 若是是NaN也會轉換爲false(在JavaScript中NaN屬於Number類型)
  • undefined >> 布爾
    • 轉換爲false
  • null >> 布爾
    • 轉換爲false
  • 空字符串/0/NaN/undefined/null 會轉換成false, 其它的都是true
  • 方法:Boolean(常量or變量)
let num = NaN;
console.log(typeof num);   >>number
let flag = Boolean(num);   
console.log(flag);   >>false
console.log(typeof flag);   >>boolean
複製代碼

運算符

簡介

  • 運算符也叫操做符。經過運算符能夠對一個或多個值進行運算,並獲取運算結果。

  • 好比:typeof 就是運算符,能夠來得到一個值的類型。它會將該值的類型以字符串的形式返回

  • +*/( 都是運算符,而(3+5)/2則是表達式

優先級和結合性

  • 從左往右先乘除後加減,有括號先算括號
優先級 運算符 說明 結合性
1 [].() 字段訪問、數組索引、函數調用和表達式分組 從左向右
2 ++ -- -~!delete new typeof void 一元運算符、返回數據類型、對象建立、未定 義的值 從右向左
3 *、/、% 相乘、相除、求餘數 從左向右
4 +、- 相加、相減、字符串串聯 從左向右
5 <<、>>、>>> 左位移、右位移、無符號右移 從左向右
6 <、<=、>、>=、instanceof 小於、小於或等於、大於、大於或等於、是否 爲特定類的實例 從左向右
7 ==、!=、===、!== 相等、不相等、全等,不全等 從左向右
8 & 按位「與」 從左向右
9 ^ 按位「異或」 從左向右
10 | 按位「或」 從左向右
11 && 短路與(邏輯「與」) 從左向右
12 || 短路或(邏輯「或」) 從左向右
13 ?: 條件運算符 從右向左
14 =、+=、-=、*=、/=、%=、&=、|=、^=、<、<=、>、>=、>>= 混合賦值運算符 從右向左
15 , 多個計算 按優先級計算,而後從右向左
算術運算符

1.什麼是算術運算符?

算術運算符
+ 加、字符串鏈接
-
*
/
% 獲取餘數(取餘)
() 括號,優先級

2.算術運算符的優先級和結合性

  • * / % 優先級要高於 + -
  • 不管是+ - * / %都是左結合性(從左至右計算)
let result = 10 % 4;
console.log(result);   >>2
複製代碼

3.注意點

  • 當對非Number類型的值進行運算,會將這些值轉換爲Number而後再運算
result1 = true + 1;  // 2 = 1+ 1
result2 = true + false; // 1 = 1+ 0
result3 = 1 + null; // 1 = 1+ 0
result4 = 100 - '1' // 99
複製代碼
  • 任何值和NaN作運算的結果都是NaN
  • 任何的值和字符串作加法運算,都會先轉換爲字符串,而後再作拼串操做
result1 = 1 + 2 + '3'  // 33
result2 = '1' + 2 + 3; // 123
複製代碼
  • 任何值作-*/運算時都會自動轉換爲Number
  • 取模(取餘)運算:m%n = 餘數
    • 若是m>n的, 那麼就正常取餘
    • 若是m<n的, 那麼結果就是m
    • 若是n是0, 那麼結果就是NaN
    • 取餘運算結果的正負性, 取決於m而不是n
let res = -10 % 3;   // -1
複製代碼
賦值運算符

1.什麼是賦值運算符?

  • 就是將等號右邊的值存儲到等號左邊的變量中
  • 簡單類型:=;複雜類型:+= -= *= /= %=

2.優先級和結合性

  • 賦值運算符的優先級低於算數運算符
  • 右結合性(從右至左的計算)
  • 左邊只能放變量, 不能放常量
//因爲算數運算符的優先級高於賦值運算符因此會先計算1 + 1, 而後再賦值給res
let res = 1 + 1;

// 因爲賦值運算符的結合性是右結合性, 因此會先將3賦值給num2, 而後再將Num2中的值賦值給num1
let num1, num2;
num1 = num2 = 3;
複製代碼
自增自減運算符

1.什麼是自增自減運算符?

  • 自增運算符: ++
  • 自減運算符: --
  • 用在變量中,不能用在常量/表達式中

2.自增

  • 自增分紅兩種:a++++a
  • 對於一個變量自增之後,原變量的值會當即自增1。也就是說,不管是 a++ 仍是++a,都會當即使原變量的值自增1。
  • 要注意的是**:a是變量,而a++++a是**表達式
  • a++的值等於原變量的值(a自增前的值)
  • ++a的值等於新值 (a自增後的值)

3.自減:同上

var n1=10;
var n2=20;

var n = n1++; //n1 = 11 n1++ = 10

console.log('n='+n);  // 10
console.log('n1='+n1); //11

n = ++n1 //n1 = 12 ++n1 =12
console.log('n='+n); //12
console.log('n1='+n1); //12

n = n2--;// n2=19 n2--=20
console.log('n='+n); //20
console.log('n2='+n2); //19

n = --n2; //n2=18 --n2 = 18
console.log('n='+n); //18
console.log('n2='+n2); //18
複製代碼
關係運算符
  • 經過關係運算符能夠比較兩個值之間的大小關係,若是關係成立它會返回true,若是關係不成立則返回false
  • 左結合性(從左至右的運算)
  • > < >= <= 的優先級高於 == != === !==
//正式由於關係運算符是左結合性, 因此不能利用關係運算符來判斷區間
let res = 10 > 5 > 3;  // true > 3; 1 > 3
let res = 10 <= 25 <= 20;  // true <= 20 1 <= 20
複製代碼
關係運算符
> 大於
< 小於
>= 大於或等於
<= 小於或等於
== 等於
=== 全等於
!= 不等於
!== 不全等於
  • 對於非數值類型的數據, 會先轉換成數值類型, 再進行判斷
let res= 1 > true; // 1 > 1
let res= 1 > false;  // 1 > 0
let res= 1 > null;  // 1 > 0
let res= 1 > '10';  // 1 > 10
複製代碼
  • 任何數據和NaN進行比較, 返回值都是false
console.log(10 <= 'hello'); //false
複製代碼
  • 若是參與比較的都是字符串類型, 那麼不會轉換成數值類型再比較, 而是直接比較字符對應的Unicode編碼
    • 比較字符編碼時,是一位一位進行比較。若是兩位同樣,則比較下一位,因此借用它能夠來對英文進行排序
    • 所以當咱們在比較兩個字符串型的數字時,必定必定要先轉型,好比 parseInt()
// 比較兩個字符串時,比較的是字符串的字符編碼,因此可能會獲得不可預期的結果
console.log('56'>'123'); //true

console.log('b' > 'a'); // 0062 > 0061
複製代碼

unicode編碼轉換地址

  • ==符號
    • 這個符號並不嚴謹,會將不一樣類型的東西,轉爲相同類型進行比較
    • 只會判斷取值
    • undefined 衍生自 null,因此這兩個值作相等判斷時,會返回true
    • NaN不和任何值相等,包括他自己
    • 能夠經過isNaN()函數來判斷一個值是不是NaN
console.log('6'== 6);  >>true
console.log(true == '1');  >>true
console.log(0 == -0);  >>true
console.log(null == 0);  >>false

console.log(undefined == null);  >>true

console.log(NaN == NaN);  >>false

console.log(isNaN(b));  >>false
複製代碼
  • ===符號
    • 若是要保證徹底等於,咱們就要用三個等號===全等不會作類型轉換
    • 會同時判斷取值和數據類型
    • ==的反面是!====的反面是!==
console.log('6' === 6);  >>false
console.log(6 === 6);  >>true
複製代碼
邏輯運算符
  • 分類

    • && 與(且):兩個都爲真,結果才爲真。

    • || 或:只要有一個是真,結果就是真。

    • ! 非:對一個布爾值進行取反。

  • 注意事項

  • 左結合性(從左至右的運算),&&的優先級高於||

  • 能參與邏輯運算的,都是布爾值

  • 邏輯短路

    • JS中的&&屬於短路的與,若是第一個值爲false,則不會看第二個值
    • JS中的||屬於短路的或,若是第一個值爲true,則不會看第二個值
    true && alert('hhh');  //hhh
    true || alert('hhh');  //true
    複製代碼
  • 非布爾值運算

    • 若是對非布爾值進行邏輯運算,則會先將其轉換爲布爾值,而後再操做, 但返回結果是原值
    var result = 5 && 6;  //true && true
    console.log(result);  //6
    複製代碼
    • 與運算的返回結果:(以兩個非布爾值的運算爲例)
    • 若是第一個值爲true,則必然返回第二個值(因此說,若是全部的值都爲true,則返回的是最後一個值)
    • 若是第一個值爲false,則直接返回第一個值
    • 或運算的返回結果:(以兩個非布爾值的運算爲例)
    • 若是第一個值爲true,則直接返回第一個值
    • 若是第一個值爲false,則返回第二個值
  • 實際開發(作容錯處理 )

// 當成功調用一個接口後,返回的數據爲 result 對象。這個時候,咱們用變量 a 來接收 result 裏的圖片資源。一般的寫法是這樣的:(這裏我只是舉個例子)

if (result.resultCode == 0) {
		var a = result && result.data && result.data.imgUrl || 'http://img.smyhvae.com/20160401_01.jpg';
}

// 上方代碼的意思是,獲取返回結果中的result.data.imgUrl這個圖片資源;若是返回結果中沒有 result.data.imgUrl 這個字段,就用 http://img.smyhvae.com/20160401_01.jpg 做爲兜底圖片。這種寫法,在實際開發中常常用到。
複製代碼
逗號運算符
  • 逗號運算符 ,
    • 在JavaScript中逗號運算符通常用於簡化代碼
    • 左結合性(從左至右的運算)
    • 優先級是全部運算符中最低的
    • 逗號運算符也是一個運算符, 因此也有運算符結果逗號運算符的運算符結果就是最後一個表達式的結果:表達式1, 表達式2, 表達式3, ....;
// 利用逗號運算符同時定義多個變量
let a,b;
// 利用逗號運算符同時給多個變量賦值
a = 10,b = 5;

let res = ((1+1),(2+2),(3+3));
console.log(res);  //6
複製代碼
三元運算符 / 條件運算符
  • 語法: 條件表達式 ? 語句1 : 語句2;
    • 若是該值爲true,則執行語句1,並返回執行結果
    • 若是該值爲false,則執行語句2,並返回執行結果

流程控制語句

基本概念

  • 默認狀況下,瀏覽器會按書寫從上值下順序執行程序中的每一行代碼,可是這並不能知足咱們全部的開發需求
  • 爲了方便咱們控制程序的運行流程,JS提供了順序結構、選擇結構(if/switch)、循環結構(while/for)三種語句
if語句

1.條件判斷語句

  • 當條件表達式爲真的時候就會執行{}中全部的代碼, 而且只會執行一次
if(條件表達式){
	條件爲真時執行語句;
}

let age = 19;
if(age >= 18){
	console.log('開網卡');
}
console.log('賣飲料');
複製代碼

3.條件分支語句

if(條件表達式){
	條件爲真時執行語句;
}else{
	條件爲假時執行語句;
}

if(條件表達式1){
	條件1爲真時執行語句;
}else if(條件表達式2){
	條件爲1不知足時執行語句;
}else if(條件表達式3){
	條件12不知足時執行語句;
}else{
	條件123都布知足時執行語句;
}
複製代碼

3.注意點

  • 對於非布爾類型的數據, 會先轉換成布爾類型再判斷
if(null){
	console.log('a');
}
console.log('b');
>>b
複製代碼
  • 對於== /===判斷, 將常量寫在前面(避免寫成賦值=而出錯)
let num = 10;
if(5 == num){
	console.log('a');
}
複製代碼
  • if / else if / else後面的大括號均可以省略, 可是省略以後只有緊隨其後的語句受到控制
if(false)
	console.log('a');
	console.log('b');  >>b
複製代碼
  • 在JavaScript中分號(;)也是一條語句(空語句)
if(false);
{
	console.log('a');
	console.log('b');
}
>>;
複製代碼
  • if選擇結構能夠嵌套使用
if(true){
	if(false){
		console.log('a');
	}else{
		console.log('b');
	}
}else{
	if(false){
		console.log('c');
	}else{
		console.log('d');
	}
}
複製代碼
  • 當if選擇結構省略大括號時, else if/else會自動和距離最近沒有被使用的if匹配
if(0)
	if(1)
		console.log('a');
	else
		console.log('b');
else
	if(1)
		console.log('c');
	else
		console.log('d');
複製代碼
在企業開發中, 若是條件知足以後只有一句代碼須要執行, 那麼就使用三元運算符
在企業開發中, 若是條件知足以後有多句代碼須要執行, 那麼就使用選擇結構

let num = prompt('請輸入');
num % 2 === 0 ? alert('yes') : alert('no');

if(num % 2 === 0){
	alert('yes');
	num = 666;
}else{
	alert('no');
	num = 123;
}
console.log(num);
複製代碼
switch語句
switch(表達式){
	case 表達式1:
		語句1;
		break;
	case 表達式2:
		語句2;
		break;
	......
	default:
		前面全部case都不匹配執行的代碼;
		break;
}
複製代碼
  • 會從上至下的依次判斷每個case是否和()中表達式的結果相等, 若是相等就執行對應case後面的代碼, 若是前面全部的case都不匹配, 那麼就會執行default後面的代碼

  • 而且全部的case和default只有一個會被執行, 而且只會被執行一次

  • 注意點:

    • case判斷的是=== , 而不是 ==
    let num = 123;
    switch (num){
    	case'123':
    		console.log('字符串123');
    		break;
    	case 123:
    		console.log('數值123');
    		beak;
    	default:
    		console.log('other');
    		break;
    }
    複製代碼
    • ()中能夠是常量也能夠是變量還能夠是表達式
    // let num = 123;
    // switch (num) { // 變量
    // switch (123) { // 常量
    switch (122 + 1) { // 表達式
        case 123:
            console.log("數值的123");
            break;
        default:
            console.log("Other");
            break;
    }
    複製代碼
    • case後面能夠是常量也能夠是變量還能夠是表達式
    let num = 123;
    switch (123) {
        // case 123: // 常量
        // case num: // 變量
        case 100 + 23: // 變量
            console.log("數值的123");
            break;
        default:
            console.log("Other");
            break;
    }
    複製代碼
    • break的做用是當即結束整個switch語句,在switch語句中一旦case或者default被匹配, 那麼其它的case和default都會失效
    let num = 1;
    switch (num) {
        case 1:
            console.log("1");
            break;
        case 2:
            console.log("2");
            break;
        default:
            console.log("Other");
            break;
    }
    複製代碼
    • default不必定要寫在最後,switch中的default不管放到什麼位置, 都會等到全部case都不匹配再執行
    let num = 7;
    switch (num) {
        // default:
        // console.log("Other");
        // break;
        case 1:
            console.log("1");
            break;
        default:
            console.log("Other");
            break;
        case 2:
            console.log("2");
            break;
    }
    複製代碼
    • 和if/else中的else同樣, default也能夠省略
    let num = 7;
    switch (num) {
        case 1:
            console.log("1");
            break;
        case 2:
            console.log("2");
            break;
    }
    複製代碼

if和switch如何選擇

  • 在企業開發中若是是對區間進行判斷, 那麼建議使用if
  • 在企業開發中若是是對幾個固定的值的判斷, 那麼建議使用switch
  • 原則: 能用if就用if
需求: 要求判斷一個數是不是大於100的數

let num = 20;
if(num > 100){
	alert('大於100的數');
}else{
	alert('不大於100的數');
}
複製代碼

循環語句

while循環
1.if的格式
if(條件表達式){
    條件知足執行的語句;
}
if的特色: 只有條件表達式爲真纔會執行後面{}中的代碼,大括號中的代碼只會被執行一次
                  
2.while的格式
while(條件表達式){
    條件知足執行的語句;
}
while的特色: 只有條件表達式爲真纔會執行後面{}中的代碼,大括號中的代碼有可能會被執行屢次                     
複製代碼
  • 執行流程
    • 首先會判斷條件表達式是否爲真, 若是爲真就執行後面{}中的代碼
    • 執行完後面{}中的代碼, 會再次判斷條件表達式是否還爲真
    • 若是條件表達式還爲真, 那麼會再次執行後面{}中的代碼
    • 直到條件表達式不爲真爲止
let num = 1;
while(num <= 10){
	console.log('發射子彈' + num);
	num++;
}
複製代碼
  • 注意點

    • 什麼是死循環?
      • 條件表達式永遠爲真的循環結構
    while(true){
    	console.log('123');
    }
    複製代碼
    • 什麼是循環體?
      • 循環結構後面的{}
    • 和if同樣對於非Boolean類型的值, 會先轉換爲Boolean類型再判斷
    while(1){
    	console.log('被執行了');
    }
    複製代碼
    • 和if同樣while 後若是隻有一條語句它能夠省略大括號,和if同樣若是省略了後面的{}, 那麼只有緊隨其後的那條語句受到控制

    • 和if同樣, 不能在()後面寫分號 ;

    • 最簡單死循環寫法

    while(1);
    複製代碼
do...while循環
while循環的格式
while(條件表達式){
    須要重複執行的代碼;
}
while循環的特色: 只有條件表達式爲真, 纔會執行循環體

dowhile循環的格式
do{
    須要重複執行的代碼;
}while(條件表達式);
dowhile循環的特色: 不管條件表達式是否爲真, 循環體都會被執行一次
複製代碼

while和do while如何選擇

  • 在企業開發中大部分狀況下while循環和dowhile循環是能夠互換的
  • 在企業開發中若是循環體中的代碼不管如何都須要先執行一次, 那麼建議使用dowhile循環
  • 在企業開發中其它的狀況都建議使用while循環
需求: 要求用戶輸入密碼, 判斷輸入密碼是否正確(假設正確密碼是123456)

let pwd = prompt('請輸入密碼');
while(pwd !== '123456'){
	pwd = prompt('請輸入密碼');
}
alert('歡迎回來');

let pwd = -1;
do{
	pwd = prompt('請輸入密碼');
}while(pwd !== '123456');
alert('歡迎回來');
複製代碼
變量做用域

1.在JavaScript中定義變量有兩種方式

  • ES6以前: var 變量名稱;
  • ES6開始: let 變量名稱;

2.兩種定義變量方式的區別

  • var

    • 能夠重複定義同名的變量, 而且不會報錯, 而且後定義的會覆蓋先定義的
    var num=123;
    var num=456;
    console.log(num);  >>456
    複製代碼
    • 能夠先使用後定義(預解析)
    console.log(num);
    var num=123;  >>undefined
    複製代碼
  • let

    • "相同做用域內"不能夠重複定義同名的變量
    • 在不一樣的做用域範圍內, 是能夠出現同名的變量的
    let num=123;
    let num=456;  >>報錯
    
    //只要出現了let, 在相同的做用域內, 就不能出現同名的變量
    let num = 123;
    var num = 456; // 會報錯
    
    var num = 123;
    let num = 456; // 會報錯
    
    {
    	let num=123;
    	{
    		let num=456;  // 不會報錯
    	}
    }
    複製代碼
    • 不能夠先使用再定義, 由於瀏覽器不會對let定義的變量進行預解析
    console.log(num);
    let num=123;  >>報錯
    複製代碼

3.什麼是全局變量和局部變量

  • 全局變量就是定義在{}外面的變量
  • 局部變量就是定義在{}裏面的變量

4.區別

  • 全局變量

    • 有效範圍是從定義變量的那一行開始直到文件的末尾均可以使用
    • 不管是經過var仍是經過let定義的全局變量, 都是從定義的那一行到文件末尾均可以使用
  • 局部變量,

    • 有效範圍是從定義變量的那一行開始直到大括號結束爲止(只有在大括號中才能使用)
    • 若是是經過var定義的局部變量, 仍是一個全局變量, 後續均可以被使用
    {
    	var num=123;
    }
    console.log(num);  //不會報錯
    複製代碼
    • 若是是經過let定義的局部變量, 那麼這個變量只能在當前定義變量的{}中使用
    {
    	let num=123;
    }
    console.log(num);  //會報錯
    複製代碼
for循環
1			2/5			4/7
for(初始化表達式;條件表達式;循環後增量表達式){
	3/6
	須要重複執行的代碼;
}
複製代碼

1.特色

  • 和while循環的特色同樣, 只有條件表達式爲真, 纔會執行循環體

2.執行流程

  • 首先會執行初始化表達式, 而且只會執行一次
  • 判斷條件表達式是否爲真, 若是條件表達式爲真, 就執行循環體
  • 執行完循環體就會執行循環後增量表達式
  • 直到條件表達式不爲真爲止
for(let num = 1;num <= 10;num++){
	console.log('發送子彈'+num);
}
複製代碼

3.注意點

  • 在for循環中"初始化表達式""條件表達式""循環後增量表達式"均可以不寫,若是不寫就至關於while(1);
  • while循環不能省略條件表達式,for循環是能夠省略條件表達式的, 默認就是真
while(){
	console.log('123');
}
>>報錯

for(;;){
	console.log('123');
}
>>123
複製代碼
  • 其它注意點和while循環同樣

for和while如何選擇

  • 若是是while循環, 在循環結束以後還可使用用於控制循環結束的變量
  • 若是是for循環, 在循環結束以後可讓外界使用, 也能夠不讓外界使用(取決於定義變量let的位置)
  • 在企業開發中因爲for循環比while循環要靈活, 因此能用for循環就用for循環
關鍵字

break

  • break能夠用來退出switch語句或整個循環語句(循環語句包括for、while,不包括if。if裏不能用 break 和 continue,不然會報錯)
  • break會當即終止離它最近的那個循環語句
  • 後面不能編寫任何的語句, 由於永遠執行不到
  • 若是在循環嵌套的結構中, break結束的是當前所在的循環結構
for(let i = 0; i < 5; i++){
    console.log("外面的循環結構" + i);
    for(let j = 0; j < 5; j++){
        console.log("裏面的循環結構-----" + j);
        break;
    }
}
複製代碼

continue

  • 只能用於循環結構,默認只會離他最近的循環起做用。
  • 做用是跳過本次循環, 進入下一次循環
  • 後面和break同樣, 不能編寫其它的代碼, 由於執行夠不到
  • 若是出如今循環嵌套結構中, 只會跳過當前所在的循環
for(let num = 1; num <= 10; num++){
    if(num === 1){
        continue;
        console.log("continue後面的代碼"); // 永遠執行不到
    }
console.log("發射子彈" + num);
複製代碼
//循環嵌套規律

// 在循環嵌套中外循環控制的是行數, 內循環控制的是列數
for(let j = 0;j < 3;j++){
	for(let i = 0;i < 4;i++){
		document.write('*');
	}
	document.write('<br>');
}

// 規律: 若是尖尖朝下, 那麼只須要修改內循環的初始化表達式爲外循環初始化表達式的變量便可
for(let i = 0;i < 5;i++){
	for(let j = i;j < 5;j++){
		document.write('*');
	}
	document.write('<br>');
}

// 規律: 若是尖尖朝上, 那麼只須要修改內循環的條件表達式爲外循環初始化表達式的變量便可
for(let i = 0;i < 5;i++){
	for(let j = 0;j <= i;j++){
		document.write('*');
	}
	document.write('<br>');
}
複製代碼

數組

1.什麼是數組?

  • 數組就是專門用於存儲一組數據的,是引用數據類型(對象類型)

2.如何建立一個數組?

let 變量名稱 = new Array(size);
複製代碼

3.如何操做數據

  • 如何往數組中存儲數據
變量名稱[索引號] = 須要存儲的數據;
複製代碼
  • 如何從數組中獲取存儲的數據
變量名稱[索引號];
複製代碼
let arr = new Array(3);
arr[0] = 'zs';
console.log(arr[0]);
複製代碼

4.注意點

  • 和其它編程語言不一樣, 若是數組對應的索引中沒有存儲數據, 默認存儲的就是undefined,其它編程語言中默認保存的是垃圾數據或者0
let arr = new Array(3);
console.log(arr[0]);  >>undefined
複製代碼
  • 和其它編程語言不一樣, JavaScript中訪問了數組中不存在的索引不會報錯, 會返回undefined,其它編程語言一旦超出索引範圍就會報錯或者返回髒數據
let arr = new Array(3);
console.log(arr[666]);  >>undefined
複製代碼
  • 和其它編程語言不一樣, 當JavaScript中數組的存儲空間不夠時數組會自動擴容,其它編程語言中數組的大小是固定的
let arr = new Array(3);
arr[0] = "lnj";
arr[1] = "zs";
arr[2] = "ls";
arr[3] = "it666";
console.log(arr);  >>['lnj','zs','ls','it666']
複製代碼
  • 和其它編程語言不一樣, JavaScript的數組能夠存儲不一樣類型數據,在其它編程語言中數組只能存儲相同類型數據(要麼所有都是字符串, 要麼所有都是數值等)
let arr = new Array(4);
arr[0] = 123;
arr[1] = "123";
arr[2] = true;
arr[3] = null;
console.log(arr);  >>[123,'123',true,null]
複製代碼
  • 和其它編程語言不一樣, JavaScript中數組分配的存儲空間不必定是連續的

    • 其它語言數組分配的存儲空間都是連續的, JavaScript數組是採用"哈希映射"方式分配存儲空間
    • 什麼是哈希映射? 比如字典能夠經過偏旁部首找到對應漢字, 咱們能夠經過索引找到對應空間
  • 在瀏覽器中各大瀏覽器也對數組分配存儲空間進行了優化

    • 若是存儲的都是相同類型的數據, 那麼會盡可能分配連續的存儲空間
    • 若是存儲的不是相同的數據類型, 那麼就不會分配連續的存儲空間
  • 建立數組的其它方式

    • 經過構造函數建立數組
    let 變量名稱 = new Array(size);  //建立一個指定大小數組
    let 變量名稱 = new Array();  //建立一個空數組
    let 變量名稱 = new Array(data1,data2,...);  
    //建立一個帶數據的數組
    複製代碼
    • 經過字面量建立數組
    let 變量名稱 = [];  //建立一個空數組
    let 變量名稱 = [data1,data2,...];  //建立一個帶數據的數組
    複製代碼
遍歷
  • 數組的遍歷就是依次取出數組中存儲的全部數據
let arr=['a','b',c];
for(let i=0; i<arr.length; i++){
	console.log(arr[i]);
}
複製代碼
解構賦值
  • ES6中新增的一種賦值方式
  • 在數組的解構賦值中, 等號左邊的格式必須和等號右邊的格式如出一轍, 才能徹底解構
let [a,b,c] = [1,3,5];
let [a,b,[c,d]] = [1,3,[2,4]];
複製代碼
  • 左邊的個數能夠和右邊的個數不同
let [a,b] = [1,3,5];
console.log(a);
console.log(b);  >>1,3
複製代碼
  • 右邊的個數能夠和左邊的個數不同
let [a,b,c] = [1];
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);  >>1,undefined,undefined
複製代碼
  • 若是右邊的個數和左邊的個數不同, 那麼咱們能夠給左邊指定默認值
let [a, b = 666, c = 888] = [1];
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);  >>1,666,888
複製代碼
  • 若是左邊的個數和右邊的個數不同, 那麼若是設置默認值會被覆蓋
let [a, b = 666] = [1, 3, 5];
console.log("a = " + a);
console.log("b = " + b);  >>1,3
複製代碼
  • 還可使用ES6中新增的擴展運算符來打包剩餘的數據,可是擴展運算符只能寫在最後
ES6中新增的擴展運算符: ...

let [a, ...b] = [1,3,5];
console.log(a);  >>1
console.log(b);  >>[3,5]
複製代碼
增刪改查

1.查

let arr = ['a','b','c'];
需求: 獲取數組中索引爲1的那個數據 
console.log(arr[1]);
複製代碼

2.改

  • splice
//需求: 將索引爲1的數據修改成m
arr[1] = 'm';
console.log(arr);

//需求: 將索引爲1的數據修改成d, 索引爲2的修改成e
arr.splice(1,2,'d','e');
console.log(arr);
//參數1:從什麼位置開始
//參數2:須要替換多少元素
//參數3:新的內容
複製代碼

3.增

  • push
    • 能夠在數組的最後新增一條數據, 而且會將新增內容以後數組當前的長度返回給咱們
    • 能夠接收1個或多個參數
需求: 要求在數組最後添加一條數據
let res = arr.push('d');
console.log(res);  >>4
console.log(arr);  >>['a','b','c','d']
複製代碼
  • unshift
    • 和push方法同樣, 會將新增內容以後當前數組的長度返回給咱們
    • 能夠接收1個或多個參數
需求: 要求在數組最前面添加一條數據
let res = arr.unshift('m');
console.log(res);  >>4
console.log(arr);  >>['a','b','c','m']
複製代碼

4.刪

  • pop
    • 能夠刪除數組中的最後一條數據, 而且將刪除的數據返回給咱們
需求: 要求刪除數組最後一條數據 
let res = arr.pop();
console.log(res);  >>c
console.log(arr);  >>['a','b']
複製代碼
  • shift
    • 能夠刪除數組中的最前面一條數據, 而且將刪除的數據返回給咱們
需求: 要求刪除數組最前面一條數據
let res = arr.shift();
console.log(res);  >>a
console.log(arr);  >>['b','c']
複製代碼
  • splice
    • 參數1: 從什麼位置開始
    • 參數2: 須要刪除多少個元素
需求: 要求刪除數組中索引爲1的數據
arr.splice(1,1);
console.log(arr);  >>['a','c']
複製代碼
經常使用方法

1..如何清空數組

let arr = [1, 2, 3, 4, 5];
arr = [];  //第一種方法
arr.length = 0;  //第二種方法
arr.splice(0, arr.length)  //第三種方法
console.log(arr);  >>Array(0)
複製代碼

2.如何將數組轉換爲字符串

let str = arr.toString();
console.log(str);  >>1,2,3,4,5
console.log(typeof str);  >>string
複製代碼

3.如何將數組轉換成指定格式字符串

  • join()
    • 默認狀況下若是沒有傳遞參數, 就是調用toString();
    • 若是傳遞了參數, 就會將傳遞的參數做爲元素和元素的鏈接符號
let str = arr.join('+');
console.log(str);  >>1+2+3+4+5
console.log(typeof str);  >>string
複製代碼

4.如何將兩個數組拼接爲一個數組

  • 數組不可以使用加號進行拼接, 若是使用加號進行拼接會先轉換成字符串再拼接
  • concat()
    • 不會修改原有的數組, 會生成一個新的數組返回給咱們
  • 拓展運算符
    • 在解構賦值中(等號的左邊)表示將剩餘的數據打包成一個新的數組
    • 在等號右邊, 那麼表示將數組中全部的數據解開, 放到所在的位置
    • 不會修改原有的數組, 會生成一個新的數組返回給咱們
let arr1 = [1, 3, 5];
let arr2 = [2, 4, 6];
let res = arr1.concat(arr2);  //第一種方法
let res = [...arr1, ...arr2];  //第二種方法
console.log(res);  >>[1,3,5,2,4,6]
console.log(typeof res);  >>object
複製代碼

5.如何對數組中的內容進行反轉

  • reverse()
    • 會修改原有的數組
let res = arr.reverse();
console.log(res);  >>[5,4,3,2,1]
console.log(arr);  >>[5,4,3,2,1]
複製代碼

6.如何截取數組中指定範圍內容

  • slice()
    • 包頭不包尾(包含起始位置, 不包含結束的位置)
let res = arr.slice(1,3);
console.log(res);  >>[2,3]
console.log(arr);  >>[1,2,3,4,5]
複製代碼

7.如何查找元素在數組中的位置

  • indexOf()
    • 若是找到了指定的元素, 就會返回元素對應的位置
    • 若是沒有找到指定的元素, 就會返回-1
    • 默認是從左至右的查找, 一旦找到就會當即中止查找
    • 參數1: 須要查找的元素
    • 參數2: 從什麼位置開始查找
  • lastindexOf()
    • 默認是從右至左的查找, 一旦找到就會當即中止查找
let arr = [1, 2, 3, 4, 5, 3];
let res = arr.indexOf(3);   >>2
let res = arr.indexOf(3,4);   >>5
let res = arr.lastIndexOf(3,4);   >>2
console.log(res);
複製代碼

8.如何判斷數組中是否包含某個元素

  • includes()
    • 有就返回true,沒有就返回false
//能夠經過indexOf和lastIndexOf的結果, 判斷是不是-1便可
let arr = [1, 2, 3, 4, 5];
let res = arr.indexOf(8);  >>-1
let res = arr.lastIndexOf(8);  >>-1
let res = arr.includes(4);  >>true
console.log(res);  
複製代碼

9.練習

  • split()
    • 能夠根據傳入的參數切割字符串, 轉換成一個數組以後返回給咱們
  • fill()
    • 設置數組中全部元素的值爲指定的數據
//已知數組[3, 5, -2, 7, 4], 獲取數組中的最大值並打印
let arr = [3,5,-2,7,4];
let max = arr[0];
for(let i=1;i<arr.length;i++){
	if(arr[1]>max){
		max=arr[i];
	}
}
console.log(max);

//要求用戶輸入3個0~9的數, 輸出0~9中哪些用戶沒有輸入過
let str = prompt('請輸入三個整數,用逗號隔開');
let arr = str.split(',');

//第一種方法
for(let i=0;i<10;i++){
	if(arr[0] == i || arr[1] == i || arr[2] == i){
		continue;
	}
	console.log(i);
}

//第二種方法
let res = new Array(10);
for(let i=0;i<arr.length;i++){
	let res=arr[i];
	res[str]=666;
}
for(let i=0;i<res.length;i++){
	if(res[i]===666){
		continue;
	}
	console.log(i);
}

//從接盤接收5個0~9的數字, 排序後輸出(數組計數)
let str = prompt("請輸入五個整數, 用逗號隔開");
let arr = str.split(",");
let res = new Array(10);
res.fill(0);
for(let i=0;i<res.length;i++){
	let str=arr[i];
	res[str]=res[str]+1;
}
for(let j=0;j<res[i];j++){
	document.write(i);
	document.write('<br>');
}

//從接盤接收4個數字, 排序後輸出
let str = prompt("請輸入五個整數, 用逗號隔開");
let arr = str.split(",");
console.log(arr);

//第一種方法(數組選擇)
for(let i=0;i<3;i++){
	for(let j=i;j<3;j++){
		if(arr[i]>arr[j+1]{
			let temp=arr[i];
			arr[i]=arr[j+1];
			arr[j+1]=temp;
		}
	}
	document.write('<br>');
}
//第二種方法(數組冒泡)
for(let i=0;i<3;i++){
	for(let j=0;j<3-i;j++){
		if(arr[j]>arr[j+1]){
			let temp=arr[j];
			arr[j]=arr[j+1];
			arr[j+1]=temp;
		}
	}
}
console.log(arr);
複製代碼
二維數組

1.什麼是二維數組?

  • 數組的每個元素又是一個數組, 咱們就稱之爲二維數組
let arr=[1,3,5];  //一維數組
let arr=[[1,3],[2,4]];  //二維數組
複製代碼

2.如何操做二維數組?

  • 獲取數據
    • 數組名稱[二維數組索引];
    • 數組名稱[二維數組索引] [一維數組索引];
let arr = [[1, 3], [2, 4]];
let arr1 = arr[0];  //獲取一維
let ele = arr[0][1];  //獲取二維
複製代碼
  • 存儲數據
    • 數組名稱[二維數組索引] = 一維數組;
    • 數組名稱[二維數組索引] [一維數組索引] = 值;
let arr = [[],[]];
arr[0] = [1, 3];
arr[1][0] = 2;
複製代碼
  • 遍歷數組
let arr = [[1, 3], [2, 4]];
for(let i=0;i<arr.length;i++){
	let subArray=arr[i];
	for(let j=0;j<subArray.length;j++){
		console.log(subArray[j]);
	}
}
複製代碼

函數

1.什麼是函數?

  • 函數是專門用於封裝代碼的, 函數是一段能夠隨時被反覆執行的代碼塊

2.函數格式

function 函數名稱(形參列表){
	被封裝的代碼;
}
複製代碼

3.不使用函數的弊端

  • 冗餘代碼太多
  • 需求變動, 須要修改不少的代碼

4.使用函數的好處

  • 冗餘代碼變少了
  • 需求變動, 須要修改的代碼變少了
// 定義向左變道的函數
function toLeft(){
	console.log('打左轉向燈');
	console.log("踩剎車");
	console.log("向左打方向盤");
	console.log("回正方向盤");
}
// 定義向右變道的函數
function toRight(){
	console.log("打右轉向燈");
	console.log("向右打方向盤");
	console.log("回正方向盤");
}
// 向左變道
// 如下代碼的含義: 找到名稱叫作toLeft的函數, 執行這個函數中封裝的代碼
toLeft();
// 向右變道
toRight();
複製代碼
定義步驟

1.書寫函數的固定格式

2.給函數起一個有意義的名稱

  • 爲了提高代碼的閱讀性
  • 函數名稱也是標識符的一種, 因此也須要遵照標識符的命名規則和規範3.

3.肯定函數的形參列表

  • 看看使用函數的時候是否須要傳入一些輔助的數據

4.將須要封裝的代碼拷貝到{}中

5.肯定函數的返回值

  • 能夠經過return 數據; 的格式, 將函數中的計算結果返回給函數的調用者
function getSum(a,b){
	let res=a+b;
	return res;
}
let num1=10;
let num2=20;
let result=getSum(num1,num2);
console.log(result);  >>30
複製代碼

注意點

1.一個函數能夠有形參也能夠沒有形參(零個或多個)

  • 什麼是形參? 定義函數時函數()中的變量咱們就稱之爲形參
// 沒有形參的函數
function say(){
	console.log('hello world');
}
say();
// 有形參的函數
function say(name){
	console.log('hello'+name);
}
say('lnj');
複製代碼

2.一個函數能夠有返回值也能夠沒有返回值

// 沒有返回值的函數
function say(){
	console.log('hello world');
}
say();
// 有返回值的函數
function getSum(a,b){
	return a+b;
}
let res=getSum(10,20);
console.log(res);
複製代碼

3.函數沒有經過return明確返回值, 默認返回undefined

function say(){
	console.log("hello world");
	return;
}
let res = say();
console.log(res);  >>undefined
複製代碼

4.return的做用和break類似, 因此return後面不能編寫任何語句(永遠執行不到)

  • break做用當即結束switch語句或者循環語句
  • return做用當即結束當前所在函數
function say(){
	console.log("hello world");
	return;
	console.log("return後面的代碼");
}
say();  >>hello world
複製代碼

5.調用函數時實參的個數和形參的個數能夠不相同

  • 什麼是實參? 調用函數時傳入的數據咱們就稱之爲實參,實參能夠是常量/變量

6.JavaScript中的函數和數組同樣, 都是引用數據類型(對象類型)

  • 既然是一種數據類型, 因此也能夠保存到一個變量中
  • 將一個函數保存到一個變量中,未來能夠經過變量名稱找到函數並執行函數
let say=function(){
	console.log("hello world");
}
say();
複製代碼
arguments

1.由於console.log();也是經過()來調用的, 因此log也是一個函數

function say(){
	console.log('hello world');
}
window.say();
複製代碼

2.log函數的特色

  • 能夠接收1個或多個參數
console.log(1);
console.log(1,2);
console.log(1,2,3);
複製代碼

3.爲何log函數能夠接收1個或多個參數

  • 內部的實現原理就用到了arguments

4.arguments的做用

  • 保存全部傳遞給函數的實參
//注意點: 每一個函數中都有一個叫作arguments的東東
// arguments實際上是一個僞數組
function getSum(){
	let sum=0;
	for(let i=0;i<arguments.length;i++){
		let num=arguments[i];
		sum +=num;
	}
	return sum;
}
let res=getSum(10,20,30);
console.log(res);  >>60
複製代碼
擴展運算符(ES6)
  • 擴展運算符在等號左邊, 將剩餘的數據打包到一個新的數組中
    • 注意點: 只能寫在最後
let [a, ...b]=[1,3,5];  >>a=1;b=[3,5];
複製代碼
  • 擴展運算符在等號右邊, 將數組中的數據解開
let arr1=[1,3,5];
let arr2=[2,4,6];
let arr=[...arr1, ...arr2]; 
>>let arr=[1,3,5,2,4,6];
複製代碼
  • 擴展運算符在函數的形參列表中的做用
    • 將傳遞給函數的全部實參打包到一個數組中
    • 在等號左邊同樣, 也只能寫在形參列表的最後
function getSum(...values){
	let sum=0;
	for(let i=0;i<values.length;i++){
		let num=values[i];
		sum +=num;
	}
	return sum;
}
let res=getSum(10,20,30);
console.log(res);  >>60
複製代碼
形參默認值
  • 在ES6以前能夠經過邏輯運算符來給形參指定默認值
格式: 條件A || 條件B
若是條件A成立, 那麼就返回條件A
若是條件A不成立, 不管條件B是否成立, 都會返回條件B

function getSum(a,b){
	a = a || "指趣學院";
	b = b || "知播漁教育";
	console.log(a, b);
}
getSum(123, "abc");  >>指趣學院 知播漁教育
複製代碼
  • 從ES6開始, 能夠直接在形參後面經過=指定默認值
    • ES6開始的默認值還能夠從其它的函數中獲取
function getSum(a='指趣學院',b=getDefault()){
	console.log(a,b);
}
getSum();
function getDefault(){
	return '李南江'
}
複製代碼
做爲參數和返回值
  • 將函數做爲其餘函數的參數
let say=function(){
	console.log('hello world');
}
function test(fn){  //let fn=say;
	fn();
}
test(say);  >>hello world
複製代碼
  • 將函數做爲其餘函數的返回值
    • 在其它編程語言中函數是不能夠嵌套定義的,可是在JavaScript中函數能夠
function test(){
	let say=function(){
		console.log('hello world');
	}
	return say;
}
let fn=test();  //let fn=say;
fn();  >>hello world
複製代碼
匿名函數

1.什麼是匿名函數?

  • 匿名函數就是沒有名稱的函數
// 有名稱的函數
function say(){
	console.log('hello lnj');
}
// 匿名函數
function(){
	console.log('hello lnj');
}
複製代碼

2.匿名函數的注意點

  • 匿名函數不可以只定義不使用

3.匿名函數的應用場景

  • 做爲其餘函數的參數
function test(fn){  //let fn=say;
	fn();
}
test(function(){
	console.log('hello world');
});
複製代碼
  • 做爲其餘函數的返回值
function test(){
	return function(){
		console.log('hello world');
	};
}
let fn=test();
fn();
複製代碼
  • 做爲一個當即執行的函數
    • 若是想讓匿名函數當即執行, 那麼必須使用()將函數的定義包裹起來才能夠
(function(){
	console.log('hello it666');
})();
複製代碼
箭頭函數(ES6)

1.什麼是箭頭函數?

  • 箭頭函數是ES6中新增的一種定義函數的格式
  • 目的: 就是爲了簡化定義函數的代碼

2.在ES6以前如何定義函數

function 函數名稱(形參列表){
    須要封裝的代碼;
}
let 函數名稱 = function(形參列表){
    須要封裝的代碼;
}
複製代碼

3.從ES6開始如何定義函數

let 函數名稱=(形參列表)=>{
	須要封裝的代碼;
}
複製代碼

4.箭頭函數的注意點

  • 在箭頭函數中若是隻有一個形參, 那麼()能夠省略
let say = name => {
    console.log("hello " + name);
}
say("it666");  >>hello it666
複製代碼
  • 在箭頭函數中若是{}中只有一句代碼, 那麼{}也能夠省略
let say = name => console.log("hello " + name);
say("it666");  >>hello it666
複製代碼
遞歸函數

1.什麼是遞歸函數?

  • 就是在函數中本身調用本身,在必定程度上能夠實現循環的功能

2.遞歸函數的注意點

  • 每次調用遞歸函數都會開闢一塊新的存儲空間, 因此性能不是很好
需求: 要求用戶輸入密碼, 判斷輸入密碼是否正確(假設正確密碼是123456),若是正確, 輸出"歡迎回來",若是不正確, 要求用戶從新輸入
function login(){
	let pwd=prompt('請輸入密碼');
	if(pwd != '123456'){
		login();
	}
	alert('歡迎回來');
}
login();
複製代碼
變量做用域
  • 在JavaScript中{}外面的做用域, 咱們稱之爲全局做用域

  • 在JavaScript中函數後面{}中的的做用域, 咱們稱之爲"局部做用域"

  • 在ES6中只要{}沒有和函數結合在一塊兒, 那麼應該"塊級做用域"

{
	// 塊級做用域
}

if(false){
	// 塊級做用域
}

while(false){
	// 塊級做用域
}

for(;;){
	// 塊級做用域
}

do{
	// 塊級做用域
}while(false);

switch(){
	// 塊級做用域
}

function say(){
	// 局部做用域
}
複製代碼
  • 塊級做用域和局部做用域區別

    • 在塊級做用域中經過var定義的變量是全局變量
    {
    	var num=123;  // 全局變量
    	let num=123;  // 局部變量
    }
    複製代碼
    • 在局部做用域中經過var定義的變量是局部變量
    function test(){
    	var value=666;  // 局部變量
    	let value=666;  // 局部變量
    	value=666;  // 全局變量
    }
    複製代碼
  • 不管是在塊級做用域仍是在局部做用域, 省略變量前面的let或者var就會變成一個全局變量

var let
{}外 全局變量 全局變量
{}內 全局變量 局部變量
函數內 局部變量 局部變量
做用域鏈

注意點: 初學者在研究"做用域鏈"的時候最好將ES6以前和ES6分開研究

1.ES6以前

  • 須要明確:

    • 定義變量經過var
    • 沒有塊級做用域, 只有全局做用域和局部做用域
    • 函數大括號外的都是全局做用域,函數大括號中的都是局部做用域
  • 做用域鏈

    • 全局做用域咱們又稱之爲0級做用域
    • 定義函數開啓的做用域就是1級/2級/3級/...做用域
    • JavaScript會將這些做用域連接在一塊兒造成一個鏈條, 這個鏈條就是做用域鏈:0 ---> 1 ----> 2 ----> 3 ----> 4
    • 除0級做用域之外, 當前做用域級別等於上一級+1
  • 查找規則

    • 先在當前找, 找到就使用當前做用域找到的
    • 若是當前做用域中沒有找到, 就去上一級做用域中查找
    • 以此類推直到0級爲止, 若是0級做用域還沒找到, 就報錯
// 全局做用域 / 0級做用域
// var num = 123;
function demo(){
	// 1級做用域
	// var num = 456;
	function test(){
		// 2級做用域
		// var num = 789;
		console.log(num);
	}
	test();
}
demo();  >>789
複製代碼

2.ES6

  • 須要明確
    • 定義變量經過let
    • 除了全局做用域、局部做用域之外, 還新增了塊級做用域
    • 雖然新增了塊級做用域, 可是經過let定義變量並沒有差別(都是局部變量)
  • 做用域鏈
    • 全局做用域咱們又稱之爲0級做用域
    • 定義函數或者代碼塊都會開啓的做用域就是1級/2級/3級/...做用域
    • JavaScript會將這些做用域連接在一塊兒造成一個鏈條, 這個鏈條就是做用域鏈:0 ---> 1 ----> 2 ----> 3 ----> 4
    • 除0級做用域之外, 當前做用域級別等於上一級+1
  • 查找規則
    • 先在當前找, 找到就使用當前做用域找到的
    • 若是當前做用域中沒有找到, 就去上一級做用域中查找
    • 以此類推直到0級爲止, 若是0級做用域還沒找到, 就報錯
// 全局做用域 / 0級做用域
// let num = 123;
{
	// 1級做用域
	function test(){
		// 2級做用域
		// let num = 789;
		console.log(num);
	}
	test();
}
複製代碼
預解析

1.什麼是預解析?

  • 瀏覽器在執行JS代碼的時候會分紅兩部分操做:預解析以及逐行執行代碼
  • 也就是說瀏覽器不會直接執行代碼, 而是加工處理以後再執行,這個加工處理的過程, 咱們就稱之爲預解析

2.預解析規則

  • 變量聲明和函數聲明提高到當前做用域最前面
  • 將剩餘代碼按照書寫順序依次放到後面

3.注意點

  • 經過let定義的變量不會被提高(不會被預解析)
// 預解析以前
console.log(num); //undefined
var num = 123;
// 預解析以後
var num;
console.log(num);
num = 123;

// 不會預解析以前
console.log(num); // 報錯
let num = 456;

// ES6以前定義函數的格式

console.log(say);
say();
// ES6以前的這種定義函數的格式, 是會被預解析的, 因此能夠提早調用
function say(){
	console.log("hello it666");
}
// 預解析以後的代碼
function say(){
	console.log("hello it666");
}
say();

console.log(say);
say(); // say is not a function
// 若是將函數賦值給一個var定義的變量, 那麼函數不會被預解析, 只有變量會被預解析
var say=function(){
	console.log("hello itzb");
}
var say; //undefined
say();
var say=function(){
	console.log("hello itzb");
}

// ES6定義函數的格式
say(); // say is not defined
let say=()=>{
	console.log("hello itzb");
}
複製代碼
  • 在高級瀏覽器中, 不會對{}中定義的函數進行提高;只有在低級瀏覽器中, 纔會按照正常的方式解析
//在ES6以前沒有塊級做用域, 而且沒有將這兩個函數定義到其它的函數中,因此這兩個函數應該屬於全局做用域
if(true){
	function demo(){
		console.log("hello demo1111111111");
	}
}else{
	function demo(){
		console.log("hello demo2222222222");
	}
}
demo();

function demo(){
	console.log("hello demo1111111111");
}
function demo(){
	console.log("hello demo2222222222");
}
if(true){}else{}
demo();
複製代碼
  • 若是變量名稱和函數名稱同名, 那麼函數的優先級高於變量
  • 必定要記住, 在企業開發中千萬不要讓變量名稱和函數名稱重名
console.log(value); // 會輸出函數的定義
var value = 123;
function value(){
	console.log("fn value");
}
console.log(value); // 會輸出123

function value(){
	console.log("fn value");
}
console.log(value);
var value;
value = 123;
console.log(value);
複製代碼

面向對象

1.面向過程和麪向對象的區別

  • 面向過程是解決這個問題須要哪些步驟
  • 面向對象是解決這個問題須要哪些對象
//買電腦
1.面向過程
瞭解電腦——瞭解本身的需求——對比參數——去電腦城——微信付錢——買回電腦——被坑
2.面向對象
找班長——描述需求——班長把電腦買回來
複製代碼

2.JavaScript中提供了一個默認的類Object, 咱們能夠經過這個類來建立對象

3.因爲咱們是使用系統默認的類建立的對象, 因此係統不知道咱們想要什麼屬性和行爲, 因此咱們必須手動的添加咱們想要的屬性和行爲

4.如何給一個對象添加屬性

  • 對象名稱.屬性名稱 = 值;

5.如何給一個對象添加行爲

  • 對象名稱.行爲名稱 = 函數;
// 建立對象的第一種方式
let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say=function(){
	console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();

// 建立對象的第二種方式
let obj = {}; // let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say=function(){
	console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();

// 建立對象的第三種方式
// 注意點: 屬性名稱和取值之間用冒號隔開, 屬性和屬性之間用逗號隔開
let obj={
	name: "lnj",
	age: 33,
	say:function(){
		console.log("hello world");
	}
}
console.log(obj.name);
console.log(obj.age);
obj.say();
複製代碼
函數和方法的區別

1.什麼是函數?

  • 函數就是沒有和其它的類顯示的綁定在一塊兒的

2.什麼是方法?

  • 方法就是顯示的和其它的類綁定在一塊兒的

3.函數和方法的區別

  • 函數能夠直接調用, 可是方法不能直接調用, 只能經過對象來調用
  • 函數內部的this輸出的是window, 方法內部的this輸出的是當前調用的那個對象

4.不管是函數仍是方法, 內部都有一個叫作this的東東

  • this是什麼? 誰調用了當前的函數或者方法, 那麼當前的this就是誰
function demo(){
	console.log(this);
}
demo();  //window.demo();

let obj={
	name: "lnj",
	test:function(){
		console.log(this);
	}
}
test();  //obj.test();
複製代碼
工廠函數

1.什麼是工廠函數?

  • 就是專門用於建立對象的函數
function createPerson(myName,myAge){
	let obj = new Object();
	obj.name = myName;
	obj.age = myAge;
	obj.say=function(){
		console.log("hello world");
	}
	return obj;
}
let obj1 = createPerson("lnj", 34);
let obj2 = createPerson("zs", 44);
console.log(obj1);
console.log(obj2);
複製代碼
構造函數

1.什麼是構造函數

  • 和工廠函數同樣, 都是專門用於建立對象的
  • 本質上是工廠函數的簡寫(更專業)

2.構造函數和工廠函數的區別

  • 構造函數的函數名稱首字母必須大寫
  • 構造函數只可以經過new來調用
function Person(myName,MyAge){
	// let obj = new Object(); // 系統自動添加的
	// let this = obj; // 系統自動添加的
	this.name = myName;
	this.age = myAge;
	this.say=function(){
		console.log("hello world");
	}
	// return this; // 系統自動添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1);
console.log(obj2);
//當咱們new Person("lnj", 34);系統作了什麼事情
1)會在構造函數中自動建立一個對象
2)會自動將剛纔建立的對象賦值給this
3)會在構造函數的最後自動添加return this;
複製代碼
function Person(myName, myAge) {
    // let obj = new Object(); // 系統自動添加的
    // let this = obj; // 系統自動添加的
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        // 方法中的this誰調用就是誰, 因此當前是obj1調用, 因此當前的this就是obj1
        // console.log("hello world");
        console.log(this.name, this.age);
    }
    // return this; // 系統自動添加的
}
let obj1 = new Person("lnj", 34);
// console.log(obj1.name);
// console.log(obj1.age);
obj1.say();

function Person(myName, myAge) {
    // let obj = new Object(); // 系統自動添加的
    // let this = obj; // 系統自動添加的
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        console.log("hello world");
    }
    // return this; // 系統自動添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
// 因爲兩個對象中的say方法的實現都是同樣的, 可是保存到了不一樣的存儲空間中,因此有性能問題
console.log(obj1.say === obj2.say); // false

function demo() {
    console.log("demo");
}
// 經過三個等號來判斷兩個函數名稱, 表示判斷兩個函數是否都存儲在同一塊內存中
console.log(demo === demo); // true
複製代碼

4.構造函數優化

  • 第一種:把函數放在全局變量中
function mySay(){
	console.log("hello world");
}
function Person(myName,Myage){
	// let obj = new Object(); // 系統自動添加的
	// let this = obj; // 系統自動添加的
	this.name = myName;
	this.age = myAge;
	this.say = mySay;
	// return this; // 系統自動添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1.say === obj2.say); // true

當前這種方式解決以後存在的弊端:
1)閱讀性下降了
2)污染了全局的命名空間
複製代碼
  • 第二種:函數做爲方法放在對象中
let fns={
	test:function(){
		console.log("test");
	}
}
console.log(fns.test === fns.test); // true
// 因爲test函數都是屬於同一個對象, 因此返回true

let fns = {
    mySay: function () {
        console.log("hello world");
    }
}
function Person(myName, myAge) {
    // let obj = new Object(); // 系統自動添加的
    // let this = obj; // 系統自動添加的
    this.name = myName;
    this.age = myAge;
    this.say = fns.mySay;
    // return this; // 系統自動添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1.say === obj2.say); // true
複製代碼
  • 第三種:函數做爲方法放在prototype(最重要、最專業)
function Person(myName, myAge) {
    // let obj = new Object(); // 系統自動添加的
    // let this = obj; // 系統自動添加的
    this.name = myName;
    this.age = myAge;
    // this.say = fns.mySay;
    // return this; // 系統自動添加的
}
Person.prototype = {
    say: function () {
        console.log("hello world");
    }
}
let obj1 = new Person("lnj", 34);
obj1.say();
let obj2 = new Person("zs", 44);
obj2.say();
console.log(obj1.say === obj2.say); // true
複製代碼
prototype

1.特色

  • 存儲在prototype中的方法能夠被對應構造函數建立出來的全部對象共享
  • 除了能夠存儲方法之外, 還能夠存儲屬性
  • 若是出現了和構造函數中同名的屬性或者方法, 對象在訪問的時候, 訪問到的是構造函中的數據

2.應用場景

  • 通常狀況下用於存儲全部對象都相同的一些屬性以及方法
  • 若是是對象特有的屬性或者方法, 咱們會存儲到構造函數中
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    this.currentType = "構造函數中的type";
    this.say = function () {
        console.log("構造函數中的say");
    }
}
Person.prototype = {
    currentType: "人",
    say: function () {
        console.log("hello world");
    }
}
let obj1 = new Person("lnj", 34);
obj1.say();  >>構造函數中的say
console.log(obj1.currentType);  >>構造函數中的type
let obj2 = new Person("zs", 44);
obj2.say();  >>構造函數中的say
console.log(obj2.currentType);  >>構造函數中的type
複製代碼
對象三角戀關係
  • 每一個"構造函數"中都有一個默認的屬性, 叫作prototype,prototype屬性保存着一個對象, 這個對象咱們稱之爲"原型對象"
  • 每一個"原型對象"中都有一個默認的屬性, 叫作constructor,constructor指向當前原型對象對應的那個"構造函數"
  • 經過構造函數建立出來的對象咱們稱之爲"實例對象",每一個"實例對象"中都有一個默認的屬性, 叫作 proto,__proto__指向建立它的那個構造函數的"原型對象"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);

console.log(Person.prototype);  >>object
console.log(Person.prototype.constructor);  >>Person函數
console.log(obj1.__proto__);  >>object
複製代碼
對象三角戀關係
Function函數
  • JavaScript中函數是引用類型(對象類型),既然是對象,因此也是經過構造函數建立出來的,"全部函數"都是經過Function構造函數建立出來的對象
  • JavaScript中只要是"函數"就有prototype屬性,"Function函數"的prototype屬性指向"Function原型對象"
  • JavaScript中只要"原型對象"就有constructor屬性,"Function原型對象"的constructor指向它對應的構造函數
  • Person構造函數是Function構造函數的實例對象, 因此也有__proto__屬性,Person構造函數的__proto__屬性指向"Function原型對象"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);
console.log(Function === Function.prototype.constructor); // true
console.log(Person.__proto__ === Function.prototype); // true
複製代碼
Object函數
  • JavaScript函數是引用類型(對象類型), 因此Function函數也是對象
  • "Function構造函數"也是一個對象, 因此也有__proto__屬性, "Function構造函數"__proto__屬性指向"Function原型對象"
  • JavaScript中還有一個系統提供的構造函數叫作Object,只要是函數都是"Function構造函數"的實例對象
  • 只要是對象就有__proto__屬性, 因此"Object構造函數"也有__proto__屬性,"Object構造函數"的__proto__屬性指向建立它那個構造函數的"原型對象"
  • 只要是構造函數都有一個默認的屬性, 叫作prototype,prototype屬性保存着一個對象, 這個對象咱們稱之爲"原型對象"
  • 只要是原型對象都有一個默認的屬性, 叫作constructor,constructor指向當前原型對象對應的那個"構造函數"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);
console.log(Function.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Object.prototype.constructor === Object); // true
console.log(Object.prototype.__proto__); // null
複製代碼
函數對象關係
  • Function函數是全部函數的祖先函數
  • 全部的構造函數都有一個prototype屬性, 全部prototype屬性都指向本身的原型對象
  • 全部的原型對象都有一個constructor屬性, 全部constructor屬性都指向本身的構造函數
  • 全部函數都是Function構造函數的實例對象
  • 全部函數都是對象, 包括Function構造函數
  • 全部對象都有__proto__屬性
  • 普通對象的__proto__屬性指向建立它的那個構造函數對應的"原型對象"
  • 全部對象的__proto__屬性最終都會指向"Object原型對象"
  • "Object原型對象"的__proto__屬性指向NULL

2

原型鏈
  • 對象中__proto__組成的鏈條咱們稱之爲原型鏈,如:實例對象——Person原型對象——Object原型對象
  • 對象在查找屬性和方法的時候, 會先在當前對象查找,若是當前對象中找不到想要的, 會依次去上一級原型對象中查找,若是找到Object原型對象都沒有找到, 就會報錯
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    // this.currentType = "構造函數中的type";
    // this.say = function () {
    // console.log("構造函數中的say");
    // }
}
Person.prototype = {
    // 注意點: 爲了避免破壞原有的關係, 在給prototype賦值的時候, 須要在自定義的對象中手動的添加constructor屬性, 手動的指定須要指向誰
    constructor: Person,
    // currentType: "人",
    // say: function () {
    // console.log("hello world");
    // }
}
let obj1 = new Person("lnj", 34);
// obj1.say(); 
console.log(obj1.currentType);  >>報錯
// console.log(Person.prototype.constructor);
複製代碼
屬性注意點
  • 在給一個對象不存在的屬性設置值的時候, 不會去原型對象中查找, 若是當前對象沒有就會給當前對象新增一個不存在的屬性
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
Person.prototype = {
    constructor: Person,
    currentType: "人",
    say: function () {
        console.log("hello world");
    }
}
let obj = new Person("lnj", 34);
obj.currentType = "新設置的值";
console.log(obj.currentType); // 新設置的值
console.log(obj.__proto__.currentType); // "人"
複製代碼
  • 因爲私有屬性的本質就是一個局部變量, 並非真正的屬性, 因此若是經過 對象.xxx的方式是找不到私有屬性的, 因此會給當前對象新增一個不存在的屬性
function Person() {
    this.name = "lnj";
    let age = 34;
    this.setAge = function (myAge) {
        if (myAge >= 0) {
            age = myAge;
        }
    }
    this.getAge = function () {
        return age;
    }
    this.say = function () {
        console.log("hello world");
    }
}
let obj = new Person();
// 1.操做的是私有屬性(局部變量)
obj.setAge(-3);
console.log(obj.getAge());
// 2.操做的是公有屬性
obj.age = -3;
console.log(obj.age);
複製代碼
屬性方法分類
  • 實例屬性/實例方法
    • 在企業開發中經過實例對象訪問的屬性, 咱們就稱之爲實例屬性
    • 在企業開發中經過實例對象調用的方法, 咱們就稱之爲實例方法
  • 靜態屬性/靜態方法
    • 在企業開發中經過構造函數訪問的屬性, 咱們就稱之爲靜態屬性
    • 在企業開發中經過構造函數調用的方法, 咱們就稱之爲靜態方法
function Person() {
    this.name = "lnj";
    this.say = function () {
        console.log("hello world");
    }
}
     
// 經過構造函數建立的對象, 咱們稱之爲"實例對象"
let obj = new Person();
console.log(obj.name);
obj.say();

obj.age = 34;
console.log(obj.age);
obj.eat = function () {
    console.log("eat");
}
obj.eat();
// 構造函數也是一個"對象", 因此咱們也能夠給構造函數動態添加屬性和方法
Person.num = 666;
Person.run = function () {
    console.log("run");
}
console.log(Person.num);
Person.run();
複製代碼
bind-call-apply

1.this是什麼?

  • 誰調用當前函數或者方法, this就是誰

2.這三個方法的做用是什麼?

  • 用於修改函數或者方法中的this的

3.三者區別

  • bind方法做用
    • 修改函數或者方法中的this爲指定的對象, 而且會返回一個修改以後的新函數給咱們
    • bind方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須寫在this對象的後面
  • call方法做用
    • 修改函數或者方法中的this爲指定的對象, 而且會當即調用修改以後的函數
    • call方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須寫在this對象的後面
  • apply方法做用
    • 修改函數或者方法中的this爲指定的對象, 而且會當即調用修改以後的函數
    • apply方法除了能夠修改this之外, 還能夠傳遞參數, 只不過參數必須經過數組的方式傳遞
let obj={
	name:'zs';
}
function test(a,b){
	console.log(a,b);
	console.log(this);
}
//修改屬性
let fn=test.bind(obj,10,20);
fn();  >>10 20 {name:'zs'}

test.call(obj,10,20);  >>10 20 {name:'zs'}

test.apply(obj,[10,20]);  >>10 20 {name:'zs'}

//修改方法
function Person(){
    this.name='lnj';
    this.say=function(){
        console.log(this);
    }
}
let p=new Person();

let fn=p.say.bind(obj);
fn();  >>{name:'zs'}

p.say.call(obj);  >>{name:'zs'}

p.say.apply(obj);  >>{name:'zs'}
複製代碼
面向對象三大特性

1.封裝性

  • 局部變量和局部函數
    • 不管是ES6以前仍是ES6, 只要定義一個函數就會開啓一個新的做用域
    • 只要在這個新的做用域中, 經過let/var定義的變量就是局部變量
    • 只要在這個新的做用域中, 定義的函數就是局部函數
  • 什麼是對象的私有變量和函數
    • 默認狀況下對象中的屬性和方法都是公有的, 只要拿到對象就能操做對象的屬性和方法
    • 外界不能直接訪問的變量和函數就是私有變量和私有函數
    • 構造函數的本質也是一個函數, 因此也會開啓一個新的做用域, 因此在構造函數中定義的變量和函數就是私有函數
  • 什麼是封裝?
    • 封裝性就是隱藏實現細節,僅對外公開接口
  • 爲何要封裝?
    • 不封裝的缺點:當一個類把本身的成員變量暴露給外部的時候,那麼該類就失去對屬性的管理權,別人能夠任意的修改你的屬性
    • 封裝就是將數據隱藏起來,只能用此類的方法才能夠讀取或者設置數據,不可被外部任意修改. 封裝是面向對象設計本質(將變化隔離)。這樣下降了數據被誤用的可能 (提升安全性和靈活性)
function Person() {
    this.name = "lnj";
    // this.age = 34;
    let age = 34;
    this.setAge = function (myAge) {
        if(myAge >= 0){
            age = myAge;
        }
    }
    this.getAge = function () {
        return age;
    }
    this.say = function () {
        console.log("hello world");
    }
    // 因爲構造函數也是一個函數, 因此也會開啓一個新的做用域
    // 因此在構造函數中經過var/let定義的變量也是局部變量
    // 因此在構造函數中定義的函數也是局部函數
    var num = 123;
    let value = 456;
    function test() {
        console.log("test");
    }
}
let obj = new Person();
// 結論: 默認狀況下對象的屬性和方法都是公開的, 只要拿到對象就能夠操做對象的屬性和方法
// console.log(obj.name);
// obj.age = -3;
// console.log(obj.age);
// obj.say();
複製代碼

2.繼承性

  • 簡介

    • 兒子繼承父親的物品就是繼承最好的體現
    • 目的:把子類型中共同的屬性和方法提取到父類型中
    • 優點:較少代碼的冗餘度,提高代碼的複用性
  • 繼承方式一

    • 在企業開發中若是構造函數和構造函數之間的關係是is a關係, 那麼就可使用繼承來優化代碼, 來減小代碼的冗餘度
    • 學生 is a 人 , 學生是一我的
    • 弊端:後構造函數沒法同時使用前構造函數和新增的形參
function Person() {
    this.name = null;
    this.age = 0;
    this.say = function () {
        console.log(this.name, this.age);
    }
}
let per = new Person();
per.name = "lnj";
per.age = 34;
per.say();

function Student() {
    // this.name = null;
    // this.age = 0;
    // this.say = function () {
    // console.log(this.name, this.age);
    // }
    this.score = 0;
    this.study = function () {
        console.log("day day up");
    }
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

//弊端
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        console.log(this.name, this.age);
    }
}
function Student(myName, myAge, myScore) {
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
複製代碼
  • 繼承方式二(call)
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    this.say = function () { // stu.say = function () {}
        console.log(this.name, this.age);
    }
    // return this;
}
function Student(myName,myAge,myScore){
    //let stu=new Object();
    //let this=stu;
    Person.call(this,myName,myAge);
    this.score=myScore;
    this.study=function(){
        console.log('day day up');
    }
    return this;
}
let stu=new Student('ww',19,99);
console.log(stu.score);  >>99
stu.say();  >>19,99
stu.study();  >>day day up
複製代碼
  • 繼承方式三(把父構造函數的原型對象裏的東西用到子構造函數)
    • 弊端
      • 因爲修改了Person原型對象的constructor屬性, 因此破壞了Person的三角戀關係
      • 因爲Person和Student的原型對象是同一個, 因此給Student的元素添加方法, Person也會新增方法
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    // return this;
}
Person.prototype.say = function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
// 注意點: 要想使用Person原型對象中的屬性和方法, 那麼就必須將Student的原型對象改成Person的原型對象才能夠
Student.prototype=Person.prototype;
Student.prototype.constructor=Student;
let stu = new Student("ww", 19, 99);
console.log(stu.score);  >>99
stu.say();  >>ww,19
stu.study();  >>day day up
複製代碼
  • 繼承方式四(終極方法,記住這個就能夠了,最重要)
    • 在子類的構造函數中經過call藉助父類的構造函數
    • 將子類的原型對象修改成父類的實例對象
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    // return this;
}
Person.prototype.say = function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
Student.prototype=new Person();
Student.prototype.constructor=Student;
Student.prototype.run=function(){
    console.log('run');
}
let per=new Person();
per.run();  >>報錯
複製代碼

3.多態性

  • 什麼是強類型語言, 什麼是是弱類型語言

    • 強類型語言
      • 通常編譯型語言都是強類型語言,要求變量的使用要嚴格符合定義
      • 例如定義 int num; 那麼num中未來就只可以存儲整型數據
    • 弱類型語言
      • 通常解釋型語言都是弱類型語言,不會要求變量的使用要嚴格符合定義
      • 例如定義 let num; num中既能夠存儲整型, 也能夠存儲布爾類型等
      • 因爲js語言是弱類型的語言, 因此咱們不用關注多態
  • 什麼是多態?

    • 指事物的多種狀態
    • 例如:按下 F1 鍵這個動做,若是當前在 webstorm 界面下彈出的就是 webstorm 的幫助文檔;若是當前在 Word 下彈出的就是 Word 幫助;同一個事件發生在不一樣的對象上會產生不一樣的結果。
  • 多態在編程語言中的體現

    • 父類型變量保存子類型對象, 父類型變量當前保存的對象不一樣, 產生的結果也不一樣
//強類型語言(繼承+多態)
function Animal(myName) {
    this.name = myName;
    this.eat = function () {
        console.log(this.name + " 動物吃東西");
    }
}
function Dog() {
    Animal.call(this, myName);
    this.eat = function () {
        console.log(" 狗吃東西");
    }
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
function Cat() {
    Animal.call(this, myName);
    this.eat = function () {
        console.log(" 貓吃東西");
    }
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
function feed(Animal animal) {
    animal.eat(); // 狗吃東西
}
//弱類型語言
function Dog() {
    //Animal.call(this, myName);
    this.eat = function () {
        console.log(" 狗吃東西");
    }
}
function Cat() {
    //Animal.call(this, myName);
    this.eat = function () {
        console.log(" 貓吃東西");
    }
}
function feed(animal){
    animal.eat();
}
let dog = new Dog();
feed(dog);  >>狗吃東西
let cat = new Cat();
feed(cat);  >>貓吃東西
複製代碼
ES6類和對象

1.在ES6以前若是定義一個類?

  • 經過構造函數來定義一個類
function Person(myName, myAge) {
    // 實例屬性
    // this.name = "lnj";
    // this.age = 34;
    this.name = myName;
    this.age = myAge;

    // 實例方法
    this.say = function () {
        console.log(this.name, this.age);
    }
    // 靜態屬性
    Person.num = 666;
            // 靜態方法
    Person.run = function () {
        console.log("run");
    }
}
// let p = new Person();
let p = new Person("zs", 18);
p.say();
console.log(Person.num);
Person.run();
複製代碼
  • 從ES6開始系統提供了一個名稱叫作class的關鍵字, 這個關鍵字就是專門用於定義類的
    • 當咱們經過new建立對象的時候, 系統會自動調用constructor,constructor咱們稱之爲構造函數
    • 在ES6標準中添加實例屬性都須要在constructor中添加
    • 在ES標準中static只支持定義靜態方法不支持定義靜態變量
class Person{
    //實例屬性
    constructor(myName,myAge){
        this.name=myName;
        this.age=myAge;
        this.hi=function(){
            console.log('hi');
        }
    }
    // 如下定義"實例屬性"的方式並非ES6正式版標準中的寫法, 大部分的瀏覽器不支持
    // name = "lnj";
    // age = 34;
    //實例方法(會自動添加到原型位置中,若是想要該方法放在原函數裏,那就放在constructor裏)
	say(){
        console.log(this.name,this.age);
    }
	//靜態屬性
    // 如下定義"靜態屬性"的方式並非ES6正式版標準中的寫法, 大部分的瀏覽器不支持
    static num=666;
	//靜態方法
	static run(){
        console.log('run');
    }
}
let p=new Person('zs',18);
p.say();  >>zs,18
console.log(Person.rum);  >>666
Person.run();  >>run
複製代碼
  • 在原型上添加方法和屬性
    • 若是經過class定義類, 那麼不能自定義這個類的原型對象
    • 若是想將屬性和方法保存到原型中, 只能動態給原型對象添加屬性和方法
//構造函數
//第一種方法:
Person.prototype.type='人';
Person.prototype.say=function(){
    console.log(this.name,this.age);
};
//第二種方法:
Person.prototype={
    constructor:Person,
    type:'人',
    say:function(){
        console.log(this.name,this.age);
    }
};
//ES6類
Person.prototype.type='人';
Person.prototype.say=function(){
    console.log(this.name,this.age);
};
複製代碼
ES6繼承
  • ES6以前的繼承
    • 在子類中經過call/apply方法藉助父類的構造函數
    • 將子類的原型對象設置爲父類的實例對象
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
Person.prototype.say =  function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    // 1.在子類中經過call/apply方法藉助父類的構造函數
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
// 2.將子類的原型對象設置爲父類的實例對象
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student("zs", 18, 99);
stu.say();
複製代碼
  • ES6開始的繼承
    • 在子類後面添加extends並指定父類的名稱
    • 在子類的constructor構造函數中經過super方法藉助父類的構造函數
class Person{
    constructor(myName, myAge){
        // this = stu;
        this.name = myName; // stu.name = myName;
        this.age = myAge; // stu.age = myAge;
    }
    say(){
        console.log(this.name, this.age);
    }
}
// 如下代碼的含義: 告訴瀏覽器未來Student這個類須要繼承於Person這個類
class Student extends Person{
    constructor(myName,myAge,myScore){
        super(myName,myAge);  //至關於Person.call(this,myName,myAge);
        this.score=myScore;
    }
    study(){
        console.log('day day up');
    }
}
let stu=new Student('zs',18,98);
stu.say();
複製代碼
獲取對象類型
let obj = new Object();
console.log(typeof obj); // object

let arr = new Array();
console.log(arr.constructor.name);  //Array

function Person() {
    // let obj = new Object();
    // let this = obj;
    this.name = "lnj";
    this.age = 34;
    this.say = function () {
        console.log(this.name, this.age);
    }
    // return this;
}
let p = new Person();
// console.log(typeof p); // object
console.log(p.constructor.name); // Person
複製代碼
instanceof關鍵字

1.什麼是instanceof關鍵字?

  • instanceof用於判斷 "對象" 是不是指定構造函數的 "實例"

2.instanceof注意點

  • 只要構造函數的原型對象出如今實例對象的原型鏈中都會返回true
class Person{
    name = "lnj";
}
let p = new Person();
console.log(p instanceof Person);  >>true

class Cat{
    name = "mm";
}
let c = new Cat();
console.log(c instanceof Person); // false

function Person(myName) {
    this.name = myName;
}
function Student(myName, myScore) {
    Person.call(this, myName);
    this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student();
console.log(stu instanceof Person); // true
複製代碼
isPrototypeOf屬性

1.什麼是isPrototypeOf屬性

  • 用於判斷 一個對象是不是另外一個對象的原型

2.isPrototypeOf注意點

  • 只要調用者在傳入對象的原型鏈上都會返回true
class Person{
    name = "lnj";
}
let  p = new Person();
console.log(Person.prototype.isPrototypeOf(p)); // true

class Cat{
    name = "mm";
}
console.log(Cat.prototype.isPrototypeOf(p)); // false

function Person(myName) {
    this.name = myName;
}
function Student(myName, myScore) {
    Person.call(this, myName);
    this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student();
console.log(Person.prototype.isPrototypeOf(stu)); // true
複製代碼
判斷對象屬性
  • in
  • hasOwnProperty
// 需求: 判斷某一個對象是否擁有某一個屬性
class Person{
    name=null;
	age=0;
}
Person.prototype.height=0;
let p = new Person();
// in的特色: 只要類中或者原型對象中有, 就會返回true
console.log("name" in p); // true
console.log("height" in p); // true

// 需求: 判斷某一個對象自身是否擁有某一個屬性
// hasOwnProperty特色: 只會去類中查找有沒有, 不會去原型對象中查找
console.log(p.hasOwnProperty("name")); // true
console.log(p.hasOwnProperty("height")); // false
複製代碼
對象增刪改查

1.增長(C)

class Person{}
let p=new Person();
//增長屬性
p.name='gh';
p['name']='hj';
//增長方法
p.say=function(){
   console.log('hello');
}
p['say']=function(){
   console.log('hello');
}
複製代碼

2.刪除(R)

//刪除屬性
delete p.name;
delete p['name'];

//刪除方法
delete p.say;
delete p['say'];
複製代碼

3.修改(U)

p.name = "lnj";
p["name"] = "ww";
console.log(p.name);

p.say = function(){
    console.log("hi");
}
p["say"] = function(){
    console.log("hi");
}
p.say();
複製代碼

4.查詢(D)

console.log(p.name);
console.log(p["name"]);
p.say();
複製代碼
對象遍歷

1.在JavaScript中對象和數組同樣是能夠遍歷的

2.什麼是對象的遍歷?

  • 對象的遍歷就是依次取出對象中全部的屬性和方法

3.如何遍歷一個對象?

  • 在JS中能夠經過高級for循環來遍歷對象
//如下代碼的含義: 將指定對象中全部的屬性和方法的名稱取出來了依次的賦值給key這個變量
for(let key in obj){}

// ES6
class Person{
    constructor(myName, myAge){
        this.name = myName;
        this.age = myAge;
    }
    // 注意點: ES6定義類的格式, 會將方法默認放到原型對象中,遍歷取不出來
    say(){
        console.log(this.name, this.age);
    }
}
let p=new Person("LNJ", 34);
for(let key in p){
    console.log(key);
}
>>lnj 34
複製代碼
  • ES6以前
function Person(myName, myAge){
    this.name = myName;
    this.age = myAge;
    this.say = function(){
        console.log(this.name, this.age);
    }
}
let p = new Person("LNJ", 34);
for(let key in p){
    if(p[key] instanceof Function){
        continue;  //判斷是否是函數
    }
    // console.log(key); // name / age / say
    // 注意點: 如下代碼的含義取出p對象中名稱叫作當前遍歷到的名稱的屬性或者方法的取值
    console.log(p[key]); // p["name"] / p["age"] / p["say"]
    // 注意點: 如下代碼的含義取出p對象中名稱叫作key的屬性的取值
    // console.log(p.key); // undefined
}
複製代碼
對象解構賦值

1.注意點

  • 對象的解構賦值和數組的解構賦值 除了符號不同, 其它的如出一轍
  • 數組解構使用[]
  • 對象解構使用{}

2.在數組的解構賦值中, 等號左邊的格式必須和等號右邊的格式如出一轍, 才能徹底解構

let [a, b, c] = [1, 3, 5];
console.log(a, b, c); // 1 3 5
複製代碼

3.在數組的解構賦值中, 兩邊的個數能夠不同

let [a, b] = [1, 3, 5];
console.log(a, b); // 1 3
let [a, b, c] = [1, 3];
console.log(a, b, c); // 1 3 undefined

複製代碼

4.在數組的解構賦值中,若是右邊少於左邊, 咱們能夠給左邊指定默認值

let [a, b, c = 666] = [1, 3];
console.log(a, b, c); // 1 3 666
複製代碼

5.練習

let obj={
    name:'lk',
    age:35
}
let {name,age}=obj;
let {name,age}={name:'lk',age:35};
console.log(name,age);  >>lk,35

let {name}={name:'lk',age:35};
console.log(name,age);  >>lk

let {name, age, height} = {name: "lnj",age: 34};
console.log(name, age, height);  >>lnj,34,undefined

let {name, age, height = 1.80} = {name: "lnj",age: 34};
console.log(name, age, height);  >>lnj,34,1.80

//注意點: 在對象解構賦值中, 左邊的變量名稱必須和對象的屬性名稱一致, 才能解構出數據
let {age} = {name: "lnj",age: 34};
console.log(age); // 34
let {a, b} = {name: "lnj",age: 34};
console.log(a, b); // undefined undefined
複製代碼

6.應用場景

//數組應用
let arr=[1,3];
function sum([a,b]){
    return a+b;
}
let res=sum(arr);
console.log(res);  >>4
//對象應用
let obj={
    name:'lk',
    age:15
}
function say({name,age}){
    console.log(name,age);
}
say(obj);  >>lk,15
複製代碼
深拷貝和淺拷貝

1.什麼是深拷貝什麼是淺拷貝?

  • 深拷貝
    • 修改新變量的值不會影響原有變量的值
    • 默認狀況下基本數據類型都是深拷貝
  • 淺拷貝
    • 修改新變量的值會影響原有的變量的值
    • 默認狀況下引用類型都是淺拷貝
//深拷貝
let num1 = 123;
let num2 = num1;
num2 = 666; // 修改形變量的值
console.log(num1);
console.log(num2);
//淺拷貝
class Person{
    name = "lnj";
    age = 34;
}
let p1 = new Person();
let p2 = p1;  //傳遞內存地址
p2.name = "zs"; // 修改變量的值
console.log(p1.name);  >>zs
console.log(p2.name);  >>zs
複製代碼

2.對象深拷貝

  • assign() —— 只對屬性有效
class Person{
    name = "lnj";
    age = 34;
}
let p1 = new Person();
let p2 = new Object();
//第一種方式
p2.name = p1.name;
p2.age = p1.age;
p2.name = "zs";
//第二種方式
for(let key in p1){
    p2[key]=p1[key];
}
p2.name='zs';
//第三種方式
// assign方法能夠將第二個參數的對象的屬性和方法拷貝到第一個參數的對象中
Object.assign(p2, p1);
p2.name = "zs";
console.log(p1.name);
console.log(p2.name);
// 注意點: 只有被拷貝對象中全部屬性都是基本數據類型, 以上代碼纔是深拷貝
複製代碼
  • depCopy()
class Person{
    name = "lnj";
    cat = {
        age : 3
    };
    scores = [1, 3, 5];
}
let p1 = new Person();  >>3
let p2 = new Object();  >>666
depCopy(p2,p1);
function depCopy(target,source){
    // 1.經過遍歷拿到source中全部的屬性
    for(let key in source){
        // 2.取出當前遍歷到的屬性對應的取值
        let sourceValue=source[key];
    	// 3.判斷當前的取值是不是引用數據類型
        if(sourceValue instanceof Object){
            let subTarget new sourceValue.constructor;
            target[key]=subTarget;
            depCopy(subTarget,sourceValue);
        }else{
            target[key]=sourceValue;
        }
    }
}
複製代碼

3.調試技巧

F12—Source:能夠斷點看函數的執行過程

數組高級API

1.遍歷數組

  • for in
    • 注意點: 在企業開發中不推薦使用forin循環來遍歷數組

MDN的解釋 developer.mozilla.org/zh-CN/docs/…

  • for of
  • forEach
    • forEach方法會自動調用傳入的函數
    • 每次調用都會將當前遍歷到的元素和當前遍歷到的索引和當前被遍歷的數組傳遞給這個函數
// 需求: 要求遍歷數組
// 1.利用傳統循環來遍歷數組
let arr = [1, 3, 5, 7, 9];
for(let i=0;i<arr.length;i++){
    console.log(arr[i]);
}
//2.利用forin循環來遍歷數組
for(let key in arr){
    console.log(arr[key]);
}

function Person() {
    this.name = "lnj";
    this.age = 34;
    this.score = 99;
}
// 注意點: 對象中的屬性是無序的
// forin循環是專門用於遍歷對象的, 可是對象的屬性是無序的, 因此forin循環就是專門用於遍歷無序的東西的, 因此不推薦使用forin循環來遍歷數組
let p = new Person();
console.log(p);

//3.利用ES6中推出的for of循環來遍歷數組
for(let value of arr){
    console.log(value);
}

//4.還能夠利用Array對象的forEach方法來遍歷數組
arr.forEach(function(currentValue,currentIndex,curentArray){
    console.log(currentValue);
});
//forEach實現原理
Array.prototype.myForEach=function(fn){
    for(let i=0;i<this.length;i++){
        fn(this[i],i,this);
    }
};
arr.myForEach(function(currentValue,currentIndex,currentArray){
    console.log(currentValue,currentIndex,currentArray);
});
複製代碼

2.數組查找

  • findIndex方法
    • 定製版的indexOf, 找到返回索引, 找不到返回-1
  • find方法
    • find方法返回找到的元素
    • find方法若是找到了就返回找到的元素, 若是找不到就返回undefined
let arr = [3, 2, 6, 7, 6];
// 從左往右查找, 找到返回索引, 找不到返回-1
let index1 = arr.indexOf(6);
console.log(index1); // 2
// 從右至左查找, 找到返回索引, 找不到返回-1
let index2 = arr.lastIndexOf(6);
console.log(index2); // 4
// 從左往右查找, 找到返回true, 找不到返回false
let result = arr.includes(6);
console.log(result); // true

//數組的findIndex方法
let index = arr.findIndex(function (currentValue, currentIndex, currentArray) {
    // console.log(currentValue, currentIndex, currentArray);
    // if(currentValue === 6){ 
    if(currentValue === 10){
        return true;
    }
});
console.log(index);  >>-1
// findIndex實現
Array.prototype.myFindIndex = function (fn) {
    for(let i = 0; i < this.length; i++){
        let result = fn(this[i], i, this);
        if(result){
            return i;
        }
    }
    return -1;
}

//數組的find方法
let value = arr.find(function (currentValue, currentIndex, currentArray) {
    // console.log(currentValue, currentIndex, currentArray);
    // if(currentValue === 6){
    if(currentValue === 10){
        return true;
    }
});
console.log(value);  >>undefined
// findIndex實現
Array.prototype.myFind = function (fn) {
    for(let i = 0; i < this.length; i++){
        let result = fn(this[i], i, this);
        if(result !== undefined){
            return result;
        }
    }
    return undefined;
}
複製代碼

3.數組過濾和映射

  • filter方法
    • 將知足條件的元素添加到一個新的數組中
let arr = [1, 2, 3, 4, 5];
let newArray=arr.filter(function(currentValue,currentIndex,currentArray){
    if(currentValue % 2 === 0){
        return true;
    }
});
console.log(newArray);  >>[2,4]
// filter實現
Array.prototype.myFilter=function(fn){
    let newArray=[];
    for(let i=0;i<this.length;i++){
        let result=fn(this[i],i,this);
        if(result){
            newArray.push(this[i]);
        }
    }
    return newArray;
}
複製代碼
  • map方法
    • 將知足條件的元素映射到一個新的數組中
let newArray=arr.map(function(currentValue,currentIndex,currentArray){
    if(currentValue % 2 === 0){
        return currentValue;
    }
});
console.log(newArray);  >>[undefined,2,undefined,4,undefined]
// map實現
Array.prototype.myMap=function(fn){
    let newArray=new Array(this.length);
    newArray.fill(undefined);
    for(let i=0;i<this.length;i++){
        let result=fn(this[i],i,this);
        if(result !== undefined){
            newArray[i]=result;
        }
    }
    return newArray;
}
複製代碼
刪除數組元素注意點
  • splice
  • delete
let arr = [1, 2, 3, 4, 5];
// 需求: 遍歷的同時刪除數組中全部元素
//第一種方法
let len=arr.length;
for(let i=len-1;i>=0;i--){
    arr.splice(i,1);
}
//第二種方法
// 注意點: 經過delete來刪除數組中的元素, 數組的length屬性不會發生變化
for(let i=0;i<arr.length;i++){
    console.log(arr.length);  >>5
    delete arr[i];
}
console.log(arr);  >>[1, 2, 3, 4, 5]

複製代碼
數組排序方法
  • compareFunction(a, b)
    • 若是 compareFunction(a, b) 小於 0 ,那麼 a 會被排列到 b 以前;
    • 若是 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變。
    • 若是 compareFunction(a, b) 大於 0 , b 會被排列到 a 以前
    • 注意點: 若是元素是字符串類型, 那麼比較的是字符串的Unicode編碼
let arr = ["c", "a", "b"];
//arr.sort();
arr.sort(function(a,b){
    if(a>b){
        return -1;
    }else if(a<b){
        return 1;
    }else{
        return 0;
    }
});
console.log(arr);

//規律: 若是數組中的元素是數值類型,若是須要升序排序, 那麼就返回a - b;若是須要降序排序, 那麼就返回b - a;
let arr = [3, 4, 2, 5, 1];
// arr.sort();
arr.sort(function(a,b){
	return a-b;
});
console.log(arr);

let arr = ["1234", "21", "54321", "123", "6"];
arr.sort(function(str1,str2){
    return str2.length-str1.length;
});
console.log(arr);

let students = [
    {name: "zs", age: 34},
    {name: "ls", age: 18},
    {name: "ww", age: 22},
    {name: "mm", age: 28},
];
students.sort(function (o1, o2) {
    // return o1.age - o2.age;
    return o2.age - o1.age;
});
console.log(students);
複製代碼
字符串經常使用方法
  • 在js中字符串能夠看作一個特殊的數組, 因此大部分數組的屬性/方法字符串均可以使用
// 1.獲取字符串長度 .length
let str='abcd';
console.log(str.length);  >>4

// 2.獲取某個字符 [索引] / charAt
let str='abcd';
//第一種方法,僅高級瀏覽器支持
let ch=str[1];  
//第二種方法,沒有兼容性問題
let ch=str.charAt(1);  
console.log(ch);  >>a

// 3.字符串查找 indexOf / lastIndexOf / includes
let str = "vavcd";
let index=str.indexOf('v');  >>0
let index=str.lastIndexOf('v');  >>2
console.log(index);
let result=str.includes('p');
console.log(result);  >>false

// 4.拼接字符串 concat / +
let str1 = "www";
let str2 = "it666";
let str=str1+str2;  //推薦
let str=str1.concat(str2);
console.log(str);

// 5.截取子串 slice / substring / substr
let str = "abcdef";
let subStr=str.slice(1,3);  >>bc
let subStr=str.substring(1,3);  >>bc
let subStr=str.substr(1,3);  >>bcd
console.log(subStr);

// 6.字符串切割
let arr = [1, 3, 5];
let str=arr.join('-');
console.log(str);  >>1-3-5
let str = "1-3-5";
let arr=str.split('-');
console.log(arr);  >>['1','3','5']

// 7.判斷是否以指定字符串開頭 ES6
let str = "www.it666.com";
let result=str.startsWith('www');
console.log(result);  >>true

// 8.判斷是否以指定字符串結尾 ES6
let str = "lnj.jpg";
let result=str.endsWith('png');
console.log(result);  >>true

// 9.字符串模板 ES6
//常規方法
let str = "";
let str = '';
//新方法:反引號
let str=`www.it666.com`;
console.log(str);  >>www.it666.com
console.log(typeof str);  >>string

let str =   "<ul>\n" +
            " <li>我是第1個li</li>\n" +
            " <li>我是第2個li</li>\n" +
            " <li>我是第3個li</li>\n" +
            "</ul>";
//上面的字符串每行都要有加號,換成反引號就不用加號了
let str = `<ul> <li>我是第1個li</li> <li>我是第2個li</li> <li>我是第3個li</li> </ul>`;

let name = "lnj";
let age = 34;
let str=`個人名字是${name},個人年齡是${age}`;
console.log(str);  >>個人名字是lnj,個人年齡是34
複製代碼
基本數據類型和基本包裝類型

1.有哪些基本數據類型?

  • 字符串類型 / 數值類型 / 布爾類型 / 空類型 / 未定義類型

2.經過字面量建立的基本數據類型的數據都是常量

3.常量的特色和注意點

  • 常量是不能被修改的
  • 每次修改或者拼接都是生成一個新的(每次拼接都會開闢新的存儲空間,多了會帶來性能問題,因此要少用)

4.基本類型特色

  • 沒有屬性和方法

5.對象類型的特色

  • 有屬性和方法

6.之前之因此可以訪問基本數據類型的屬性和方法, 是由於在運行的時候系統自動將基本數據類型包裝成了對象類型

  • String() / Number() / Boolean()
//修改常量
let str = "abc";
str[1] = "m";  
console.log(str);  // abc
let newStr=str.replace('b','m');
console.log(newStr); // amc

//修改拼接
let str1 = "www";
let str2 = "it666";
let str3 = str1 + str2;
console.log(str1);  //www
console.log(str2);  //it666
console.log(str3);  //wwwit666

//基本類型沒有屬性和方法
let str = "lnj";
str.age = 34;
str.say=function(){
    console.log('hello');
}
console.log(str.age); // undefined
str.say(); // str.say is not a function

//系統自動將基本數據類型包裝成了對象類型
let str = "www.it666.com";
// let str = new String(str); //隱藏過程
console.log(str.length);
str.split(".");
複製代碼
三大對象

JavaScript中提供三種自帶的對象, 分別是"本地對象"/"內置對象"/"宿主對象"

什麼是宿主?

  • 宿主就是指JavaScript運行環境, js能夠在瀏覽器中運行, 也能夠在服務器上運行(nodejs)

1.本地對象

  • 與宿主無關,不管在瀏覽器仍是服務器中都有的對象,就是ECMAScript標準中定義的類(構造函數)。
  • 在使用過程當中須要咱們手動new建立,例如:Boolean、Number、String、Array、Function、Object、Date、RegExp等。

2.內置對象

  • 與宿主無關,不管在瀏覽器仍是服務器中都有的對象,ECMAScript已經幫咱們建立好的對象。
  • 在使用過程當中無需咱們手動new建立,例如:Global、Math、JSON

3.宿主對象

  • 對於嵌入到網頁中的JS來講,其宿主對象就是瀏覽器, 因此宿主對象就是瀏覽器提供的對象
  • 包含: Window和Document等
  • 全部的DOM和BOM對象都屬於宿主對象

4.自定義對象

  • 咱們本身編寫的類建立的對象
Math內置對象
  • Math.floor() 向下取整
    • 直接砍掉全部的小數位就是向下取整
let num = 3.9;
let value=Math.floor(num);
console.log(value);  //3
複製代碼
  • Math.ceil() 向上取整
    • 只要有小數位就會給整數位+1, 而後砍掉全部小數位
let num = 3.9;
let value=Math.ceil(num);
console.log(value);  //4
複製代碼
  • Math.round() 四捨五入
    • 和小學數學同樣, 若是小數位滿5就會進1
let num = 3.5;
let value=Math.round(num);
console.log(value);  //4.0
複製代碼
  • Math.abs() 絕對值
    • 和小學數學同樣, 統一變爲正數
let num = -3;
let value=Math.abs(num);
console.log(value);  //3
複製代碼
  • Math.random() 生成隨機數
    • 會生成一個0~1的隨機數, 可是不包括1
let value = Math.random();
console.log(value);
// 需求: 要求生成一個1~10的隨機數
function getRandomIntInclusive(min,max){
    min=Math.ceil(min);
    max=Math.floor(max);
    return Math.floor(Math.random()*(max-min+1))+min;  
}  //含最大值,含最小值,從MDN複製過來
let value=getRandomIntInclusive(1,10);
console.log(value);
複製代碼
相關文章
相關標籤/搜索