JavaScript知識點總結(二)

變量, 做用域, 垃圾收集(內存問題)

基本類型和引用類型

ES中的變量包含基本類型值引用類型值javascript

基本類型值指的是簡單的數據段前端

引用類型值值那些可能有多個值構成的對象java

五種基本數據類型(Undefined, Null, Boolean, Number, String)的值即基本類型值是按訪問的, 所以操做的是保存在變量中實際的值正則表達式

引用類型值是保存在內存中的對象,ES不容許直接訪問內存中的位置, 即不能直接操做對象的內存空間. 在操做對象時, 其實是在操做對象的引用而不是實際的對象. express

當複製保存在對象中的某個變量時, 操做的是對象的引用. 但在爲對象添加屬性時, 操做的是實際的對象.數組

動態的屬性

定義基本類型的值和引用類型的值方式是基本同樣的, 就是建立一個變量, 而後爲該變量賦值.瀏覽器

建立變量之後,基本類型的值和引用類型的值執行的操做有很大不一樣.數據結構

//引用類型能夠爲其添加或刪除屬性和方法
var p = new Object();
p.name = "Jon";
console.log(p.name); //Jon
delete p.name;
console.log(p.name); //undefined
//基本類型不能添加屬性, 由於即便添加了也不能訪問
var n = "Jon"; //一個string字符串類型
n.age = 25;
console.log(n.age); //undefined

複製變量值

複製基本類型值的變量值與複製引用類型值的變量值也存在不一樣.app

複製基本類型值時, 是直接建立新值, 佔據不一樣的內存(棧)空間, 複製以後兩個變量能夠參與任何操做而不互相影響函數

var n1 = 5;
var n2 = n1; //複製n1

複製引用類型值時, 一樣也會把儲存在變量對象中的值複製一份到新變量分配的空間中, 但這個新變量的值實際上是一個指針, 指向儲存在堆中的一個對象. 因此二者引用的是同一個對象. 因此, 改變其中一個變量, 另外一個變量也會受到影響.

var o1 = new Object();
var o2 = o1;
o1.name = 'Jon';
console.log(o2.name); //o1和o2指向的是堆內存中的同一個對象, 因此一樣會輸出Jon

參數傳遞

ES中全部函數的參數都是按傳遞的,不存在引用傳遞的參數

函數外部的值複製給函數內部的參數, 就等於把值從一個變量複製到另外一個變量同樣.

基本類型值的傳遞就像基本類型變量的複製同樣, 向參數傳遞基本類型值的時候,被傳遞的值會被複制給一個局部變量(即命名參數, 用ES的概念來講, 就是arguments中的一個元素)

引用類型值的傳遞就像引用類型變量的複製同樣, 向參數傳遞引用類型值的時候, 會把這個值在內存中的地址複製給一個變量, 所以這個局部變量的變化會反映在函數的外部

function addTen(n){ //參數(這裏是n)實際上是函數的局部變量
  n += 10;
  return n;
}
var c = 20;
var r = addTen(c); //調用時,c做爲一個局部變量傳遞給n,函數體內又會自増10而後返回
console.log(c); //外部的c變量不會被影響,仍是20
console.log(r); //30
function setName(obj){
  obj.name = "Jon";
}
var p = new Object(); //建立了一個對象並保存在變量p中
setName(p); //隨即被傳遞到setName()中,p複製給了obj
console.log(p.name); //因此obj的屬性name也能被p訪問到,因此這裏輸出Jon
//證實對象是按值傳遞的例子
function setName(obj){
  obj.name = 'Jon';
  obj = new Object(); //爲obj從新定義了一個對象
  obj.name = 'Percy'; //而後爲obj定義了另外一個name屬性
}//若是p是引用傳遞的話, 那麼p就會自動被修改成指向其name屬性值爲Percy的對象,但下面的例子輸出的仍然是Jon. 說明即便函數內部修改了參數的值, 但原始的引用仍然保持不變.
var p = new Object();
setName(p);
console.log(p.name); //Jon

能夠吧ES函數的參數想象成局部變量.

檢測類型

typeof — 檢測變量是哪一種基本數據類型.string, number, boolean, undefined, object(若是變量的值是一個對象或null, 則返回object)

console.log(typeof "Jon"); //string
console.log(typeof true); //boolean
console.log(typeof 1); //number
var a;
console.log(typeof a); //undefined
console.log(typeof null); //object
var o = new Object();
console.log(typeof o); //object

instanceof — 檢測引用數據類型值時, 檢測其引用數據類型值是什麼類型的對象

result = variable instanceof constructor

若是變量是給定引用類型的實例, 那麼instanceof操做符就會返回true

console.log(person instanceof Object); //變量person是Object嗎?
console.log(colors instanceof Array); //變量colors是Array嗎?
console.log(pattern instanceof RegExp); //變量pattern是RegExp嗎?

全部引用類型的值都是Object的實例, 因此檢測一個引用類型的值和Object構造函數時會始終返回true

使用instanceof操做符檢測基本類型的值會始終返回false, 由於基本類型不是對象

執行環境, 做用域

  • 執行環境定義了變量或函數是否有權訪問的其餘數據.

  • 全局執行環境是最外圍的執行環境(Web瀏覽器中指的是window對象),所以因此全局變量函數都是做爲window對象的屬性和方法建立的.

  • 每一個函數都有本身的執行環境, 當執行流進入一個函數時, 函數的環境就會被推入一個環境棧中, 函數執行以後, 棧將其環境彈出, 把控制權返回給以前的執行環境.

  • 代碼在一個環境中執行時, 會建立變量對象的一個做用域鏈, 它保證了對執行環境有權訪問的全部變量和函數的有序訪問.

  • 全局執行環境的變量對象始終都是做用域鏈中的最後一個對象

var color = "blue";
function changeColor(){
  if(color === "blue"){
      color = "red";
  }else{
      color = "blue";
  }
}
changeColor();
console.log("Now color is : " + color);
//Now color is : red
//函數changeColor()的做用域鏈包含兩個對象,它本身的變量對象和全局環境的變量對象.
//內部環境能夠經過做用域鏈訪問全部的外部環境, 但外部環境相反不能訪問內部環境的任何變量和函數
var color = "blue";
        
function changeColor(){
  var anotherColor = "red";

  function swapColors(){
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
    //這裏能夠訪問color, anotherColor, tempColor
  }
  swapColors();
  //這裏只能夠訪問color, anotherColor
}
changeColor();
////這裏只能夠訪問color
alert("Color is now " + color);

沒有塊級做用域

if(true){
  var color = "blue";
}
console.log(color); //ES中, 在外部依然能訪問塊級做用域內的變量和函數
for (var i=0; i < 10; i++){
        doSomething(i);
}
alert(i);      //能夠訪問塊級做用域內的變量,輸出10
//ES中查詢標識符會從正在執行的局部環境查找, 若是當前局部環境查找不到, 就會沿着做用域鏈一級一級向上查找. 若是在全局環境都找不到須要查找的標識符, 說明該變量未聲明
var color = "blue";
function getColor(){
  return color;
}
console.log(getColor()); //這裏會先搜索getColor()內部有沒有color變量, 若是沒有就向上一級查找, 直到查找到位置, 這裏在上一級已經找到, 因此會輸出blue
var color = "blue";
function getColor(){
  var color = "red";
  return color;
}
console.log(getColor()); //red , 同級找到就不會再向上查找

垃圾收集

標記清除

ES中, 當變量進入環境(例如, 在函數中聲明一個變量時), 就把這個變量標記爲"進入環境", 這種進入環境的變量從邏輯上講不能釋放其內存, 由於有可能用到它們. 而當變量離開環境時, 就將其標記爲"離開環境".

過程 :

  • 垃圾收集器運行的時候會給儲存在內存中的全部變量都加上標記(可使用任意可以使用的標記方式)

  • 接着會去掉環境中的變量, 以及被環境中的變量引用的變量的標記(我的理解就是當前執行環境的變量以及被環境中變量引用的變量 的標記)

  • 在此以後再被加上標記的變量, 就是被視爲準備刪除的變量(由於它們以前用的時候已經被標記一次了, 再次(第二次)標記說明已經使用完畢), 環境中的變量已經沒法訪問這些變量了

  • 垃圾收集器完成內存清除工做, 銷燬那些帶標記的值並回收它們所佔用的內存空間

引用計數

引用計數的含義是跟蹤記錄每一個值被引用的次數

  • 聲明瞭一個變量並將一個引用類型值賦值給該變量時, 則這個值的引用次數就是1

  • 引用類型值又賦值給另外一個變量, 則引用次數加1

  • 相反, 若是包含對這個值的引用的變量(如a變量)又取得了另外一個引用類型值, 則前一個引用類型值的引用次數減1

  • 當這個引用類型值的引用次數變成0時, 則說明沒辦法再訪問這個值了, 於是能夠將其回收, 釋放內存空間

該垃圾收集機制早期的循環引用問題
function problem(){
  var oA = new Object();
  var oB = new Object();
  
  //oA與oB經過各自的屬性互相引用, 在標記清除的回收機制中, 它們的引用次數永遠不多是0, 而且若是這個函數重複屢次調用, 會致使大量內存得不到回收
  //因此這種方式已經被摒棄, 而採用標記清除來實現其垃圾回收
  oA.someOtherObject = oB;
  oB.anotherObject = oA;
}
IE中的BOM與DOM使用引用計數來做爲垃圾收集機制的問題
var element = document.getElementById("some_element");
var myObject = new Object();
//DOM元素(element)與一個原生JS對象(myObject)之間建立了循環引用
myObject.element = element; //myObject的element屬性指向element對象
element.someObject = myObject; //變量element也有一個屬性名叫someObject回指myObject
//基於上述問題, 即便將力爭中的DOM從頁面中移除, 它也永遠不會被回收

//解決方案是在他們不使用時手動斷開原生JS對象與DOM元素之間的連接
//把變量設置爲null意味着斷開變量與它以前引用的值之間的連接, 當下一次的垃圾回收執行時, 就會刪除這些值並回收他它們佔用的內存
myObject.element = null;
element.someObject = null;
//IE9以上已經把DOM和BOM轉換成了真正的JavaScript對象, 因此避免了上述問題

性能問題及內存管理

性能問題

早期的瀏覽器按內存分配量運行的, 達到一個臨界值就會觸發垃圾回收機制, 這個問題在於, 若是一個腳本中包含大量的變量, 那麼會在其生命週期也保持有那麼多變量, 致使長時間處於垃圾回收機制的臨界值, 從而使垃圾回收機制頻繁運行, 形成嚴重的性能問題.

新版本的瀏覽器已經將其垃圾回收機制的工做方式改變, 會動態的調整觸發的臨界值.

內存管理

優化內存佔用的方式, 就是爲執行中的代碼只保存必要的數據. 一旦數據再也不有用, 就經過將其值設置爲null來釋放引用 — 即解除引用

function createPerson(){
  var localPerson = new Object();
  localPerson.name = name;
  return localPerson;
}
var globalPerson = createPerson("Jon");

//手動解除globalPerson的引用
globalPerson = null;

引用類型

引用類型的值(對象)引用類型的一個實例.

ES中, 引用類型是一種數據結構, 用於將數據和功能組織在一塊兒.就像傳統的同樣.

對象是某個特定引用類型的實例

新對象使用new操做符後跟一個構造函數來建立的.

構造函數自己就是一個函數, 只不過該函數是出於建立新對象的目的而定義的.

var person = new Object(); //建立Object引用類型的一個新實例, 並把實例保存在person變量中, 併爲新對象定義了默認的屬性和方法

ES中提供了不少原生引用類型,用於平常的開發任務.

Object類型

//建立Object實例
var person = new Object();
person.name = "Jon";
person.age = 25;
person.sayName = function(){
  console.log("My name is " + name);
}

//使用 對象字面量 建立
var person = {
  name : "Jon",
  age : 25,
  sayName : function(){
  console.log("My name is " + name);
  }
};

//使用 對象字面量 時, 屬性名也能使用字符串
var person2 = {
  "name" : "Mark",
  "age" : 24,
  //...
}
var person = {
  name : "Jon",
  age : 25,
  sayName : function(){
  console.log("My name is " + name);
  }
};

//訪問對象屬性: 使用點表示法或者方括號表示法
console.log(person.name); //經常使用, Jon
console.log(person[name]); //不經常使用, 但若是屬性名包含特殊字符或者空格等, 可使用方括號表示法來訪問對象屬性

Array類型

ES的Array每一項均可以保存任何類型的數據.

ES的Array大小是能夠動態調整的, 便可以隨着數據的添加自動增加以容納新增數據.

數組的建立方式

var arr1 = new Array(); //建立數據的基本方式
var arr2 = new Array(10); //預先知道要保存的項目數量能夠直接建立特定長度的數組, 這裏建立了length爲10的數組
var arr3 = new Array("Jon","Mark","Martin"); //建立包含特定值的數組
var arr4 = Array(5); //建立數組也能夠省略new操做符
var arr5 = ["blue","yellow","green"]; //使用數組字面量 表示法來建立數組, 使用這種方法並不會調用Array構造函數

數組的讀取和設置

var arr1 = ["blue","yellow","green"];

//讀取
console.log(arr1[0]); //數組元素索引從0開始, 訪問每一個元素就是 數組名[索引號], 好比第1個就是arr1[0] , 因此這裏會輸出blue

//設置
arr1[1] = "red"; //把數組arr1的第二個元素值設置爲red;
console.log(arr[1]); //red
arr1[arr1.length] = "black"; //在數組的末尾添加一個元素
console.log(arr1); //["blue", "red", "green", "black"]

//訪問數組長度
console.log(arr1.length); //3
//數組長度屬性length屬性不是隻讀的..能夠經過設置其長度來改變數組的長度
arr1.length = 5;
console.log(arr1.length); //5
console.log(arr1[4]); //undefined

//喪心病狂地增長數組長度
arr1[99] = "purple"; //除了前面有效的值和這個新增有效的值, 其餘的都是undefined
console.log(arr1.length); //100

檢測對象是否爲數組 — instanceof 或者 ES5裏面新增的Array.isArray()

var arr = [];
if(Array.isArray(arr)){
  //do sth...
}

轉換方法

var arr = [1,2,3];
console.log(arr.toString()); //1,2,3(返回的是字符串形式拼接而成的用逗號分隔的字符串)
console.log(arr.valueOf()); //1,2,3(返回的是原來的數組)
console.log(arr); //1,2,3(與toString()同樣)

//**使用join()方法可使用不一樣的分隔符構建指定的數組**
console.log(arr.join("-")); //1-2-3

//若是數組中的值是null或undefined, 那麼該值在join(),toLocaleString(),toString(),valueOf()中返回的結果會以空字符串表示

數組的插入和刪除

棧方法

LIFO(Last-In-First-Out), 後進先出

push(), 接收任意參數並把它們逐個添加到數組末尾, 返回修改後數組的長度

pop(), 從數組末尾移除最後一項, 減小數組的length值, 而後返回移除的項

var colors = Array();
var count = colors.push("red","green"); //推入兩項
console.log(count); //2

count = colors.push("black"); //推入另外一項
console.log(count); //3

var item = colors.pop(); //取得最後一項
console.log(item); //black
console.log(colors.length); //2
//能夠跟其餘數組方法一塊兒使用
 var colors = ["red","blue"];
 colors.push("brown"); //添加一項
 colors[3] = "purple"; //添加一項
 console.log(colors.length); //4
 
 var item = colors.pop(); //取得一項
 console.log(item); //purple

隊列方法

FIFO(First-In-First-Out), 先進先出

數組最左側的會被移除, 最右側的會被添加

shift(), 移除數組中的第一個項(左邊)並返回該項, 同時將數組長度減1

unshift(),在數組前端(左邊)添加任意個項並返回數組長度

//結合使用shift()和push()方法, 能夠像使用隊列同樣使用數組
var colors = [];
var count = colors.push("red","green"); //推入兩項
console.log(count); //2

count = colors.push("black"); //推入另外一項
console.log(count); //3

var item = colors.shift(); //取得第一項(左邊)
console.log(item); //red
console.log(colors.length); //2
//結合使用unshift()和pop()方法, 能夠反向模擬隊列, 即在數組的前端(左邊)添加項, 在末尾(右邊)移除項
var colors = [];
var count = colors.unshift("red","green"); //推入兩項
count = colors.unshift("black"); //推入另外一項
console.log(count); //3

var item = colors.pop(); //取得最後一項
console.log(item); //black 
console.log(colors.length); //2

重排序方法

reverse(),反向排序

sort(),把數組的每一項轉換成字符串再進行排序

//reverse()
var values = [1, 2, 3, 4, 5]; 
values.reverse(); 
console.log(values); //5, 4, 3, 2, 1
//sort()方法由於會轉換爲字符串, 因此排序時會出現問題, 不少時候不會按照正常的規則排列 -- 即最小的排最前面, 最大的排最後面
//因此使用sort()時應該接收一個比較函數, 比較函數定義兩個參數, 若是第一個參數應該位於第二個以前就返回一個負數, 若是兩個參數相等就返回0, 若是第一個參數應該位於第二個以後就返回一個整數.
function compare(value1, value2){
  if(value1 < value2){
      return -1;
  }else if(value1 > value2){
      return 1;
  }else{
    return 0;
  }
} 
//使用上面的比較函數
var values = [3, 2, 6, 8, 1];
values.sort(compare);
console.log(values); //1, 2, 3, 6, 8

操做方法

concat(), 拼接接收的參數, 返回一個拼接後的數組.

slice(), 數組截取方法, 接受一個或兩個索引參數, 一個時, 會返回該索引到數組結尾的項(包括該索引的項), 兩個時, 會返回第一個到第二個索引參數之間的項(不包括第二個索引的項).

splice(), 像數組的中部插入項, 有3種方式

  • 刪除 : 兩個參數, 要刪除的起始索引位置, 以及要刪除的項

  • 插入 : 三個參數, 起始索引位置, 要刪除的項數, 要插入的項. 若是第二個參數設置爲0, 則不刪除直接插入

  • 替換 : 三個參數, 起始因此位置, 要刪除的項數, 要插入的想, 跟上面插入同樣, 只不過第二個參數不爲0, 刪除後插入第三個參數的數據

//concat
var colors1 = ["purple", "pink", "blue"]; 
var colors2 = ["green", "red", "yellow"];
var allcolors = colors1.concat(colors2);
console.log(allcolors); //["purple", "pink", "blue", "green", "red", "yellow"]

var anothercolors = colors1.concat("white",["orange","lightpink"]);
console.log(anothercolors); //["purple", "pink", "blue", "white", "orange", "lightpink"]
//slice
var colors = ["red", "blue", "green", "pink", "orange", "purple"];
var colors2 = colors.slice(1); //"blue", "green", "pink", "orange", "purple"
var colors3 = colors.slice(2,5); //"green", "pink", "orange"

/*若是參數是負數, 則使用數組長度加上該負數來決定相應的位置; 
若是這時第二個參數的位置小於第一個參數的位置, 則會返回一個空數組
若是第一個參數和數組長度相加以後等於負數, 則從索引0開始計算
*/
var colors3 = colors.slice(-3 , -1); //等同於slice(3,5), 結果是"pink", "orange"
var colors4 = colors.slice(-7 , -1); //等同於(0,5), 結果是"red", "blue", "green", "pink", "orange"
var colors5 = colors.slice(-7 , -8); //第二個索引參數大於第一個索引參數(起始位置), 因此這裏返回空數組 "[]"
//splice
var colors = ["green","red","blue"]; 
var removed = colors.splice(0, 1); //刪除索引爲0開始的1項
console.log(colors); //red, blue
console.log(removed); //green

removed = colors.splice(1, 0, "purple", "pink"); //從索引爲1的地方刪除0項, 添加後面兩項
console.log(colors); //red, purple, pink, blue
console.log(removed); //空數組 []

removed = colors.splice(1, 1, "green","black"); //從索引爲1的地方刪除1項(索引1的項), 添加後面兩項
console.log(colors); //red, green, black, pink, blue
console.log(removed); //被刪除的項 purple

位置方法

indexOf(),兩個參數, 要查找的項, 以及(可選的)查找的起點位置的索引

lastIndexOf(),同上, 但該方法會在數組的末尾向前查找

兩個方法都會返回要查找的想在數組中的位置, 沒有找到的話返回-1

var numbers = [1,2,3,4,5,4,3,2,1];

alert(numbers.indexOf(4));//3
alert(numbers.lastIndexOf(4));    //是從左邊開始數起的索引值, 但尋找是從右邊向左找, 因此是5

alert(numbers.indexOf(4, 4));     //5
alert(numbers.lastIndexOf(4, 4)); //3       

var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];

alert(people.indexOf(person));     //-1
alert(morePeople.indexOf(person)); //0

迭代方法

ES5定義了5個迭代方法, 每一個方法都接收兩個參數, 一是要在每一項上運行的函數, 二(可選)是運行在該函數的做用域對象 — 影響this的值.

傳入這5個迭代方法做爲參數的函數(即第一個參數)會接收三個參數, 數組項的值, 該項在數組中的位置 以及 數組對象自己.

  • every(), 對數組的每一項運行給定函數, 若是該函數對每一項都返回true, 則返回true

  • filter(), 對數組的每一項運行給定函數, 返回該函數會返回true的項組成的數組

  • forEach(), 對數組的每一項運行給定函數, 沒有返回值

  • map(), 對數組的每一項運行給定函數, 返回每次函數調用的結果組成的數組

  • some(), 對數組的每一項運行給定函數, 若是該函數對任一項返回true, 則返回true

//every()和some()都是用於查詢數組中的項是否知足某個條件
var numbers = [1,2,3,4,5,4,3,2,1];

var everyResult = numbers.every(function(item, index, array){
    return (item > 2);
});
alert(everyResult);       //every()在每一項都知足條件纔會返回true,因此這裏返回false
        
var someResult = numbers.some(function(item, index, array){
    return (item > 2);
});
alert(someResult);       //some()只有有其中一項或多項知足條件就會返回true, 因此這裏返回true
//filter()返回條件中知足條件的項
var numbers = [1,2,3,4,5,4,3,2,1];

var filterResult = numbers.filter(function(item, index, array){
      return (item > 2);
});
alert(filterResult); // 3,4,5,4,3
//map()返回原始數組與給定函數運算而產生的結果
var numbers = [1,2,3,4,5,4,3,2,1];

var mapResult = numbers.map(function(item, index, array){
    return item * 2;
});
alert(mapResult);   //[2,4,6,8,10,8,6,4,2]
//forEach()沒有返回值, 會使用原始數組和給定函數進行運算, 本質上和for循環迭代數組同樣
var numbers = [1,2,3,4,5,4,3,2,1];

numbers.forEach(function(item, index, array){
    //do sth..
});

歸併方法

ES5新增reduce()reduceRight()兩個歸併方法, 均會迭代數組的全部項, 而後構建一個最終返回的值. 前者會在數組的第一項開始迭代, 後者相反.

兩個方法都接收兩個參數, 一是在每一項上調用的函數, 二是(可選)做爲歸併基礎的初始值

其中在每一項上調用的函數接收四個參數, 前一個值, 當前值, 項的索引, 數組對象

//reduce()
var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array){
  //第一次執行回調函數時, prev是1, cur是2
  //第二次, prev是3(1加2的結果), cur是3(數組第三項)
  return prev + cur;
});
console.log(sum); //15
//reduceRight()
var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array){
  //左右類似, 不過做用方向相反
  //第一次執行回調函數時, prev是5, cur是4
  return prev + cur;
});
console.log(sum); //15

Date類型

var now = new Date(); //建立日期對象, 得到當前時間

其餘參見 :

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date#.E6.91.98.E8.A6.81

RegExp類型

//建立正則表達式
var expression = / pattern / flags ;

模式(pattern)部分能夠是任何簡單或複雜的表達式, 包括字符類, 限定符, 分組, 向前查找, 反向引用

標誌(flags)能夠有一或多個, 用以代表正則表達式的行爲 :

  • g , 全局(global)模式, 即模式會被應用於全部字符串, 而非在發現第一個匹配項時當即中止

  • i , 不區分大小寫(case-insensitive)模式, 即在肯定匹配項時忽略模式與字符串的大小寫

  • m , 多行(multiline)模式, 即在到達一行文本末尾時還會繼續查找下一行中是否存在與模式匹配的項

/*
 *匹配字符串中因此"at"的實例
 */
var pattern1 = /at/g;

 /*
  *匹配第一個"bat"或"cat", 不區分大小寫
  */
var pattern2 = /[bc]at/i;

 /*
  *匹配全部以"at"結尾的三個字符的組合, 不區分大小寫
  */
var pattern3 = /.at/gi;

正則表達式中的元字符包括 : ( [ { \ ^ $ | ) ? * + .]}

若是須要在正則表達式中使用這些元字符, 則必須進行轉義 :

/*
 *匹配第一個"bat"或"cat", 不區分大小寫
 */
 
var pattern1 = /[bc]at/i;

/*
 *匹配第一個" [bc]at", 不區分大小寫
 */
 
var pattern2 = /\[bc\]at/i;

/*
 *匹配全部以"at"結尾的三個字符的組合, 不區分大小寫
 */
  
var pattern3 = /.at/gi;

/*
 *匹配全部以".at"結尾的三個字符的組合, 不區分大小寫
 */
 
var pattern4 = /\.at/gi;
//不使用上面的字面量形式來定義正則表達式, 而使用RegExp構造函數,
//接收兩個參數, 一是要匹配的字符串模式, 二(可選)是標誌字符串
/*
 *匹配第一個"bat"或"cat", 不區分大小寫
 */
 
var pattern1 = /[bc]at/i;
var pattern2 = new RegExp("[bc]at", "i");

使用構造函數模式時注意某些字符的雙重轉義問題 :

var pattern1 = /\[bc\]at/;
var pattern1c = new RegExp("\\[bc\\]at"); //上面的構造函數形式, 注意符號的雙重轉義

var pattern2 = /\.at/;
var pattern2c = new RegExp("\\.at");

var pattern3 = /name\/age/;
var pattern3c = new RegExp("name\\/age");

var pattern4 = /\d.\d{1,2}/;
var pattern4c = new RegExp("\\d.\\d{1,2}");

var pattern5 = /\w\\hello\\123/;
var pattern5c = new RegExp("\\w\\\\hello\\\\123");

RegExp實例方法

exec(), RegExp對象的主要方法, 專門爲捕獲組而設計的, 接收一個參數, 即要應用模式的字符串, 而後返回包含第一個匹配項信息的數組, 沒有匹配項的狀況下返回null.

返回的數組是Array的實例, 但包含兩個額外的屬性,indexinput, index表示匹配項在字符串中的位置, input表示應用正則表達式的字符串.

….TODO

Function類型

函數.每一個函數都是Function類型的實例.

函數是對象, 函數名是一個指向函數對象的指針,不會與某個函數綁定

//函數定義
function sum(n1,n2){
  return n1 + n2;
}

//另外一種方式
var sum = function(n1, n2){
  return n1 + n2;
}

//函數名只是指針, 因此一個函數能有多個名字
var anotherSum = sum;
console.log(auntherSum(1+2)); //3
sum = null; //把sum設置爲空
console.log(auntherSum(1+2)); //並不影響, 而且依然能輸出3
ES中沒有重載
function addSomeNumber(n){
  return n + 100;
}

//後聲明的纔有效
function addSomeNumber(n){
  return n + 200;
}

var r = addSomeNumber(100); //300

//其實就至關於下面的代碼
var addSomeNumber = function(n){
  return n + 100;
}

//覆蓋了前面的同名函數
addSomeNumber = function(n){
  return n + 200;
}

var r = addSomeNumber(100); //300
//函數聲明與函數表達式的區別, 函數聲明支持函數聲明提高, 即解析器會率先解讀函數聲明, 而後才執行代碼
alert(sum(10, 10)); //有效, 輸出20
function sum(n1, n2){
  return n1 + n2;
}

//但函數表達式不支持函數聲明提高
alert(sum(10, 10)); //錯誤
var sum = function(n1, n2){
  return n1 + n2;
}
函數名自己就是變量, 因此也能做爲值來使用
function callSomeFunction(someFunction, someArgument){
  //第一個參數是函數, 第二個參數是傳遞給該函數的一個值
  return someFunction(someArgument);
}

function add10(n){
  return n + 10;
}

//注意add10沒有加括號, 是由於只訪問函數的指針而不執行函數, 就要去掉括號
var r1 = callSomeFunction(add10, 10);
console.log(r1); //20

function getGreeting(name){
  return "Hi, " + name;
}

var r2 = callSomeFunction(getGreeting, "Jon");
console.log(r2); //Hi, Jon
//從一個函數中返回另外一個函數
function createComparisonFunction(propertyName){
  return function(o1, o2){
    var v1 = o1[propertyName];
    var v2 = o2[propertyName];
    
    if(v1 < v2){
        return -1;
    }else if(v1 > v2){
        return 1;
    }else{
       return 0;
    }
  }
}

var data = [
  {name : "Jon", age : 25},
  {name : "Mark", age : 24}
];

data.sort(createComparisonFunction("name"));
console.log(data[0].name); //Jon

data.sort(createComparisonFunction("age"));
console.log(data[0].name); //Mark
函數的內部屬性

argumentscallee屬性, 是一個指針, 指向擁有這個arguments對象的函數

//階乘函數
function factorial(n){
  if(n <= 1){
    return 1;
  }else{
    //函數執行與函數名factorial牢牢耦合
      return n * factorial(n - 1);
  }
}

//使用callee消除耦合
function factorial(n){
  if(n <= 1){
    return 1;
  }else{
      return n * arguments.callee(n - 1);
  }
}

//trueFactorial得到了factorial的值, 其實是在另外一個位置保存了一個函數的指針
var trueFactorial = factorial;

//把factorial變成返回0的簡單函數
factorial = function(){
  return 0;
};

//假如不使用arguments.callee, 那麼下面的trueFactorial也會返回0, 使用了arguments.callee, 便可以解除耦合, 返回正常
console.log(trueFactorial(5)); //120
console.log(factorial(5)); //0

this

window.color = "red";
var o = {
  color : "blue"
};

function sayColor(){
  console.log(this.color);
}

//在全局做用域內調用, 此時this引用的對象是window, 因此輸出red
sayColor(); //red

//把函數賦給對象o並調用sayColor(), 此時this引用的對象是對象o, 因此輸出blue
o.sayColor = sayColor;
o.sayColor(); //blue

ES5新增的對象屬性caller, 這個屬性保存着調用當前函數的函數的引用.

全局做用域調用時它的值爲null

function outer(){
  inner();
}

function inner(){
  console.log(inner.caller);
}

outer();
/*function outer(){
  inner();
  }*/
//更鬆散的耦合, 使用arguments.callee.caller
function outer(){
  inner();
}

function inner(){
  console.log(arguments.callee.caller);
}

outer();
/*function outer(){
  inner();
  }*/
//嚴格模式下, 訪問arguments.callee會致使錯誤; 訪問arguments.caller也會致使錯誤; 嚴格模式下還不能爲函數的caller屬性賦值, 不然會致使錯誤
函數屬性和方法

每一個函數開始都包含兩個屬性, length,prototype

length, 表示函數但願接收的命名參數的個數

//length
function sayName(name){
  console.log(name);
}

function sum(sum1, sum2){
  return num1 + num2;
}

function sayHi(){
  console.log("Hi");
}

console.log(sayName.length); //1
console.log(sum.length); //2
console.log(sayHi.length); //0

prototype, ES引用類型中保存全部實例方法的屬性, 該屬性不可枚舉

apply(), call(), ES函數中兩個原生的方法, 用途都是在特定的做用域中調用函數, 實際上等於設置函數體內this對象的值. 接收兩個參數, 一是在其中運行函數的做用域, 二是參數數組(第二個參數能夠是Array的實例, 也能是arguments對象)

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

function callSum1(num1, num2){
  return sum.apply(this, arguments); //傳入arguments對象
}

function callSum2(num1, num2){
  return sum.apply(this, [num1, num2]); //傳入數組
}

console.log(callSum1(10, 10)); //20
console.log(callSum2(10, 10)); //20
//call()與apply()做用相同, 只是接收參數的方式不一樣, 使用call()時, 傳遞給函數的參數必須逐個列舉出來
function sum(num1, num2){
  return num1 + num2;
}

function callSum(num1, num2){
  return sum.call(this, num1, num2);
}

console.log(callSum(10, 10)); //20
//apply()和call()重要的做用是擴充函數的做用域, 好處是對象不須要和方法有任何的耦合關係
window.color = "red";
var o = {
  color : "blue"
}

function sayColor(){
  cosnole.log(this.color);
}

sayColor(); //red

sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue

ES5新增了bind()方法, 會建立一個函數的實例, 其this值會被綁定到傳給bind()函數的值

window.color = "red";
var o = {
  color : "blue"
}

function sayColor(){
  cosnole.log(this.color);
}

//用sayColor()方法綁定o對象, this的值指向o, 因此輸出blue
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

基本包裝類型

3種特殊的引用類型, Boolean, Number, String

這三種特殊的類型可使用new操做符建立實例, 但如非必要不推薦

//Boolean
var booleanObject = new Boolean(true); //不推薦使用
//Number
var numberObject = new Number(10);

//toFixed()按照指定的小數位返回數值的字符串表示
var num = 12;
console.log(num.toFixed(2)); //12.00

//toExponential()返回指數表示法(e表示法)表示的數值的字符串形式
var num2 = 11;
console.log(num2.toExponential(1)); //1.1e1

//toPrecision(), 返回合適的格式, 有多是固定大小(fixed)格式, 也多是e表示法, 接受一個參數, 即表示數值的全部數字的位數(不包括指數部分)

var num3 = 88;
console.log(num3.toPrecision(1)); //9e+1
console.log(num3.toPrecision(2)); //99
console.log(num3.toPrecision(3)); //99.0
//String
var stringObject = new String("Hi Jon");

//length屬性, 表示字符串中包含的字符數量
console.log(stringObjet.length); //6

//charAt與charCodeAt(), 一個參數, 即須要查找的字符的索引, 返回查找到的字符, 後者獲得的是 字符編碼
var s1 = "Happy FrontEnd!";
console.log(s1.charAt("F")); //6
console.log(s1.charCodeAt("F")); //72
//ES5可使用方括號表示法, 接收一個索引值以返回獲得的字符串
console.log(s1[3]); //p

//字符串操做方法
//concat(), 用於拼接字符串
var s2 = "Hi ";
var s3 = "Jon";
var r = s2.concat(s3);
console.log(r); //Hi Jon
//能夠直接接受字符串使用
console.log("Hi", "Mark", "!"); //Hi Mark !

//slice(), substr(), substring(), 用於截取字符串並返回一個副本
//slice()和substring()接收兩個參數, 開始索引值和結束索引值, 返回兩個索引值之間的值, 在參數不是負數的時候做用相同
//substr(), 第一個參數接收開始的索引值, 第二個參數接收**返回的字符個數**
//若是這三個方法不接收第二個參數, 則返回開始字符串到結尾的字符串

var s4 = "Have a Good Day!";
console.log(s4.slice(3)); //e a Good Day!
console.log(s4.substring(3)); //e a Good Day!
console.log(s4.substr(3)); //e a Good Day!
console.log(s4.slice(3,8)); //e a G
console.log(s4.substring(3,8)); //e a G
console.log(s4.substr(3,8)); //e a Good

//傳入負值的狀況下, slice()會把 傳入的負值 和 字符串的長度相加; substr()會把第一個負值的參數加上字符串的長度, 而把第二個負的參數轉爲0; substring()會把全部負值參數都轉爲0
var s5 = "My WOW player is Warlock";
console.log(s5.slice(-3)); //-3 + 24(字符串長度) = 21(從索引值爲21的字符開始); 輸出ock
console.log(s5.substr(-3)); //-3 + 24(字符串長度) = 21(從索引值爲21的字符開始); //輸出ock
console.log(s5.substring(-3)); //全部負數參數轉換爲0, 輸出My WOW player is Warlock
console.log(s5.slice(3, -3)); //WOW player is Warl
console.log(s5.substr(3, -3)); //輸出"", 由於第二個參數轉爲0(返回字符的個數)
console.log(s5.substring(3, -3)); //My


//字符串位置方法, indexOf(), lastIndexOf(), 從一個字符串中搜索給定的子字符串, 而後返回字符串的位置(沒有則返回-1), 其中indexOf()會從頭開始搜索, 而lastIndexOf()會在後面開始搜索

var s6 = "Happy WOW game!";
console.log(s6.indexOf("a")); //1 從前面開始搜索, 最早出現的索引位置是1
console.log(s6.lastIndexOf("a")); //11 從後面開始搜索, 最早出現的索引位置是11
//能夠接收第二個可選參數, 表示從字符串中的哪個位置開始搜索
console.log(s6.lastIndexOf("a",4)); //11, 從索引值4的位置開始搜索, 索引值1的a已經被忽略, 因此找到第二個a在索引值11的位置
console.log(s6.lastIndexOf("a",6)); //1, 從索引值6開始向前搜索, 因此位置11的a已經被忽略, 因此找到第二個a, 在索引值1的位置
//indexOf()循環調用獲得字符串中全部匹配的子字符串
var s7 = "My name is JonHo, World Of Warcraft is my favourite game";
var positions = []; //用於存取找到的所有子字符串的索引值數組
var pos = s7.indexOf('a'); //找到第一個a, 進入循環

while(pos > -1){ //當找到a子字符串時(找不到會返回-1,大於-1即表示找到)進入循環
  positions.push(pos); //把找到的索引值(pos內的內容)push到positions數組內
  pos = s7.indexOf('a', pos + 1); //每次循環都給上一次找到的a的索引值位置加1, 那樣能確保每次新的搜索都在上一次找到的子字符串(a)的索引值後一個值開始
}

console.log(positions); //[4, 28, 32, 43, 53]
//ES5的trim()方法, 會刪除字符串的前置及後置空格
var s8 = "    My name is Jon.       ";
var trimmedString = s8.trim();
console.log(trimmedString); //My name is Jon.
//字符串大小寫轉換, 經常使用的兩個, toLowerCase()和toUpperCase(), 還有兩個不經常使用的toLocaleLowerCase()和toLocaleUpperCase()
//不經常使用的兩個方法一般會返回與前面兩個方法相同的結果, 但在少數語言中會爲Unicode大小寫轉換應用特殊的規則.
var s9 = "My WOW player is Warlock.";
var upperResult = s9.toUpperCase(); 
var lowerResult = s9.toLowerCase(); 
console.log(upperResult); //MY WOW PLAYER IS WARLOCK.
console.log(lowerResult); //my wow player is warlock.
//String的RegExp匹配方法, 略
單體內置對象(Global和Math)

Global是實際上是終極對象, 在平常使用中不存在這個對象, 注意其屬性和某些特殊方法

Math對象的屬性和方法都是數學上經常使用的運算方法, 不詳述, 參見

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math

相關文章
相關標籤/搜索