javascript
JavaScript知识点总结(二)
變量, 作用域, 垃圾收集(內存問題)
基本類型和引用類型
ES中的變量包含基本類型值和引用類型值
基本類型值指的是簡單的數據段
引用類型值值那些可能有多個值構成的對象
五種基本數據類型(Undefined, Null, Boolean, Number, String)的值即基本類型值是按值訪問的, 因此操作的是保存在變量中實際的值
引用類型值是保存在內存中的對象,ES不允許直接訪問內存中的位置, 即不能直接操作對象的內存空間. 在操作對象時, 實際上是在操作對象的引用而不是實際的對象.
當復制保存在對象中的某個變量時, 操作的是對象的引用. 但在為對象添加屬性時, 操作的是實際的對象.
動態的屬性
定義基本類型的值和引用類型的值方式是基本一樣的, 就是創建一個變量, 然后為該變量賦值.
創建變量以后,基本類型的值和引用類型的值執行的操作有很大不同.
//引用類型可以為其添加或刪除屬性和方法 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復制變量值
復制基本類型值的變量值與復制引用類型值的變量值也存在不同.
復制基本類型值時, 是直接創建新值, 占據不同的內存(棧)空間, 復制之后兩個變量可以參與任何操作而不互相影響
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); //objectinstanceof — 檢測引用數據類型值時, 檢測其引用數據類型值是什么類型的對象
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對象的屬性和方法創建的.
每個函數都有自己的執行環境, 當執行流進入一個函數時, 函數的環境就會被推入一個環境棧中, 函數執行之后, 棧將其環境彈出, 把控制權返回給之前的執行環境.
代碼在一個環境中執行時, 會創建變量對象的一個作用域鏈, 它保證了對執行環境有權訪問的所有變量和函數的有序訪問.
全局執行環境的變量對象始終都是作用域鏈中的最后一個對象
沒有塊級作用域
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); //2count = colors.push("black"); //推入另一項 console.log(count); //3var 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); //4var 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); //2count = colors.push("black"); //推入另一項 console.log(count); //3var 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); //3var 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, 刪除后插入第三個參數的數據
位置方法
indexOf(),兩個參數, 要查找的項, 以及(可選的)查找的起點位置的索引
lastIndexOf(),同上, 但該方法會在數組的末尾向前查找
兩個方法都會返回要查找的想在數組中的位置, 沒有找到的話返回-1
var numbers = [1,2,3,4,5,4,3,2,1];alert(numbers.indexOf(4));//3 alert(numbers.lastIndexOf(4)); //是從左邊開始數起的索引值, 但尋找是從右邊向左找, 所以是5alert(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
歸并方法
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是4return prev + cur; }); console.log(sum); //15Date類型
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)模式, 即在到達一行文本末尾時還會繼續查找下一行中是否存在與模式匹配的項
正則表達式中的元字符包括 : ( [ { \ ^ $ | ) ? * + .]}
如果需要在正則表達式中使用這些元字符, 則必須進行轉義 :
/**匹配第一個"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的實例, 但包含兩個額外的屬性,index和input, 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)); //并不影響, 并且依然能輸出3ES中沒有重載
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); //20function 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); //Jondata.sort(createComparisonFunction("age")); console.log(data[0].name); //Mark函數的內部屬性
arguments的callee屬性, 是一個指針, 指向擁有這個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)); //0this
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(); //blueES5新增的對象屬性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); //0prototype, 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(); //redsayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blueES5新增了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
總結
以上是生活随笔為你收集整理的JavaScript知识点总结(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 监控统计阻塞脚本信
- 下一篇: activiti监听器使用