《javascript面向對象編程指南》讀書筆記

《javascript面向對象編程指南》讀書筆記



第一章 面向對象的JavaScript

  • HTML專一於內容,CSS專一於表現,JavaScript專一於行爲。javascript

  • JavaScript術語包含三個部分:php

    1. ECMAScript:語言的核心部分(變量、函數、循環等等),獨立於瀏覽器,能夠在其餘環境中使用。
    2. 文檔對象模型(DOM)
    3. 瀏覽器對象模型(BOM)
  • ECMAScript和JavaScript的區別:
    ECMAScript是爲了規範各類前端腳本語法的標準(後端node.js也包含ECMAScript),JavaScript只是其的一種實現。css

第二章 基本數據類型與流程控制

變量

  • 變量名能夠由字母、數字、下劃線、美圓符號組合而成。
  • 變量名不能以數字開頭
  • 變量名區分大小寫。

數據類型

共6大數據類型html

  • 值類型
    1. 數字
    2. 字符串
    3. 布爾值
    4. undefined
    5. null
  • 引用類型
    1. object

typeof

  • 做用:查看變量的類型是什麼
  • 取值:
    1. "number"
    2. "string"
    3. "boolean"
    4. "undefined"
    5. "object"
    6. "function" (相比6種數據類型,null變成了function)

注意:前端

  • typeof(null) === 'object'; //true
  • 檢測變量存在且初始化過用typeof(somevar) !== "undefined"

數字

  1. 整數:255
  2. 浮點數:255.0
  3. 八進制數:0377
  4. 十六進制數: 0xff
  5. 指數:2.55e+2
  6. Infinity、-Infinity:超出JavaScript處理範圍的數值
  7. NaN:表示not a number,NaN!=NaN;//true,只能經過isNaN(變量)判斷是否爲數字,而不能經過=

字符串

  1. 字符串+數字=字符串
  2. 字符串與數字進行減、乘、除運算=數字
轉義字符
字符串 含義
\\、\'、\" 因爲\、'、"在js中是關鍵符號,因此須要轉義
\n 換行符
\r 回車符
\t 製表符
\u \u後面的字符將會被視爲Unicode碼

bool值

if(變量)時,如下值被看成false。java

  • 空字符串
  • null
  • undefined
  • 數字0
  • 數字NaN

注意:node

  • 不表明以上值 == false,如null == false; //false
  • if('false')if('0')都爲true,由於非空字符串看成true
邏輯運算符
  • 優先級:!> && > || ,但爲了可讀性,最好加括號。git

  • 惰性求值:前面的知足,後面不會執行。es6

比較運算符
操做符 名稱 說明 例子
== 相等運算符 null == undefined;//true
'1'==true; //true
'1'==1; //true
=== 嚴格相等運算符 類型相同&&值相同 null === undefined; //false
1 === '1'; //false
!= 不相等運算符 NaN!=NaN;//true
'1'!=1; //false
!== 嚴格不相等運算符 '1'!==1; //true
> 大於運算符 '2'>1 ;//true
>= 大於等於運算符 1>='1'; //true
< 小於運算符 1<'2'; //true
<= 小於等於運算符 1<='1'; //true

注意:github

  • 以上表格僅針對值類型,對於引用類型,若引用的是同一個對象,則相等且嚴格相等。
  • 後四種沒有對應的嚴格大於、嚴格大於等於、......。

undefined與null

  • 聲明而不初始化一個變量時,javascript會自動用undefined值來初始化。
  • 當與數字進行運算時,undefined返回NaN,null則會被看成0進行運算

例子:

let somevar;
 somevar === undefined;//true
1*undefined;//NaN;
 1*null;//0

數組

數組爲引用類型,typeof([]) === 'object' //true

數組元素的增刪改查
  • 聲明:let a = [2,4,5]
  • 增長:a[4]=6;//跳過了a[3],a[3]將爲undefined
  • 刪除:delete a[0];//刪除後數組長度不變,被刪除地方的值變爲undefined
  • 更新:a[4]=7
  • 訪問:a[0]

注意:

  • delete元素不會改變數組長度,要想改變數組長度見第四章的Array的pop和splice方法。
  • 字符串也以看成數組用索引訪問每一個字符。
多維數組

Map(ES6規範)

  • 存在必要:JavaScript中的{}的鍵只能是字符串,若要以其餘數據類型(包括引用類型)爲鍵則能夠用到map。
  • 初始化:

    let m1 = new Map(); // 空Map
    let m2 = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
  • 操做:
    |屬性/方法|做用|
    |---|---|
    |size|返回成員數量|
    |clear()|清空全部成員,無返回值|
    |has(key)|判斷是否存在指定成員,返回值爲 true / false|
    |get(key)|獲取指定成員的值,如不存在則返回 undefined|
    |set(key, value)|爲key設置鍵值,如已經存在該key則更新,不然添加新元素,返回值是實例自己|
    |delete(key)|刪除key的鍵值對,返回值爲 true / false|
  • 注意:map的鍵必須惟一,若加入重複的鍵,後面的值會沖掉前面的值。

Set(ES6規範)

  • 初始化:

    let s1 = new Set(); // 空Set
    let s2 = new Set([1, 2, 3]); // 含1, 2, 3
  • 操做:
    |屬性/方法|做用|
    |---|---|
    |size|返回成員數量|
    |clear()|清空全部成員,無返回值|
    |has(ele)|判斷是否存在指定成員,返回值爲 true / false|
    |add(ele)|添加元素ele,若是已經存在,沒有變更,不然添加,返回值是實例自己|
    |delete(ele)|刪除某個值,返回值爲 true / false|
  • 注意:

    1. Set中的值必須惟一,重複的會自動保留一個。(map鍵、set重複的標準:值類型必須值嚴格相等,引用類型必須引用同一個對象)
    2. Set沒有數組那種經過索引取值的方法,只可以遍歷。

流程控制

條件語句

  • if、if else、if else if...else
  • switch
  • 三元表達式

循環語句

  • while
  • do while
  • for
  • for in
  • for of(es6規範)
  • forEach

注意:for in循環遍歷鍵,而for of和forEach會遍歷鍵值。

第三章 函數

函數定義

  • 聲明式定義

    function func(){} //在全局中,或函數中的函數,多用此法定義
  • 函數標識記法

    let func = function(){}    //函數看成對象一個屬性時,用詞定義法,以下:
        let student = {
          name:zhangsan,
          study:function(){
            console.write('study hard!');
          }
        }

js中函數也是一種數據(應該屬於六種數據類型的object,雖然說typeof爲'function',而不是'object'),故命名規則和變量同樣用駝峯,而不是C#中的帕斯卡。

參數

  • arguments:能夠經過索引獲取傳過來的全部參數,相似數組但不是。

    function foo(x) {
      for (let i=0; i<arguments.length; i++) {
        console.log(arguments[i]);
      }
    }
  • rest(ES6規範):獲取多餘的參數。

    function foo(a, b, ...rest) {
      console.log('a = ' + a);
      console.log('b = ' + b);
      console.log(rest);
    }
    
    foo(1, 2, 3, 4, 5);
    // 結果:
    // a = 1
    // b = 2
    // Array [ 3, 4, 5 ]
    
    foo(1);
    // 結果:
    // a = 1
    // b = undefined
    // Array []

返回值

  • 若函數沒有返回值,默認返回undefined。(new 調用構造函數除外)

內建函數

  • parseInt()
    轉換字符串爲整數

    parseInt('123abc1');//輸出123,遇到第一個字母或其餘非法符號截斷
    parseInt('FF',16);//輸出255,第二個參數爲進制
    parseInt('0x377');//輸出887,以0x開頭表明16進制,無需指定進制
    parseInt('0377');//輸出377,雖然以0開頭表明八進制,但易與十進制混淆,因此仍是當成十進制
    parseInt('1.258e2');//125
  • parseFloat()
  • isNaN()
    判斷變量不是數字不能用a!==NaN,由於NaN===NaN返回false,因此只能用isNaN判斷。
  • isFinite()
    表示是否有限,注意Infinity、-Infinity表明正負無限

    isFinite(Infinity);//false
    isFinite(-Infinity);//false
    isFinite(1e309);//false,由於超出了js能表示的最大數字
  • encodeURI()
    • 存在必要:URL中如/?&等都是關鍵字符,有特殊含義,若須要他們僅僅做爲字符出現,則須要'轉義'
    • 編碼:通常對中文、空格編碼,保持 ASCII字母、數字、~!*()@#$&=:/,;?+' 不變。
    • 使用場景:對整個url編碼,返回一個完整格式的url。
  • decodeURI()
    對應encodeURL的解碼。
  • encodeURIComponent()
    • 編碼:保留 ASCII字母、數字、~!*()',其餘字符編碼。
    • 使用場景:對部分url編碼,如queryString中一項參數的值也是一個url,則爲了轉義其中的/?&等關鍵字,須要先用encodeURIComponent編碼,再與整個url拼接起來
  • encodeURIComponent()
    對應encodeURL的解碼。
  • escape()、unescape()
    **已棄用,用於字符串的編解碼,多用於將中文轉義成16進製表示
  • eval()
    • 做用:將一段字符串看成js腳本執行。
    • 缺點:
      1. 安全性差
      2. 動態執行代碼,效率差
  • alert()
    彈窗,且會阻塞js線程。

變量做用域

  • var聲明的做用域爲函數體。
  • let、const聲明的做用域爲大括號。
  • 函數內不加var、let、const修飾的做用域爲全局。不過,要等到函數被調用後變量纔會建立。
  • 全局變量也能夠經過window.變量名獲取到

變量提高

  • 定義:執行過程進入函數時,函數內變量的定義會提高到函數開始處。
  • 特色:提高定義,賦值不提高。
  • 例子:

    let a = 123;
    function f(){
      alert(a);
      let a = 1;
      alert(a);
    }
    f();
    //輸出:先彈出'undefined',再彈出1.

    解釋:由於函數域始終優先與全局域,因此全局的123沒做用。上面代碼被等價的改寫爲下面

    let a = 123;
    function f(){
      let a; //變量會被提高至此,且js引擎默認初始化爲undefined
      alert(a);
      a = 1; //賦值不會提高,只提高了定義
      alert(a);
    }
    f();

匿名函數

  • 通常用來執行一次性的函數,如自執行函數或回調函數
  • 通常寫成箭頭函數形式()=>{}。(ES6規範)

回調函數

  • 函數做爲一個參數傳遞個另外一個函數,並在後面這個函數中調用。

即時函數(自執行函數)

多用於執行一些一次性的或初始化的任務。

function (name){
  alert('Hello'+name+'!');
}('dude'); //匿名自執行函數

內部(私有)函數

函數內再定義函數。

function outer(param) {
  function inner(theinput) {
    return theinput * 2;
  }
  return 'The result is ' + inner(param);
}

函數中this指向的坑

坑1:

let xiaoming = {
    birth: 1990,
    age: function () {//根據出生日期獲取xiaoming年齡
        let y = new Date().getFullYear();
        return y - this.birth;
    }
};

let fn = xiaoming.age;//fn指向了age函數,fn跟xiaoming沒任何關係
fn();
//輸出:Uncaught TypeError: Cannot read property 'birth' of undefined
//緣由:至關於window調用的fn,age中的this.birth就是window.birth.

坑2:

'use strict';

let xiaoming = {
    birth: 1990,
    age: function () {//根據出生日期獲取xiaoming年齡
        console.log(this.birth);//這裏的this正常的指向xiaoming對象
        function getAgeFromBirth() {
            let y = new Date().getFullYear();
            return y - this.birth;//嵌套函數中的this又指向了window
        }
        return getAgeFromBirth();
    }
};
xiaoming.age();
//輸出:Uncaught TypeError: Cannot read property 'birth' of undefined
//緣由:對象中的嵌套函數(第二層及以上)中的this指向window,只有直接子級函數的this指向該對象自己。

解決辦法:

  1. 在子級函數中let that = this;保存住對象的引用,而後在孫子及更深層嵌套的函數中用that.屬性名訪問對象屬性。
let xiaoming = {
  birth: 1990,
  getAge: function () {
      let that = this;
      function getAgeFromBirth() {
        let y = new Date().getFullYear();
        return y - that.birth;//that指向了xiaoming
      }
      return getAgeFromBirth();
  }
};
xiaoming.getAge();
  1. 用箭頭函數。
let xiaoming = {
  birth: 1990,
  getAge: function () {
      let fn = () => new Date().getFullYear() - this.birth; // this指向xiaoming
      return fn();
  }
};
xiaoming.getAge();

返回函數的函數

function a(){
  alert('A!');
  return function(){
    alert('B!');
  }
}
let func = a();//彈出'A'
func();//彈出'B'
//或者直接以下
a()();//先彈出'A',後彈出'B'

能重寫本身的函數

function a() {
    alert('A!');
    a = function(){
      alert('B!');
    };  
  }

  a();//彈出'A!'
  a();//彈出'B!'

應用場景:瀏覽器兼容性探測,初始化時根據瀏覽器類型,重寫某些方法。

閉包

做用域鏈

私有變量能夠訪問自身做用域(var爲函數,let、const爲塊)和其外層做用域,就造成了一條做用域鏈。

寫法

用一個全局變量指向內層的函數,這樣經過這個全局變量就能夠訪問內層函數同級的變量,突破了做用域鏈。(看着像從外層訪問裏層)

  1. 函數套函數,返回一個函數
let F = function () {
  let b = "local variable";
  let N = function () {
    let c = "inner local";
    return b;
  };
  return N;
};
let inner = F();
inner();//輸出"local variable"
  1. 函數套函數,裏層函數賦值給外層變量
let inner;
let F = function (){
  let b = "local variable";
  let N = function () {
    return b;
  };
  inner = N;
};
F();
inner();//輸出"local variable"

函數所綁定的是做用域自己,而不是在函數定義時該做用域中的變量或變量當前所返回的值。
若須要綁定(非引用類型)變量當前值的快照,則能夠經過調用傳參。由於函數傳參是傳的當前值的拷貝。

應用

  • getter與setter
    經過setter給變量賦值能夠加驗證。

    //變量寫成局部變量,不可直接訪問。經過getter和setter暴露取值和賦值的接口。
    let getValue, setValue;
    (function() {
      let secret = 0;
      getValue = function(){
        return secret;
      };
      setValue = function (v) {
        if (typeof v === "number") {
          secret = v;
        }
      };
    }());
    
    
    getValue();//輸出0
    setValue(123);
    getValue();//輸出123
    setValue(false);//false驗證失敗,賦值不成功
    getValue();//輸出123
  • 迭代器

function setup(x) {
  let i = 0;
  return function(){
    return x[i++];
  };
}

let next = setup(['a', 'b', 'c']);
next();//輸出"a"
next();//輸出"b"
next();//輸出"c"

第四章 對象

js中對象與數組的區別

鍵類型 數據類型
數字 數組
字符串 對象
任意類型 map

在一些程序語言中,一般會存在兩種不一樣的數組形式。

  1. 通常性數組,也叫索引型數組或枚舉型數組(一般以數字爲鍵名)。
  2. 關聯型數組,也叫哈希表或者字典(一般以字符串爲鍵名)。

js中數組表示索引型數組,對象表示關聯型數組。

對象聲明和訪問

聲明

1.文本標識法

let obj = {
  breed: 'Turtle',
  occupation: 'Ninja'
};

對象的屬性名若不符合變量命名規範,則屬性名須要加引號。

let obj = {
  'this':'abc',
  '123':123
}

2.構造函數

function Hero() {
  this.occupation = 'Ninja';
}
let hero = new Hero();
  • 構造函數中無需寫return,自動返回this。若顯式return了一個object,則以顯示return的爲準。
  • 構造函數首字母大寫。

屬性的訪問

1.經過變量名.屬性名訪問

hero.occupation;

2.經過變量名[屬性名]訪問

hero['occupation'];

此種訪問有兩種使用場景:

  1. 屬性名是一個變量
  2. 屬性名不符合js變量命名規範

屬性的增刪改

增:hero.name = 'zhangsan';
改:hero.name = 'lisi';
刪:delete hero.name;

全局對象

瀏覽器環境中,全局對象爲window。
調用全局變量能夠 a 或 window.a 或 this.a 。

constructor屬性

指向實例化時所用的構造函數。若用文本標識法建立對象,則它的constructor指向Object()。

h2.constructor;
//輸出
//function Hero(name){
//  this.name = name;  
//}
let o = {};
o.constructor;
//輸出 function Object(){[native code]}

instanceof操做符

用於判斷某個對象是否由某個指定的構造器或其父級構造器所建立。

h instanceof Hero;//true
h instanceof Object;//true

對象做爲參數傳遞

  • 對象類型的以引用類型傳遞,引用同一份。
  • 值類型拷貝一份再傳遞。

比較對象

當且僅當倆引用指向同一對象時,倆對象相等且嚴格相等。

let fido = {breed: 'dog'};
let benji = {breed: 'dog'};

fido == benji;//輸出false

內建對象

內建對象大體分三類。

  1. 數據封裝類對象————包括Object、Array、Boolean、Number 和String。
  2. 工具類對象————包括Math、Date、RegExp 等用於提供便利的對象。
  3. 錯誤類對象————包括通常性錯誤對象以及其餘各類更特殊的錯誤類對象。它們能夠在某些異常發生時幫助咱們糾正程序工做狀態。

Object

Object是全部對象的父級對象。
let o = {}let o = new Object()等價。
Object含如下三個內建屬性:

  1. o.constructor————返回構造函數的引用。
  2. o.toString()————返回對象的描述字符串。
  3. o.valueOf()————返回對象的單值描述信息,一般就是對象自己。

Array

數組也是一種對象。

let a = [];
typeof a;//Object
a instanceof Object;//true

相比對象的特殊之處

  1. 數組的屬性名從0開始遞增,並自動生成數值;
  2. 數組有一個用於記錄元素數量的length屬性;
  3. 數組在Object上擴展了更多的內建方法。
length屬性
  • length屬性會隨着數字鍵名的數量而更新,而忽略非數字鍵名屬性。

    a[0] = 0;
      a.prop = 2;//非數字鍵名不會增長length
      a.length;//輸出1
      a;//輸出[0,prop:2];
  • 當手動設置length的值大於數組中元素數量時,剩下的部分會被undefined填充。
  • 當手動設置length的值小於數組中的元素數量時,多出的部分元素被移除。

一些內建方法

原素組會改變的方法:

  • push————在數組末端添加一個新元素,並返回改變後數組的長度
  • pop————移除數組末端的一個元素,返回該元素
  • unshift————在數組頭部添加若干元素,返回數組長度
  • shift————移除數組頭部的一個元素,返回該移除的元素
  • sort————排序,返回排序後的數組。(原數組也會變)
  • reverse————數組元素順序翻轉過來
  • indexOf————獲取數組中某元素的索引(若爲引用類型,必須爲同一份引用)
  • splice————萬能改變數組的方法,截取數組同時填充若干元素,返回截取後的數組。

    let a = [1,3,5,7,9];
    let b = a.splice(1,2,100,101,102);//第二個參數爲截取長度,與slice的第二個參數不一樣
    console.log(a);//[1,100,101,102,7,9]
    console.log(b);//[3,5]

原數組不受影響,返回一個改變後的數組的方法:

  • join————用某個字符串鏈接數組中的每一個元素。(split爲把字符串切割成數組)
  • slice————截取數組,兩參數爲截取起止索引,包括開始,不包括結束索引
  • concat————合併兩個數組返回

Function

函數對象的內建屬性:
  • constructor————繼承自Object(由於js中函數也是一種對象),其引用Function()這個構造函數
  • length————記錄該函數聲明時的參數數量
  • prototype
    • 每一個函數的prototype屬性都指向一個對象
    • 只有在函數是構造函數是才發揮做用
    • 該函數建立的全部對象都會持有一個該prototype 屬性的引用,並能夠將其當作自身的屬性來使用。
call和apply

call和apply能夠指定方法的執行上下文,從而能夠實現一個對象去"借用"另外一個對象的方法爲己所用。
例如,arguments對象沒有數組的內建方法,能夠像以下方式調用

function f(){
  let args = [].slice.call(arguments);
}

經過該方法能夠實現子類對象中調用父類對象中的方法
例如:Array繼承自Object,Array中重寫了Object中的toString方法,但在Array中想調用Object中的方法時能夠Object.prototype.toString.call([])

apply和call的惟一區別是,apply傳參放在一個數組裏。

Boolean

做用:

  1. 看成構造函數時,生成一個bool類型的對象(typeof爲'object'),通常不這麼用,直接用字面量false或true。
  2. 當普通函數調用,用於把其餘類型的值轉換爲bool值,且返回的值typeof爲boolean。

其餘幾種基本數據類型也有以上功能

五個基本類型數據都有一個對應的Object類型封裝。
('abcdefg').slice(0,2)會發生裝箱拆箱。
由於slice方法在String類型對象上,因此會先把值類型轉換爲引用類型,獲得結果後再轉換回值類型。

Number

做用與Boolean相同,但增長了一些屬性和方法。

把Number當成一個對象,該對象裏有如下屬性:

  • Number.MAX_VALUE————最大值
  • Number.MIN_VALUE————最小值
  • Number.POSITIVE_INFINITY————正無窮
  • Number.NEGATIVE_INFINITY————負無窮
  • Number.NaN————NaN

把Number當成構造函數,該構造函數prototype指向對象上有如下方法:

  • toFixed————保留小數位
  • toPrecision————把數字格式化爲指定的長度
  • toExponential————轉化位指數寫法
  • toString————重寫了object的toString,有一個可選的radix參數(默認10)

    (255).toString(16);//'ff'

String

  • toUpperCase————轉大寫
  • toLowerCasse————轉小寫
  • charAt————返回指定位置的字符,位置不存在時返回空字符串
  • indexOf————返回第一個匹配的位置
  • lastIndexOf————返回最後一個匹配的位置
  • slice————根據起止索引截取部分字符串,不包括截止索引,若截止索引爲負值,則至關於與字符串長度相加後的值
  • subString————與slice惟一區別是對截止索引爲負值時,subString會看成0處理
  • split—————根據所傳遞的分割字符串,將目標字符串分割成一個數組
  • concat————鏈接兩個字符串返回

Math

Math用法與上面不一樣,不能new。

經常使用屬性:

  • Math.PI————經常使用數字π
  • Math.E————歐拉常數e
  • Math.SQRT2————2的平方根
  • Math.LN2————2的天然對數
  • Math.LN10————10的天然對數

經常使用方法:

  • Math.random————獲取0到1之間的某個數
  • Math.floor————向下取整
  • Math.ceil————向上取整
  • Math.round————四捨五入
  • Math.max————獲取最大值
  • Math.min————獲取最小值
  • Math.pow————指數運算
  • Math.sqrt————求平方根
  • Math.sin、Math.cos————正弦、餘弦

Date

用於建立Date對象的構造器函數,能夠傳遞如下幾種參數

  • 無參數,默認返回當前時間
  • 一個用於表現日期的字符串
  • 分開傳遞年、日、月、時間等值
  • 一個timestamp值

注意:js中的月份是從0開始,0表示一月,1表示二月...

經常使用方法

var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份範圍是0~11,5表示六月
now.getDate(); // 24, 表示24號
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小時制
now.getMinutes(); // 49, 分鐘
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒數
now.getTime(); // 1435146562875, 以number形式表示的時間戳

同時有相應的set方法設置值。
注意:getDay表示獲取星期,getDate才表示獲取日

Date.now();//返回當前時間的時間戳
Date.parse('Jan 11, 2018');//返回時間戳
Date.UTC(2018,0,11);//返回時間戳

RegExp

寫法:

  1. let re = new RegExp('j.*t','gmi')
  2. let re = /j.*t/ig

組成部分:

  1. 一個用於匹配的模式文本,如/j.*t/
  2. 0或多個修飾符,如i、g、m

修飾符:

  • i————ignoreCase,忽略大小寫
  • g————global,找出全部的匹配
  • m————跨行搜索

RegExp對象的方法:

  • test————返回一個布爾值
  • exec————返回一個匹配到的數組,若加g修飾符,數組可能會有多個元素,不加則最多隻有一個
/j.*t/.test("Javascript");//false
/j.*t/i.test("Javascript");//true
/j.*t/i.exec("Javascript")[0];//Javascript

字符串中使用正則的方法:

  • match————返回包含匹配內容的數組
  • search————返回第一個匹配內容所在位置
  • replace————將匹配的文本替換爲指定的字符串
  • split————根據指定的正則表達式將字符串分割成若干個數組元素
let s = 'HelloJavaScriptWorld';
s.match(/a/);//返回["a"],沒有加g修飾符,故匹配到第一個就返回
s.match(/a/g);//返回["a","a"],加g後匹配全部
s.search(/j.*a/i);//返回5
replace
s.replace(/[A-z]/,'');//返回elloJavaScriptWorld,替換首個大寫字母爲空
s.replace(/[A-Z]/g,'');//返回elloavacriptorld,替換全部大寫字母爲空
s.replace(/[A-Z]/g,"_$&");//返回_Hello_Java_Script_World,用$&代替所匹配的文本
s.replace(/([A-Z])/g, "_$1");//返回_Hello_Java_Script_World,若正則表達式中分了組(即帶括號),那麼能夠用$1表示分組中的第一組,$2表示第二組,依此類推

注意:replace中第一個參數不是正則而是字符串時,只會替換掉第一個。這是與其餘語言不一樣的。

"pool".replace('o','*');//返回"p*ol"
"pool".replace('/o/g','*');//返回"p**l"
回調式替換
function replaceCallback(match){
  return ""+match.toLowerCase();
}

s.replace(/[A-Z]/g,replaceCallback);//輸出_hello_java_script_world

該回調函數接受一系列參數(以上示例僅用到了第一個參數)

  • 首參數是所匹配的內容
  • 尾參數是被搜索的字符串
  • 倒數第二個參數是匹配內容所在位置
  • 剩下的參數是分組所匹配的字符串

Error對象

包括一些派生的ReferenceError、RangeError、EvalError、SyntaxError、TypeError、URIError。

Error類對象都有兩個屬性:

  • name————構造當前Error對象的構造器名稱
  • message————出錯的詳細信息描述
try {
  var total = maybeExists();
  if (total === 0) {
    throw new Error('Division by zero!');
  } else {
    alert(50 / total);
  }
} catch (e){
  alert(e.name + ': ' + e.message);
} finally {
  alert('Finally!');
}

IE瀏覽器跟Chrome、FireFox拋出異常的name和message不一樣,能夠自定義拋出一個匿名對象。

throw {
  name: "MyError",
  message: "OMG! Something terrible has happened"
}

第五章 原型

https://github.com/LeesonVictor/notebook/blob/master/images/%E5%9B%BE%E7%89%87/%E5%8E%9F%E5%9E%8B.png?raw=true

js中的繼承就是基於原型的。

原型屬性

函數也是對象,每一個函數上都有一個prototype屬性。

function foo(a,b){
  return a*b;
}

typeof foo.prototype;//輸出"object"

函數的原型屬性只有在函數看成構造函數使用(即便用new調用)時才起做用。

原型對象上的屬性和方法

  • 原型對象上的屬性和方法不管實例化多少個對象都只存在一份。
  • 若是實例屬性名和原型對象上的屬性名相同(或方法名相同,由於js中不存在方法重載),則實例屬性優先級高。
  • 原型對象上的方法也能夠經過this.屬性名訪問實例屬性值和原型對象上的屬性。

原型鏈

每一個對象都會有一個構造器,而原型自己也是一個對象,這意味着它必然也有一個構造器,而這個構造器又會有本身的原型。因而這種結構可能會一直不斷地持續下去,並最終造成原型鏈。

枚舉屬性

  • 內建對象.propertyIsEnumerable('屬性名||方法名||原型鏈上的屬性名||原型鏈上的方法名')返回false。
  • 對象.propertyIsEnumerable('原型鏈上的屬性名||原型鏈上的方法名')返回false。
  • for in 會例舉出實例對象和其原型鏈上的屬性或方法。但不會例舉內建對象的原型屬性。如Object的原型對象的屬性和方法。

isPrototypeOf()

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

let monkey = {
  hair:true,
  feeds:'bananas',
  breathes:'air'
};

function Human(name){
  this.name = name;
}
Human.prototype = monkey;

let george = new Human('George');
monkey.isPrototypeOf(george);//返回true

獲取一個對象的原型對象

Object.getPrototypeOf(george);//返回monkey對象
george.constructor.prototype;//這樣也能夠獲取

__proto__

生成一個對象時,js引擎自動在對象上加了一個__proto__屬性,指向該對象的原型對象。而後原型對象也是對象,也有一個__proto__屬性指向一個原型對象...如此下去,便造成了一條原型鏈。

__proto__只能在學習或調試環境下使用

注意:以上爲對象層面的原型鏈。new一個對象時,js引擎把構造函數、原型對象層面的鏈狀關係轉化爲對象層面的原型鏈。構造原型對象和構造函數之間的鏈狀關係纔是咱們所需編寫的代碼。

原型做用

  1. 實現繼承
  2. 擴展內建對象
//擴展Date,加一個format方法

Date.prototype.format = function (fmt) {
            var o = {
                "M+": this.getMonth() + 1,                 //月份
                "d+": this.getDate(),                    //日
                "h+": this.getHours(),                   //小時
                "m+": this.getMinutes(),                 //分
                "s+": this.getSeconds(),                 //秒
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                "S": this.getMilliseconds()             //毫秒
            };
            if (/(y+)/.test(fmt))
                fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
                if (new RegExp("(" + k + ")").test(fmt))
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        };

原型陷阱

  • 對原型對象執行徹底替換時,可能會出發原型鏈中的某種異常。
    1. 在對象生成後才徹底替換原型對象。
      此種狀況下,先前生成的對象的__proto__仍是連接着原先的原型對象。
    2. 徹底替換原型對象後生成對象。
      此種狀況下,__proto__連接着被替換掉的原型對象。
  • prototype.constructor屬性是不可靠的。
function Dog(){
    this.tail = true;
  }
  let benji = new Dog();
  Dog.prototype.say = function(){
    return 'Woof!';
  }
  benji.say();//對象生成後,再在原型對象上加方法,依然能夠調用該方法
  beiji.constructor === Dog;//返回true

  Dog.prototype = { //此處徹底替換掉了原來的原型對象
    paws:4,
    hair:true
  }

  typeof benji.paws;//返回'undefined'

  beiji.say();//返回'Woof!',由於beiji的__proto__屬性還連接着原來的原型對象

  let lucy = new Dog();//在徹底替換掉原型對象後,再實例化對象
  
  lucy.say();//TypeError:lucy.say is not a function
  lucy.paws;//4
  lucy.constructor;//function Object(){[naticve code]},替換掉原型對象後,該原型對象的constructor屬性就不指向Dog了

最佳實踐:當咱們重寫對象的prototype時,須要重置相應的constructor屬性。

第六章 繼承

js引擎所作的:查找對象屬性時,先在對象自身屬性中查找,再沿着__proto__鏈着的對象上查找。
咱們所需作的:經過構造函數和原型對象構建連接關係。(new一個對象時,js引擎把轉化爲經過__proto__把對象之間鏈接起來)

實現方法1

Child.prototype = new Parent();
Child.prototype.constructor = Child;//重置原型對象上的constructor屬性,否則就指向了Parent

缺點:該方法不只繼承了父類的實例屬性,還繼承了父類的原型屬性

實現方法2

function extend(Child,Parent){
  let F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
}

該方法只會繼承父類的原型屬性,不會繼承父類的實例屬性。

ES6中繼承寫法

ES6中引入了class關鍵字,但並不是js中有了類,只是一個實現繼承的語法糖,實際仍是經過原型實現繼承。

class Student {
    constructor(name) {
        this.name = name;//實例屬性、方法放在constructor中
    }

    hello() { //原型屬性、方法放在外面
        alert('Hello, ' + this.name + '!');
    }
}

class PrimaryStudent extends Student { //extends關鍵字表明繼承自誰
    constructor(name, grade) {
        super(name); // 記得用super調用父類的構造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

第七章 瀏覽器環境

運行javascript代碼須要宿主環境,通常是瀏覽器環境。

BOM(瀏覽器對象模型)

BOM(瀏覽器對象模型)是一個用於訪問瀏覽器計算機屏幕的對象集合。能夠經過window訪問這些對象。

window對象

window對象是瀏覽器中的全局對象,全部的全局變量和函數均可以經過window對象的屬性訪問。

window.navigator

navigator是一個反應瀏覽器及其功能信息的對象。如navigator.userAgent能夠識別不一樣的瀏覽器。

window.location

location是一個用於存儲當前載入頁面url信息的對象。

屬性:

//若果當前頁面的url爲http://search.phpied.com:8080/search?p=java&what=script#results

for(var i in location) {
  if(typeof location[i] === 「string」) {
    console.log(i + ' = "' + location[i] + '"');
  }
}
//輸出:
//href = "http://search.phpied.com:8080/search?q=java&what=script#results"
//hash = "#results"
//host = "search.phpied.com:8080"
//hostname = "search.phpied.com"
//pathname = "/search"
//port = "8080"
//protocol = "http:"
//search = "?q=java&what=script"

注:location的href中除了hash變化不會引發向服務端發起請求外,其餘部分變化都會從新向服務端發起請求。

方法:

  • reload()————從新加載當前頁面
  • assign()————導航到其餘頁面
  • replace()————也是導航到其餘頁面,可是不會在瀏覽器的歷史記錄表中留下記錄
window.history

window.histoty屬性容許咱們以有限的權限操做同一個瀏覽器會話中的已訪問頁面。

  • history.length————查看用戶在這以前訪問了多少頁面
  • histoty.forward()————前進
  • history.back()————後退
  • history.go(-1)————後退一步,同理能夠傳其餘正、負、0數,實現前進、後退、重載當前頁面
H5中history的API

做用:讓無跳轉的單站點也能夠將它的各個狀態保存爲瀏覽器的多條歷史記錄。當經過歷史記錄從新加載站點時,站點能夠直接加載到對應的狀態。

API:

  • history.pushState()
    完整是history.pushState(stateObject, title, url),包括三個參數。
    第一個爲狀態參數,存放須要記錄的狀態;第二個是標題,目前瀏覽器忽略它,傳空字符串;第三個爲當前狀態對應的url地址。
  • history.repalceState()
    與pushState惟一區別是不會新生成歷史記錄,而是將當前歷史記錄替換掉。
  • window.onpopstate
    頁面前進後退時,若當前url有對應的stateObject則觸發事件,並在參數中包含stateObject。

    //假如當前網頁地址爲http://example.com/example.html,則運行下述代碼後:
    window.onpopstate = function(event) {
      alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
    };
    //綁定事件處理函數
    history.pushState({page: 1}, "title 1", "?page=1");    //添加並激活一個歷史記錄條目 http://example.com/example.html?page=1,條目索引爲1
    history.pushState({page: 2}, "title 2", "?page=2");    //添加並激活一個歷史記錄條目 http://example.com/example.html?page=2,條目索引爲2
    history.replaceState({page: 3}, "title 3", "?page=3"); //修改當前激活的歷史記錄條目 http://ex..?page=2 變爲 http://ex..?page=3,條目索引爲3
    history.back(); // 彈出 "location: http://example.com/example.html?page=1, state: {"page":1}"
    history.back(); // 彈出 "location: http://example.com/example.html, state: null
    history.go(2);  // 彈出 "location: http://example.com/example.html?page=3, state: {"page":3}
window.frames

window.frames屬性是當前頁面中全部框架的集合。
frames中的每一個元素都包含了一個頁面,都有各自的window全局對象。

window.screen

screen所提供的是瀏覽器之外的環境信息。

  • screen.colorDepth————當前顯示器的色位
  • screen.width————屏幕寬
  • screen.height————屏幕高
  • screen.availWith————屏幕可用寬(與width不一樣之處是availWith不包括任務欄)
  • screen.availHeight————屏幕可用高(不包括任務欄)
window.open/window.close

打開窗口、關閉窗口

window.moveTo()、window.moveBy()、window.resizeTo()、window.resizeBy()

:Chrome、edge中測試均無反應,已廢棄。

window.alert()、window.confirm()、window.prompt()
  • window.alert()————彈出提示框
  • window.confirm()————彈出肯定框,能夠選擇肯定或取消,相應返回true或false
  • window.prompt()————彈出帶輸入框的窗口,輸入後點肯定,返回所輸入的文本
window.setTimeout()、window.setInterval()

setTimeout:在必定時間後執行

function boo(){
  console.log('boo!');
}

let id = setTimeout(boo,2000);//2秒後執行boo方法
clearTimeout(id);//根據id取消計時器

setInterval:每隔多少毫秒執行一次

let id = setInterval(boo,2000);//每隔2秒執行一次
clearInterval(id);//根據id取消計時器
window.document

DOM(文檔對象模型)

DOM是一種將XML或HTML文檔解析成樹形節點的方法。經過DOM的方法與屬性,咱們能夠訪問到頁面中的任何元素,並進行元素的修改刪除及添加操做。

基於DOM Level1用於解析全部XML文檔的那部分稱爲Core DOM,在Core DOM上進行擴展的那部分稱爲HTML DOM。

https://github.com/LeesonVictor/notebook/blob/master/images/%E5%9B%BE%E7%89%87/Core&HTML_DOM.png?raw=true

DOM節點的訪問

方法一 索引訪問法
  • nodeType————節點類型,經常使用的1(元素)、2(屬性)、3(文本)...
  • nodeName————節點名稱,對HTML標籤來講,通常就是標籤名(即tagName屬性),對文本則是#text
  • nodeValue————節點值,對文本節點來講,值就是實際文本。

document表示當前所訪問的文檔。
document.documentElement表示document上的HTML節點。

document.documentElement(即HTML節點)有三個,即head元素、body元素,以及二者之間的空白(空白默認爲文本節點)。

  • 節點相關:
    • hasChildNodes()————判斷某節點是否包含子節點
    • childNodes————NodeList類型的子節點集合,經過索引能夠訪問到子節點
    • parentNode————某節點的父節點
  • 屬性相關:
    • hasAttributes()————檢查元素是否存在屬性
    • attributes————屬性集合,經過索引訪問
    • getAttribute()————獲取對應屬性名的值
    • 直接在節點後面.屬性名,如節點對象.id訪問id屬性的值,節點對象.className訪問class屬性的值
  • 節點中的內容相關:
    • textContent————節點中的文本
    • innerHTML————節點中的html的字符串

若一段HTML結構以下:

<html>
  <head></head>
  <!--這中間雖然什麼都沒有,但其實仍是有一個text節點-->
  <body>
    <p class="opener">first paragraph</p>
    <p><em>second</em> paragraph</p>
    <p id="closer">final</p>
    <!-- and that's about it -->
  </body>
</html>
document.documentElement.childNodes[1];//#text,由於head和body之間存在一個空白,該空白爲text節點
document.documentElement.childNodes[1].parentNode;//<html>...</html>
let bd = document.documentElement.childNodes[2];
bd.childNodes.length;//9,由於四個節點(包括註釋)之間、子節點和父節點之間一共存在5個空白的text節點,因此一共9個節點

bd.childNodes[1].hasAttributes();//true,第一個標籤有個class屬性。注意索引0是空白text,索引1纔是p標籤
bd.childNodes[1].attributes.length;//1
bd.childNodes[1].attributes[0].nodeName;//'class'
bd.childNodes[1].attributes[0].nodeValue;//'opener'
bd.childNodes[1].attributes['class'].nodeValue;//'opener'
bd.childNodes[1].getAttribute('class');//'opener'
bd.childNodes[1].className;//'opener'
bd.childNodes[1].nodeName;//'p'
bd.childNodes[1].textContent;//'first paragraph'
bd.childNodes[1].innerHTML;//'first paragraph'
bd.childNodes[3].textContent;//'second paragraph'
bd.childNodes[3].innerHTML;//'<em>second</em> paragraph'
bd.childNodes[1].childNodes[0].nodeValue;//'first paragraph'
方法二 快捷訪問法

索引法的問題:

  • 訪問的節點層次很深時,須要寫很長的代碼
  • 節點之間存在空白text節點,致使計算索引很麻煩

快捷方法:

  • getElement系列:
    • getElementsByTagName()————以標籤名爲參數,返回當前html頁面中全部匹配該標籤名的元素集合
    • getElementsByClassName()————以類名爲參數,返回全部該類名的元素集合
    • getElementById()————以id爲參數返回一個element
    • getElementsByName()————以name屬性爲參數,返回全部該name屬性的元素集合
  • querySelector系列
    • querySlector()————經過css選擇器返回第一個元素
    • querySelectorAll————經過css先選擇器返回全部元素

注:getElement系列除了getElementById返回一個Element外,其餘都返回一個HTMLCollection集合,HTMLCollection是動態的,會隨着文檔樹的變化動態更新。
querySelector系列返回一個NodeList,NodeList是靜態的,獲取後,文檔樹變化不會影響NodeList.

document.getElementsByTagName('p').length;//3
document.getElementsByTagName('p')[0];//<p class="opener">first paragraph</p>
document.getElementsByTagName('p')[0].className;//'opener'
document.querySlector('p').className;//'opener'
document.querySlectorAll('p')[0].className;//'opener'

body雖說嵌套在html標籤裏,但document.body就能夠訪問到body,而不用document.documentElement.body。

  • nextSibling————下一個相鄰節點
  • previousSibling————上一個相鄰節點
  • firstChild————第一個子節點,通常爲空白text
  • lastChild————最後一個子節點,通常爲空白text

DOM節點的修改

  • innerHTML————修改元素裏的HTML
  • nodeValue————修改元素的內容,text類型元素則爲文本值
  • style————修改元素的樣式
let my = document.getElementById(,'closer');
my.innerHTML = 'final!!!';
my.innerHTML = '<em>my</em> final';
my.firstChild;//<em>my</em>
my.firstChild.firstChild;//'my'
my.firstChild.firstChild.nodeValue = 'your';

my.style.border = '1px solid red';
my.style.cssText += 'font-weight:bold;';//直接在原有cssText屬性上加css字符串文本

DOM節點的新建

  • createElement()————新建元素
  • createTextNode()————新建文本節點
  • cloneNode()————拷貝節點,參數傳false則爲淺拷貝,傳true則爲深拷貝
  • appendChild()————添加到最後
  • insertBefore()————插入到某元素前面
  • replaceChild()————替換某元素

注意:後三個方法調用方是父容器節點。

let p = document.createElement('p');
p.innerHTML = '<em>yet</em> another';
p.style.border = '2px dotted blue';
document.body.appendChild(p);
let list=document.getElementById("myList")
list.insertBefore(p,list.childNodes[0]);//第一個參數爲新節點,第二個參數爲插入誰的前面

item.replaceChild(p,list.childNodes[0]);//第一個參數爲替換者,第二個爲被替換者

document.body.appendChild(p.cloneNode(false));//將只會拷貝一個p標籤,至關於document.body.appendChild(document.createElement('p'));
document.body.appendChild(p.cloneNode(true));//p及p標籤裏的子元素都將拷貝

DOM節點的移除

  • removeChild()————移除節點
  • replaceChild()————替換節點
  • innerHTML='' ————把innerHTML置爲空字符串
let p = document.getElementsByTagName('p')[1];
let removed = document.body.removeChild(p);

let p1 = document.getElementsByTagName('p')[1];
let replaced = document.body.replaceChild(removed,p1);

document.body.innerHTML = '';//清空body裏的全部HTML

只適用於HTML的DOM對象

以上總結的都是屬於DOM Level 0(或叫Core DOM),既適用於XML又適用於HTML。如下的只適用於HTML。

  • document.body————HTML中的body元素
  • document.images————當前頁中全部圖片的集合,等價於Core DOM中的document.getElementsByTagName('img')
  • document.links————包含全部<a href='...'/>的集合
  • document.anchors—————包含全部<a name='...'>的集合
  • document.forms————包含全部表單的集合

  • document.write()————在頁面載入時插入一些HTML元素,當載入完成後調用則會覆蓋整個HTML。通常沒什麼用。

  • document.cookie————獲取或設置cookie
  • document.title————獲取或設置title
  • document.referrer————記錄前面訪問的頁面的URL,同HTTP 頭信息中的Referer
  • document.domain————獲取或設置當前域名,注意設置只能比真實域名更簡短,如www.yahoo.com改成yahoo.com
  • document.location————同window.location

事件

註冊事件三種方式

1.內聯HTML屬性法

<div onclick="alert('Ouch!')">click me</div>

缺點:Javascript代碼和HTML耦合在一塊兒。

2.元素屬性法

<div id="my-div">click</div>
let myelement = document.getElementById('my-div');
myelement.onclick = function() {
alert('Ouch!');
}

缺點:只能指定一個事件函數。

3.事件監聽器法

<p id="closer">final</p>
let mypara = document.getElementById('closer');
mypara.addEventListener('click', function(){alert('Boo!')}, false);
mypara.addEventListener('click', console.log.bind(console), false);

移除某個監聽器:

function func(){
  alert('Woo');
}
mypara.addEventListener('click', func, false);
mypara.removeEventListener('click', func, false);

注意:移除某個監聽器時,傳遞的方法參數必須是同一個方法的引用,即便寫一個徹底相同的方法爺不行。故須要移除的監聽器在註冊時不能用匿名方法。

捕捉法和冒泡法

addEventListner方法的第三個參數,當置爲true時爲捕捉法,默認爲false即冒泡法。

<body>
  <ul>
    <li><a href="http://phpied.com">my blog</a></li>
  </ul>
</body>

單擊連接

  • 事件捕捉————單機首先發生在document上,而後依次傳遞給body、列表、列表項,並最終到達該連接,稱爲捕捉法。
  • 事件冒泡————單機首先發生在連接上,而後逐層向上冒泡,直到document對象,稱爲冒泡法

按照DOM2的規定,事件傳播分三階段:先捕捉標籤,而後到達標籤,再冒泡。

阻斷冒泡

在最裏層的處理器中加e.stopPropagation(),就不會觸發上層父容器的事件。

function paraHandler(e){
alert('clicked paragraph');
e.stopPropagation();
}

防止默認行爲

在瀏覽器中某些元素的事件有一些預約義行爲,例如,單機連接會載入另外一個頁面。能夠在事件處理器中加e.preventDefault()來阻斷默認行爲。

// 在點擊全部連接前詢問是否導航至目標連接,若選擇否,則不導航
let all_links = document.getElementsByTagName('a');
for (let i = 0; i < all_links.length; i++) {
  all_links[i].addEventListener(
    'click',
    function(e){
      if (!confirm('Are you sure you want to follow this link?')){
        e.preventDefault();
      }
    },
    false // don't use capturing
  );
}

事件類型

  • 鼠標類事件
    • 鼠標鍵的按下、鬆開、單擊、雙擊
    • 鼠標的懸停、移出、拖動
  • 鍵盤類事件
    • 鍵盤鍵的按下、輸入、鬆開
  • 載入/窗口類事件
    • 載入(圖片、頁面或其餘組件完成載入操做)、卸載(指用戶離開當前頁面)、卸載以前(由腳本提供的、容許用戶終止卸載的選項)
    • 停止(指用戶在IE 中中止頁面或圖片載入)、錯誤(指在IE 發生了JavaScript錯誤或圖片載入失敗)。
    • 調整大小(指瀏覽器窗口大小被重置)、滾動(指頁面進行了滾動操做)、上下文菜單(即右鍵菜單出現)。
  • 表單類事件
    • 得到焦點(指某字段得到輸入)、失去焦點(指離開該字段)。
    • 改變(指改變某字段的值後離開)、選中(指某文本字段中的文本被選中)。
    • 重置(指擦除用戶輸入的全部信息)、提交(指發送表單)。

現代瀏覽器還有dragstart、dragend、drop事件,觸控設備還有touchstart、touchmove、touchend事件。

XMLHttpRequest對象

let xhr = new XMLHttpRequest();
xhr.onreadystatechange = myCallback;
xhr.open('GET','somefile.text',true);
xhr.send('');//若要攜帶參數則以格式'key=value&key2=value2'

fuction myCallback(){
  if(xhr.readyState<4){
    return;
  }
  if(xhr.status!==200){
    alert('Error!');
    return;
  }
  alert(xhr.responseText);
}
相關文章
相關標籤/搜索