因爲工做緣由,開發語言逐漸以JavaScript爲主,因此,抽空學習了下JavaScript語法。等現階段的工做穩定以後,陸續會分享下本身在學習和開發過程當中的一些經驗總結。本着"技多不壓身"的原則以及對各類編程語言的熱愛,雖然筆者一直從事遊戲開發工做,也愉快而又義無反顧的加入了JavaScript陣營。javascript
首先,JavaScript是一門動態類型的編程語言。支持面向對象、函數式等編程範式。同時,它也是運行在宿主環境下的輕量級的腳本語言,例如:瀏覽器,JavaScript代碼可嵌入到HTML頁面中。固然,也有應用基於Node.js的服務器環境。能夠說它是主流瀏覽器都支持的腳本語言,這也造就了JavaScript在PC、手機、平板等環境處理與網頁交互時的自然優點。隨着HTML5的推廣與普遍應用,出現了大量基於JavaScript的跨平臺框架和遊戲引擎,是通往全棧開發很值得學習的一門編程語言。正如在編程語言界流行着「世界終將是JS的」的槽點,足以可見JavaScript的強大。前端
web前端:最近比較火的Vue.js、React、 Angular等等前端框架層出不窮。
手機app:React Native、PhoneGap、Weex等跨平臺移動框架等,以及微信小程序等。
遊戲引擎:Cocos2d-js、Unity、egret等引擎都支持JavaScript腳本語言。
服務器:pomelo、Node.js等。java
JavaScript語法中大量借鑑了C和Java、Perl等語言的語法,所以熟悉這些語言的人再來學習JavaScript會感受似曾相識。但JavaScript與Java是兩回事,JavaScript徹底是蹭了當年Java的熱度,來推進本身。web
一個完整的JavaScript實現由下面三個不一樣的部分組成:express
ECMAScript:
定義了語言的基礎,規定並對該語言在語法、類型、語句、關鍵字、保留字、操做符、對象等方面做了具體描述,並不包含輸入和輸出。編程
DOM:
文檔對象模型(Document Object Model)是XML和HTML的編程接口。DOM會把整個頁面映射爲一個多層節點結構樹,程序能夠對結構樹進行添加、刪除、替換或修改任何節點。小程序
BOM:
瀏覽器對象模型(Browser Object Model)支持訪問和操做瀏覽器窗口。微信小程序
注:如下描述就不區分ECMAScript和JavaScript,也不簡稱js,均統稱JavaScript。api
這裏的標識符,統指變量名、函數名、屬性名以及函數參數的名字。那麼,標識符的命名有如下規則:(這些規則跟C、Java等語言相似)數組
注:在JavaScript中,變量名、函數名甚至操做符等是區分大小寫的。即name和Name表明的是兩個不一樣的變量。
單行註釋使用//。例如:
// 這是一個單行註釋
多行註釋使用/***/結構。例如:
/* * 這是一個多行註釋 */
啓用嚴格模式,會對一些不肯定的行爲或不安全的操做拋出錯誤。在腳本的頂部添加以下字符串:
"use strict";
也能夠添加到函數內部,來指定該函數在嚴格模式下執行。
JavaScript中每條語句都是以分號結尾,但分號不是必需的。但建議是不要省略分號,來避免如不完整的輸入等問題。語句塊用花括號包含。
可使用var
關鍵字來定義變量,JavaScript的變量能夠用來保存任何類型的數據。例如:
var name = "AlphaGL"; name = 18; name = function() { };
也可使用逗號來定義多個變量。例如:
var name = "AlphaGL", age = 18, work = true;
雖然JavaScript支持這種寫法,通常不推薦。不只可讀性差,並且易出錯。
在函數內聲明的變量,那麼該變量的做用域爲該函數內。例如:
function test() { var name = "AlphaGL"; } test(); console.log(name); // ReferenceError: name is not defined
雖然能夠去掉var聲明來解決這個問題,例如:
function test() { name = "AlphaGL"; } test(); console.log(name);
這時,name就成了全局變量。通常不推薦這種作法。全局變量難以維護,並且容易致使結構混亂。
JavaScript中數據類型主要有:Number、String、Boolean、Object、undefined、null這幾種類型。以及涉及到類型判斷時會用到的操做有:typeof、instanceof、Object.prototype.toString。
(1)JavaScript中並不區分整型或浮點數,全部數字均統一採用64位浮點數表示。可用Number.MIN_VALUE和Number.MAX_VALUE來獲取Number類型的值的範圍。
console.log(Number.MIN_VALUE); // 5e-324 console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
(2)NaN
非數值(Not a Number)是一個特殊的數值。當算術運算返回一個未定義或沒法表示的值時,就產生NaN了。
console.log(typeof(NaN)); // number console.log(0 / 0); // NaN console.log("hello" / 5); // NaN console.log(NaN + 1); // NaN
注:0除以0會返回NaN。其它數除以0不會。
判斷一個變量是不是非數值,可使用isNaN()函數。
console.log(isNaN(NaN)); // true console.log(NaN == NaN); // false console.log(isNaN("hello")); // true,"hello"不能轉換成數值 console.log(isNaN(true)); // false console.log(isNaN(false)); // false console.log(isNaN(5)); // false console.log(isNaN("5")); // false
注:NaN與其它全部值都不相等,包括它本身。所以判斷NaN不要用
==
或===
。
(3)Infinity(正無窮)和-Infinity(負無窮)
JavaScript中還有兩個特殊的數值Infinity和-Infinity。
console.log(typeof(Infinity)); // number console.log(typeof(-Infinity)); // number console.log(1 / 0); // Infinity console.log(1 / -0); // -Infinity
判斷一個變量是不是有窮數,可使用isFinite()函數。
console.log(isFinite(Infinity)); // false console.log(isFinite(-Infinity)); // false console.log(isFinite(NaN)); // false console.log(isFinite(0)); // true console.log(isFinite("0")); // true
(1)String類型字符串是由單引號或雙引號包括的零個或多個字符序列。
var key = "hello"; var value = 'world';
(2)字符串能夠像數組同樣訪問,但一旦字符串的值肯定了,就不能改變。
var foo = "Java"; foo[0] = "S"; console.log(foo); // Java foo = foo + "Script"; console.log(foo); // JavaScript
(3)toString()與String
var a = 5; var b = false; var c = "AlphaGL" var d = new Date(); console.log(a); // 5 console.log(b); // false console.log(c); // AlphaGL console.log(d); // 2017-05-12T05:54:30.547Z
console.log(String(5)); // 5 console.log(String(false)); // false console.log(null); // null console.log(undefined); // undefined
還有一些常規的String操做的api,能夠查閱JavaScript的String api文檔。
該類型只有兩個字面值:true和false。須要強調的是,true不必定是1,false也不必定是0。其它類型轉換成Boolean類型時,有一下規則:
console.log(Boolean("AlphaGL")); // true console.log(Boolean("false")); // true console.log(Boolean("")) // false console.log(Boolean(10)); // true console.log(Boolean(Infinity)); // true console.log(Boolean(-Infinity)); // true console.log(Boolean(0)); // false console.log(Boolean(NaN)); // false console.log(Boolean(new Date())); // true console.log(Boolean(null)); // false console.log(Boolean(undefined)); // false
綜上:當值爲0,null,false,NaN,undefined,空字符串("")轉換成Boolean類型時值都爲false,其它任何值都爲true。
如下實例:
(1)
var x; if (x) { console.log("test1") }else { console.log("test2") }
輸出:test2
(2)
var x = null; if (x) { console.log("test1") }else { console.log("test2") }
輸出:test2
(3)
var x = Boolean(false); if (x) { console.log("test1") }else { console.log("test2") }
輸出:test2
(4)
var x = false; if (x) { console.log("test1") }else { console.log("test2") }
輸出:test2
(5)
var x = new Boolean(false); if (x) { console.log("test1") }else { console.log("test2") }
輸出:test1
綜上:任何值不爲undefined或者null的對象, 包括值爲false的Boolean對象, 在條件語句中,其值都將做爲true來判斷。
對象是JavaScript中重要的數據類型,除數字、true、false、null、undefined和字符串外,全部的值都是對象。在JavaScript中對象是一組無序的鍵值對集合,相似其它語言的HashMap、Dictionary等數據結構。下面會有單獨一小節來專門講述下對象。
在使用var聲明變量但未對其初始化時,該變量的值即爲:undefined。undefined類型也只有這一個值。
字面值undefined主要用於比較。
var i = undefined; console.log(i == undefined); // true
未初始化的變量與未聲明的變量
var foo; console.log(foo); // undefined console.log(bar); // 報錯
var foo; console.log(typeof(foo)); // undefined console.log(typeof(bar)); // undefined
使用typeof操做符來檢測未初始化的變量與未聲明的變量,都返回undefined。
與undefined相似,null類型也只有一個值null。空值null表示一個空的對象指針。
console.log(typeof(null)); // object
null與undefined比較:
console.log(Number(null)); // 0 console.log(Number(undefined)); // NaN console.log(isNaN(1 + null)); // false console.log(isNaN(1 + undefined)); // true console.log(null === undefined); // false console.log(null == undefined); // true
因爲JavaScript是動態類型的,所以就須要一種手段來檢測給定的變量的具體數據類型。
(1)typeof
typeof操做符返回的值是如下某個字符串:
console.log(typeof(123)); // number console.log(typeof("123")); // string console.log(typeof(NaN)); // number console.log(typeof(Infinity)); // number console.log(typeof(false)); // boolean console.log(typeof(null)); // object console.log(typeof([])); // object console.log(typeof({})); // object console.log(typeof(function(){})); // function console.log(typeof(undefined)); // undefined
(2)instanceof:下文會討論。
(3)Object.prototype.toString:下文會討論。
流程控制主要有條件語句和循環語句。條件語句又分爲:if語句,if-else語句,switch語句等。循環語句分爲:while循環,for循環,do-while循環。
(1)if語句
if (condition1) { statement1; }else if(condition2) { statement2; }else { statement3; }
該結構,當條件1(condition1爲true)知足時,執行語句1(statement1),不然條件2(condition2爲true)知足時,執行語句2,以上都不知足則執行語句3(statement3)。
這裏的else if
能夠有零個或多個,甚至else
都是可選的,根據實際狀況來作實際判斷。
if (i > 0) { console.log("i大於0"); }else if(i < 0) { console.log("i小於0"); }else { console.log("i等於0"); }
(2)switch語句
switch是一種多分支結構,與if結構相似,也是一種廣泛使用的流程控制語句。
switch(expression) { case value1: statement1; break; case value2: statement2; break; default: statement3; }
當表達式(expression)的值,等於對應case的值(value),則會執行對應的語句塊(statement),break用於跳出該switch結構,若省略break,則會繼續執行下一個case。default用於以上表達式的值都不知足條件。
表達式(expression)可使用任何數據類型,包括字符串、對象。case的值(value)也能夠是變量、表達式,不必定是常量。這點與其餘有的語言不一樣,只能使用數值。
(1)while語句
while循環包含循環條件和循環體,當循環條件知足條件時,纔會執行循環體內的代碼塊。
while(expression) { statement; }
while(i < 100) { i++; }
(2)for語句
for語句通常包含初始條件、循環條件、循環遞增(遞減)語句三部分。
for(initialization; expression; post-loop-expression) { statement; }
for(var i = 0; i < 100; i++) { console.log(i); }
(3)for-in語句
該語句相似於其餘語言的foreach語句,能夠用了遍歷容器結構或對象的屬性。
for (property in expression) { statement; }
var o = [1, 2, 3]; for(var i in o) { console.log(o[i]); }
(4)do-while語句
do-while語句與while語句相似,不一樣的時,do-while語句會先執行循環體,再檢測循環條件,這就意味着該循環語句至少會執行一次。
do { statement; }while(expression);
(5)break與continue
break和continue均可用於控制循環中代碼的執行。
break:當即退出循環。
continue:跳出當前循環,繼續執行下次循環。
JavaScript中運算符大致可分爲,算術運算符、關係運算符、邏輯運算符以、位運算符以及其它操做符等,固然也包括運算符重載。
+
,-
,*
,/
,%
,++
,--
,+=
,-=
,*=
,/=
,%=
等。<
,>
,<=
,>=
,==
,===
,!=
,!==
。&&
,||
,!
~
,&
,|
,^
,<<
,>>
,>>>
=
,,
,?=
(1)加法操做符(+)
加減法操做符時JavaScript中最經常使用得操做符之一,與其餘語言不一樣得是,在JavaScript中有一系列特殊得行爲,使用時應當注意。
console.log(1 + 2); // 3 console.log(1 + "2"); // 12 console.log("1" + "2"); // 12 console.log(1 + [2]); // 12 console.log(1 + "2" + 3); // 123 console.log("1" + 2 + 3); // 123 console.log(1 + 2 + "3"); // 33 console.log("1" + 2 + "3"); // 123 console.log(1 + [2, 3]); // 12,3 console.log(1 + ""); // 1 console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.05 + 0.25); // 0.3 // boolean console.log(1 + true); // 2 console.log(1 + false); // 1 // string console.log("AlphaGL:" + null); // AlphaGL:null console.log("AlphaGL:" + undefined); // AlphaGL:undefined console.log("AlphaGL:" + Infinity); // AlphaGL:Infinity console.log("AlphaGL:" + NaN); // AlphaGL:NaN console.log("AlphaGL:" + false); // AlphaGL:false console.log("AlphaGL:" + true); // AlphaGL:true // infinity console.log(1 + Infinity); // Infinity console.log(Infinity + Infinity); // Infinity console.log(1 + (-Infinity)); // -Infinity console.log((-Infinity) + (-Infinity)); // -Infinity console.log(Infinity + (-Infinity)); // NaN // 0 console.log((+0) + (+0)); // 0 console.log((-0) + (-0)); // -0 console.log(0 + (-0)); // 0 // NaN console.log(1 + NaN); // NaN console.log(1 + null); // 1 console.log(1 + undefined); // NaN console.log(1 + (new Date())); // 1Mon May 25 2017 17:09:08 GMT+0800 (中國標準時間) console.log(1 + {name: "AlphaGL"}); // 1[object Object]
綜上,使用加法操做符能夠總結爲以下規則:
- 若是兩個操做數都是數值,則執行常規得加法計算。這裏須要注意浮點數的加法。
- 若是一個操做數爲字符串,則將另一個操做數也轉化成字符串類型,再執行字符串的拼接。
- 若是一個操做數是數值,另一個操做是Infinity,則加法結果爲Infinity。若是一個操做數是數值,另一個操做數是-Infinity,則加法結果爲-Infinity。若是是Infinity加-Infinity,則加法結果爲NaN。若是一個操做數是數值,另一個操做數是NaN,則加法結果爲NaN。
- 若是一個操做數是數值,另一個操做數是boolean,null類型,則先將boolean和null類型轉行成原始值,再執行加法運算。
- 若是一個操做數是數值,另一個操做數是對象,則會先調用對象的
valueOf
方法轉化成原始值,若是對象沒有valueOf
方法,則調用toString
方法。
(2)減法運算符(-)
減法的運算規則與加法相似,這裏就再也不詳細介紹了。
(3)乘法運算符(*)
console.log(2 * 3); // 6 console.log(-2 * -3); // 6 console.log(2 * -3); // -6 console.log(2 * Number.MAX_VALUE); // Infinity console.log(-2 * Number.MAX_VALUE); // -Infinity // NaN console.log(2 * NaN); // NaN console.log(-2 * NaN); // NaN console.log(0 * NaN); // NaN console.log(NaN * NaN); // NaN // Infinity console.log(2 * Infinity); // Infinity console.log(-2 * Infinity); // -Infinity console.log(-2 * -Infinity); // Infinity console.log(0 * Infinity); // NaN console.log(Infinity * Infinity); // Infinity console.log(-Infinity * -Infinity); // Infinity // undefined console.log(2 * undefined); // NaN console.log(0 * undefined); // NaN console.log(undefined * undefined); // NaN // boolean console.log(2 * false); // 0 console.log(2 * true); // 2 console.log(2 * "34"); // 68 console.log(2 * "AlphaGL"); // NaN console.log(2 * [3, 4]); // NaN console.log(2 * {name:"34"}); // NaN console.log(2 * new Date()); // 2992421935692
綜上,使用減法操做符能夠總結爲以下規則:
- 兩個正數或兩個負數相乘,結果爲正數。其它有一個操做數爲負數,那結果也爲負數。若是結果超出數值的表示範圍,則結果爲Infinity或-Infinity。
- 若是有一個操做數爲NaN或undefined,則結果爲NaN。
- 若是一個非0數值與Infinity或-Infinity相乘,則結果爲Infinity或-Infinity,符號取決於操做數的符號和Infinity仍是-Infinity。0與Infinity或-Infinity,則結果爲NaN。
- 若是一個操做數是數值,另一個操做數是boolean或者字符串,則先將該操做數轉化爲原始值,若是轉化後的值不是數值,則結果爲NaN,不然執行常規乘法運算。
- 若是一個操做數是數值,另一個操做數是對象,則結果爲NaN。若是是Date對象,則乘以基於當前到1970年1月1日起的毫米數。
(4)除法操做數(/)
除法的運算規則與乘法相似,一樣,這裏就再也不詳細介紹了。
(5)模(求餘)運算(%)
該運算符是求得除法運算後的餘數。
console.log(10 % 3); // 1 console.log(-10 % 3); // -1 console.log(10 % -3); // 1 console.log(10 % 3.14); // 0.5799999999999996
綜上,模運算規則以下:
- 模運算的結果的符號,與第一個操做數相同。模運算用於浮點數時,結果會有偏差。
(6)自增(++)與自減(--)
自增和自減有分爲前置和後置。
var x = 5; var y = ++x - 2 /* 等價於 * var x = 5; * x = x + 1; * var y = x - 2; */ console.log(x); // 6 console.log(y); // 4
var x = 5; var y = x++ - 2; /* 等價於 * var x = 5; * var y = x - 2; * x = x + 1; */ console.log(x); // 6 console.log(y); // 3
前置自增與後置自增的區別是,前置自增先執行自增,再執行後續的運算,後置自增是先執行運算,再執行自增。同理,自減原理也同樣,就不在贅述了。
(7)x op= y操做
這裏把+=
,-=
,*=
,/=
,%=
等複合運算統稱爲op=
,那麼:
x op= y
大多數狀況下等價於:
x = x op y
其中,下面這個表達式中x計算了兩次,在x含有反作用的表達式時,兩者就不等價了。
var c = [1, 2, 3]; var i = 0; c[i++] *= 2; console.log(c) // [ 2, 2, 3 ] var d = [1, 2, 3]; var j = 0; d[j++] = d[j++] * 2; console.log(d); // [ 4, 2, 3 ]
用來判斷一個操做數是否大於或小於或等於另一個操做數。
console.log(2 < 3); // true console.log("12" < 3); // false console.log("12" < "3"); // true console.log("Alpha" < "alpha"); // true console.log("AlphaGL" < "AlphagL"); // true console.log(2 < "AlphaGL"); // false console.log(2 < true); // false console.log(2 < undefined); // false console.log(2 < null); // false console.log(2 < NaN); // false console.log(false < true); // true console.log(2 < Infinity); // true console.log(2 < -Infinity); // false console.log(Infinity < Infinity); // false console.log(Infinity < Infinity + 1); // false console.log(0 <= 0); // true console.log(0 >= 0); // true console.log(12 == "12"); // true console.log(12 === "12"); // false console.log(12 !== "12"); // true console.log(undefined == 0); // false console.log(undefined == null); // true console.log(undefined == false); // false console.log(null == false); // false console.log(null == 0); // false console.log("" == 0); // true console.log(undefined == ""); // false console.log(2 != NaN); // true console.log(NaN == NaN); // false console.log(NaN != NaN); // true console.log(false == 0); // true console.log(true == 1); // true
綜上,關係運算符返回的都是boolean值,有如下規則:
- 若是比較的兩個操做數都是數值,則執行數值比較。若是隻有一個操做數是數值,則將另一個操做數轉換爲數值,再執行數值比較。
- 若是比較的兩個操做數都是字符串,則依次比較字符串每一個字符的Unicode值。
- 若是有一個操做數是NaN,則執行結果爲false,執行不相等操做時,執行結果爲true。
- null和undefined相等。但不能將null和undefined轉化爲其它任何值。
- 若是有一個操做數是對象,另一個操做數不是,則會調用對象的valueOf方法獲得原始值,再應用上面的規則。
- 當兩個操做數的值相同,類型也相同,而且都不是NaN時,則兩個操做數全等(===)。當比較的兩個操做數轉換爲相同類型後的值相等,則兩個操做數相等(==)。
(1)邏輯與(&&)
在boolean環境下當邏輯與的兩個操做數同時爲true時,結果才爲true,不然爲false。
console.log(new Date() && 2); // 2 console.log(2 && new Date()); // 2017-05-31T02:39:51.033Z console.log(false && new Date()); // false console.log(new Date() && new Date()); // 2017-05-31T02:39:51.035Z console.log(false && 0); // false console.log(true && 0); // 0 console.log(2 && 0); // 0 console.log(2 && ""); // "" console.log(2 && "AlphaGL"); // AlphaGL console.log(2 && null); // null console.log(2 && undefined); // undefined console.log(2 && NaN); // NaN console.log(2 && Infinity); // Infinity
綜上,邏輯與的使用規則能夠總結以下:
- 若是第一個操做數能轉換成false,則返回第一個操做數,不然返回第二個操做數。在boolean環境中使用時,兩個操做數結果都爲true時,返回true,不然返回false。
- 可以轉換爲false的值有,0,"",null,undefined。
短路操做:
在執行邏輯與操做時,當第一個操做數的結果爲false時,就不在執行第二個操做數的求值了。由於不管第二個操做數爲什麼值,其結果都不可能爲true。
function test(i) { if(i > 0) { return i; }else{ return -i; } } console.log(false && test(2)); // false console.log(true && test(2)); // 2
(2)邏輯或(||)
在boolean環境下當邏輯或的兩個操做數任意一個爲true時,結果都爲true。通常,可用來給變量設置默認值。
console.log(new Date() || 2); // 2017-05-31T02:46:51.732Z console.log(2 || new Date()); // 2 console.log(false || new Date()); // 2017-05-31T02:48:51.732Z console.log(new Date() || new Date()); // 2017-05-31T02:48:51.732Z console.log(false || 0); // 0 console.log(true || 0); // true console.log(2 || 0); // 2 console.log(2 || ""); // 2 console.log(2 || "AlphaGL"); // 2 console.log(2 || null); // 2 console.log(2 || undefined); // 2 console.log(2 || NaN); // 2 console.log(2 || Infinity); // 2
綜上,邏輯或的使用規則能夠總結以下:
- 若是第一個操做數能轉換成true,則返回第一個操做數,不然返回第二個操做數。在boolean環境中使用時,兩個操做數任意一個爲true時,返回true,不然返回false。
- 可以轉換爲false的值有,0,"",null,undefined。
短路操做:
在執行邏輯或操做時,當第一個操做數的結果爲true時,就不在執行第二個操做數的求值了。由於不管第二個操做數爲什麼值,其結果都不可能爲false。
function test(i) { if(i > 0) { return i; }else{ return -i; } } console.log(false || test(2)); // 2 console.log(true || test(2)); // true
(3)邏輯非(!)
不管操做數是什麼類型的數據,該操做都會返回一個boolean。邏輯非會先將操做數轉換爲一個boolean,再對齊求反。
console.log(!0); // true console.log(!""); // true console.log(!NaN); // true console.log(!null); // true console.log(!undefined); // true console.log(!Infinity); // false console.log(!2); // false console.log(!"AlphaGL"); // false console.log(!new Date()); // false
綜上,邏輯非的使用規則能夠總結以下:
- 若是操做數能轉換爲true的話,則返回false,不然返回false。
- 可以轉換爲false的值有,0,"",null,undefined。
位運算是比較低層次的運算,按內存中表示數值的位來操做數值。JavaScript中全部的數值都是以64位格式存儲,而位操做符是先將64位的值轉換成32位的整數,而後執行操做,最後再將結果轉換回64位。
對於有符號的整數,32中的前31位表示整數的值,第32位表示數值的符號,用0表示整數,1表示負數,所以第32位也叫符號位。其中,正數是以二進制格式存儲的,負數二進制補碼的形式存儲的。
(1)原碼、反碼和補碼
原碼,是該數值的符號位與絕對值的二進制表示。例如:
2[原碼]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[原碼]: 1000 0000 0000 0000 0000 0000 0000 0010
反碼,正數的反碼是其原碼。負數的反碼,是符號位不變,其他各位取反,即1變成0,0變成1。例如:
2[反碼]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[反碼]:1111 1111 1111 1111 1111 1111 1111 1101
補碼,正數的補碼是其原碼。負數的補碼,是其反碼加1。例如:
2[補碼]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[補碼]:1111 1111 1111 1111 1111 1111 1111 1110
(2)按位與(&)
按位於,是將兩個操做數的二進制位對齊,當兩個數值的位都爲1時,結果爲1,任意一個爲0,則結果爲0。
console.log(3 & 5); // 1 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 & = 0000 0000 0000 0000 0000 0000 0000 0001
(3)按位或(|)
按位或,是將兩個操做數的二進制位對齊,當兩個數值的位任意一個爲1時,結果爲1,兩個都爲0,則結果爲0。
console.log(3 | 5); // 7 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 | = 0000 0000 0000 0000 0000 0000 0000 0111
(4)按位非(~)
按位非,是獲得該數值的反碼。
console.log(~3); // -4 3 = 0000 0000 0000 0000 0000 0000 0000 0011 ~ = 1111 1111 1111 1111 1111 1111 1111 1100
(5)按位異或(^)
按位異或,是將兩個操做數的二進制位對齊,當兩個數值的位其中只有一個爲1時,結果爲1,兩個都爲0或都爲1,則結果爲0。
console.log(3 ^ 5); // 6 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 ^ = 0000 0000 0000 0000 0000 0000 0000 0110
(6)左移(<<)
左移,是將操做數的全部位移動指定的位數,右側多出的位用0填充。左移不影響操做數的符號位。
console.log(3 << 2); // 12 console.log(-3 << 2); // -12 3 = 0000 0000 0000 0000 0000 0000 0000 0011 << 2 = 0000 0000 0000 0000 0000 0000 0000 1100
(7)有符號右移(>>)
有符號右移,是將操做數的全部位移動指定的位數,並保留符號位。左側多出的位用0填充。
console.log(12 >> 2); // 3 console.log(-12 >> 2); // -3 12 = 0000 0000 0000 0000 0000 0000 0000 1100 >> 2 = 0000 0000 0000 0000 0000 0000 0000 0011
(8)無符號右移(>>>)
無符號右移,是將操做數的全部位移動指定的位數。對於正數,無符號右移與有符號右移結果相同,負數會以補碼的形式右移指定的位。
console.log(12 >>> 2); // 3 console.log(-12 >>> 2); // 1073741821
(1)賦值運算符(=)
賦值能夠和其餘運算符組合使用。例如:
var x = 3; console.log(x += 5); // 8
(2)逗號運算符(,)
逗號運算符,能夠再一條語句中執行多個操做。若是,逗號運算符用於賦值,則返回表達式中的最後一項。
var x = 2, y = 3, z = 5; var pos = (2, 3, 5); console.log(z); // 5 console.log(pos); // 5
(3)三目運算符(?=)
三目運算符,格式形如:
variable = boolean_expression ? true_value : false_value
當表達式boolean_expression的值位true時,則返回true_value的值,不然,返回false_value的值。
console.log(1 > 2 ? 1 + 2 : 1 - 2); // -1
在介紹數據類型的時候提到過,在JavaScript中對象是一組無序的鍵值對集合,相似其它語言的HashMap、Dictionary等數據結構。除數字、true、false、null、undefined和字符串外,全部的值都是對象。JavaScript內置了Object、Date、Array、Function、RegExp等對象。全部對象繼承Object對象。
對象的建立分爲兩種方式:
(1)使用new操做符,後面緊跟構造函數
var student = new Object(); // 等價於 var student = {}; student.name = "AlphaGL"; student.age = 18; student.print = function () { console.log("hello AlphaGL"); }
(2)使用對象字面量表示法。由若干名/值對中間用冒號分割,每對名/值對間用逗號分隔組成的映射表。
var student = { name : "AlphaGL", age : 18 print: function () { console.log("hello AlphaGL"); }, };
能夠經過點(.)或者中括號([])的方式獲取對象屬性的值。
(1)經過點(.)來獲取
var student = { name : "AlphaGL", age : 18 }; console.log("name = " + student.name); // name = AlphaGL
(2)經過中括號訪問屬性的值,中括號內能夠是變量且計算結果必須是字符串的表達式。若是屬性名包含回致使語法錯誤的字符,或者屬性名使用的是關鍵字或者保留字,也可使用中括號表示。
var name = "nick name"; student[name] = "AlphaGL"; // 等價於 student["nick name"] = "AlphaGL";
通常推薦使用點的方式去獲取對象屬性。
(1)hasOwnProperty()方法能夠檢測給定屬性存在於對象實例中時,則返回true。
function Student() { } Student.prototype.work = "game"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log(stu.hasOwnProperty("name")); // true console.log(stu.hasOwnProperty("work")) // false
(2)in操做符會訪問對象的給定屬性,不管該屬性是存在於實例中仍是原型中都返回true。
function Student() { } Student.prototype.work = "game"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log("name" in stu); // true console.log("work" in stu) // true
delete運算符能夠用來刪除對象的自有屬性,不會刪除原型的同名屬性,刪除不存在的屬性在對象上,delete將不會起做用,但仍會返回true。成功刪除的時候會返回true,不然返回false。
function Student() { }; Student.prototype.name = "hello"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log(delete stu.name); // true console.log(delete stu.name); // 什麼不作,一樣返回true console.log(stu.name); // hello
JavaScript中,數組算是最經常使用的類型。數組的大小能夠動態調整,每一項能夠保存任何類型的數據,起始項從0開始。還能夠實現堆棧,隊列等數據結構。
(1)數組的建立
使用Array構造函數建立。
var nums = new Aarray(3); var names = new Array("foo", "bar") var colors = Array("R", "G", "B")
使用數組字面量表示法。即便用中括號,並將每一個元素用逗號隔開。
var num = [1, 2, 3]; var names = ["foo", "bar"]; var params = [1.2, "ab", true]; var pos = [{x:1, y:2}, {x:3, y:4}];
(2)數組元素的訪問。
var a = ["AlphaGL", 18, true]; console.log(a[0]); // AlphaGL console.log(a[1]); // 18 console.log(a[2]); // true //indexOf返回在數組中能夠找到一個給定元素的第一個索引,若是不存在,則返回-1。相似的還有lastIndexOf方法。 console.log(a.indexOf("AlphaGL")); // 0 console.log(a.indexOf(true)); // 2 console.log(a.indexOf(18)); // 1 console.log(a.indexOf(2)); // -1 console.log(a.length); // 3 console.log(a[3]); // undefined。javascript數組下標從0開始。
可使用負數或非整數來索引數組。這時,數值將會轉換爲字符串,而該索引被看成對象的常規屬性。若是,使用了非負整數的字符串,則它會被看成數組索引訪問,而不是對象屬性訪問。
var a = ["AlphaGL", 18, true]; console.log(a[-1]); // undefined console.log(a[1.5]); // undefined console.log(a["1"]); // 18 console.log(a["2"]); // true
由此可知,數組的訪問只是對象訪問的一種特殊形式,當訪問不存在的屬性時,javascript也不會報錯,只會返回undefined值。所以,javascript中數組不存在數組越界的問題。
(3)數組元素的添加與刪除
添加元素:
var a = []; a[0] = "AlphaGL"; a.push(18, true); console.log(a); // [ 'AlphaGL', 18, true ]
刪除元素:
var a = ["AlphaGL", 18, true]; delete a[1]; console.log(a[1]); // undefined console.log(a.length); // 3 console.log(a.pop()); // true。從數組中刪除最後一個元素,並返回該元素的值 console.log(a.length); // 2 console.log(a.shift()) // AlphaGL。從數組中刪除第一個元素,並返回該元素的值 console.log(a.length); // 1 a[0] = undefined; console.log(a.length); // 1
var a = ["AlphaGL", 18, true]; a.length = 2; console.log(a[2]); // undefined a.length = 0; console.log(a[0]); // undefined
var a = ["AlphaGL", 18, true]; a.splice(2, 0, "haha"); // 從第2個元素開始,刪除0個元素,即添加元素haha console.log(a); // [ 'AlphaGL', 18, 'haha', true ] a.splice(1, 2); // 從第1個元素開始,刪除2個元素。 console.log(a); // [ 'AlphaGL', true ] a.splice(0, 1, "haha"); // 從第0個元素開始,刪除1個元素,並添加haha元素。 console.log(a); // [ 'haha', true ]
注:刪除數組元素與將數組元素賦值爲undefined值相似。使用delete不是修改數組的length屬性,也不會移動後繼元素位置。其它操做方法基本都會移動數組元素和改變數組length值。也能夠直接操做數組的length屬性來達到輸出元素的目的。push和pop方法提供了相似棧結構的操做。push和shift方法則提供了相似隊列結構的操做。splice有替換數組中任意數量的項的做用。
(4)數組的檢測
var a = ["AlphaGL", 18, true]; console.log(Array.isArray(a)); // true console.log(a instanceof Array); // true
注:當存在兩個以上的全局執行環境時,即存在兩個以上不一樣版本的Array構造函數,instanceof則只能在單一的全局執行環境有效。
(5)數組的排序
var a = [1, 11, 57, 7, 23]; a.sort(function (p1, p2) { // 使用比較函數來對數組元素進行排序。返回的值小於0,則p1放到p2位置以前;大於0則p1在p2以後;等於0則位置不變。 return p1 > p2; }); console.log(a); // [ 1, 7, 11, 23, 57 ]
var a = ["AlphaGL", 18, true]; a.reverse(); // 逆序數組。 console.log(a); // [ true, 18, 'AlphaGL' ]
(6)數組的遍歷與迭代
var a = [1, 11, 57, 7, 23]; var t1 = a.every(function (element, index, array) { return element % 2 != 0; }); var t2 = a.every(function (element, index, array) { return element > 10; }); console.log(t1); // true console.log(t2); // false
注:every方法會對數組中的每一項運行給定函數,若是該函數的每一項都返回true,則結果才爲true。
var a = [1, 11, 57, 7, 23]; var t1 = a.filter(function (element, index, array) { return element % 2 != 0; }); var t2 = a.filter(function (element, index, array) { return element > 10; }); console.log(t1); // [ 1, 11, 57, 7, 23 ] console.log(t2); // [ 11, 57, 23 ]
注:filter方法會對數組中的每一項運行給定的函數,並返回該函數會返回爲true的項組成的新數組。
var a = [1, 11, 57, 7, 23]; var t1 = a.forEach(function (element, index, array) { array[index] = element + 1; }); console.log(a); // [ 2, 12, 58, 8, 24 ]
注:forEach方法一樣會對數組中每一項運行給定的函數。該方法沒有返回值。
var a = [1, 11, 57, 7, 23]; var t1 = a.map(function (element, index, array) { if(element > 10) { return element + 1; } return element - 1; }); console.log(t1); // [ 0, 12, 58, 6, 24 ]
注:map方法會將每次運行給定的函數返回的值,組成一個新的數組。
var a = [1, 11, 57, 7, 23]; var t1 = a.some(function (element, index, array) { return element > 50; }); console.log(t1); // true
注:map方法一樣會對數組中的每一項都運行給定的函數,若是該函數的任一項結果爲true,則返回true。
(7)其它
固然,數組還有一些其它的用法和函數。這裏就不一一介紹了。感興趣的,能夠參考文末列舉的參考連接。
函數,簡單描述就是可重複調用屢次的功能模塊。在JavaScript中,每一個函數都是Function類型的實例,所以也同樣具備屬性和方法。函數名也是對象,能夠把函數看成值來使用,這樣就提供極大的靈活性。
在JavaScript中,函數的定義有以下幾種實現方式:
(1)function關鍵字+函數名+參數列表+花括號構成的語句塊,例如:
function foo(p1, p2) { return p1 + p2; } console.log(typeof(foo)); // function console.log(foo(3, 4)); // 7
(2)使用Function構造函數。通常,不推薦這種使用方法。
var foo = new Function("p1", "p2", "return p1 + p2"); console.log(foo(3, 4)); // 7
(3)函數表達式
// 聲明瞭一個匿名函數,並賦值給foo變量。 var foo = function(p1, p2) { return p1 + p2; } console.log(foo(3, 4)); // 7 // 函數表達式也能夠包含名稱 var bar = function sum(p) { if(p <= 1) { return 1; }else { return p + sum(p - 1); } } console.log(bar(5)); // 15 // 聲明即調用 var sum = function(p1, p2) { return p1 + p2; }(3, 4); console.log(sum); // 7
JavaScript中函數定義並未指定函數參數的類型,調用時也未對實參的值作類型檢查,一樣也不檢查參數個數。
function foo(p1, p2, p3) { return p2; } console.log(foo(1)); // undefined console.log(foo(1, 2)); // 2 console.log(foo(1, 2, 3)); // 2 console.log(foo(1, 2, 3, 4)); // 2
當形參與實參的個數不匹配時,少的參數將被置爲undefined,多的參數將被丟棄。
在函數內部,有個特殊的對象arguments。該對象用來保存函數參數,能夠像數組樣使用數字索引來訪問參數,一樣它也包含length屬性。但它並非真正的數組。另外,該對象還包含callee屬性,該屬性指向擁有這個arguments對象的函數。
function foo(p1, p2, p3) { console.log(arguments.length); // 3 console.log(arguments[0]); // 第一個參數,即:1 console.log(arguments[1]); // 第二個參數,即:2 console.log(arguments[2]); // 第三個參數,即:3 } foo(1, 2, 3);
使用arguments和callee:
function sum(p) { if (p <= 1) { return 1; }else { return p + arguments.callee(p -1); } } console.log(sum(5)); // 15
前面提到過,每一個函數都是Function類型的實例,所以也同樣具備屬性和方法。函數有如下比較經常使用的屬性。
function foo(p1, p2 , p3) { console.log(arguments.length); console.log(arguments.callee.length); } console.log(foo.name); // foo console.log(foo.length); // 3 foo(1, 2); // 2 3
由上可知:
foo.name:函數的名稱。
foo.length:形參的個數。
arguments.length:實參的個數。
arguments.callee.length:形參的個數。
閉包(closure)是函數型語言的一個重要的特性,許多高級特性都依賴閉包來實現。閉包,是建立在一個函數內部的函數,可以訪問函數內部的變量,並保存在內存中,記錄上次運行的結果,即保存了建立時的上下文環境信息。所以,能夠簡單總結爲:
閉包=函數內部建立的函數 + 該函數建立時的上下文環境信息
例如:
function counter() { var count = 0; return function() { return count++; } } var foo = counter(); console.log(foo()); // 0 console.log(foo()); // 1 console.log(foo()); // 2
閉包的這種機制,就實現面向對象的封裝提供了支持。將私有變量封裝在內部,提供外包接口函數,來訪問該變量。
構造函數
函數內部屬性
函數的做用域
reduce
前面提到過,JavaScript中全部的都是對象。在面向對象編程中,對象是類的實例,而類是具備相同屬性和行爲的一類對象的抽象和集合。例如:獅子對象是動物這一類型中的一個實例。面向對象編程有三大特性:封裝,繼承和多態。
前面提到過,使用new關鍵字調用構造函數能夠建立一個新對象。
function Student(name, age) { this.name = name; this.age = age; this.setName = function(n) { this.name = n; } this.getName = function() { return this.name; } } var student = new Student("張三", 18); student.setName("李四"); console.log(student.getName()); // 李四
其中,this關鍵字指向了,當前要建立的對象。
每一個對象都有一個私有屬性(prototype)原型,該屬性指向該對象的原型對象。能夠理解爲其餘編程語言中的,指向基類或者父類的做用。固然,該原型對象一樣有一個本身的prototype,層層向上直到該對象的原型爲null。null沒有原型。JavaScript中幾乎全部的對象都是位於原型鏈頂端的Object的實例,一樣能夠理解爲,都是Object的子類。所以,使用原型對象可讓全部對象實例共享它所包含的屬性和方法。
function Student(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } var student1 = new Student("張三", 18); var student2 = new Student("李四", 18); console.log(student1.getName == student2.getName); // false
上面,建立兩個不一樣的對象實例,getName實現了相同的功能,卻每一個對象中都保留了一份,形成沒必要要的浪費。這就須要經過原型prototype來解決此問題了。
function Student(name, age) { this.name = name; this.age = age; Student.prototype.getName = function() { return this.name; } } Student.prototype.country = "china"; var student1 = new Student("張三", 18); var student2 = new Student("李四", 18); console.log(student1.getName == student2.getName); // true console.log(student1.country); // china console.log(student2.country); // china
function A() { } A.prototype.name = "小明"; A.prototype.age = 18; A.prototype.country = "china"; function B() { } B.prototype = new A(); B.prototype.name = "小李"; B.prototype.age = 20; function C() { } C.prototype = new B(); var c = new C(); c.name = "小趙"; c.country = "shanghai"; console.log(c.country); // shanghai console.log(c.age); // 20 console.log(c.name); // 小趙
當訪問對象的某個屬性時,會根據給定的屬性名稱來查找。若是,在該對象的實例中找到該屬性,則返回屬性的值;不然,則繼續查找該對象的原型對象,在原型對象中查找該屬性,依次層層向上搜索,直到搜索到原型鏈的末尾。所以,對象的屬性會覆蓋同名的該對象的原型對象的同名屬性。
function A() { } function B() { } var a1 = new A(); console.log(a1 instanceof A); // true console.log(a1 instanceof B); // false console.log(A.prototype.isPrototypeOf(a1)); // true console.log(B.prototype.isPrototypeOf(a1)); // false A.prototype = {}; var a2 = new A(); console.log(a1 instanceof A); // false console.log(a2 instanceof A); // true console.log(A.prototype.isPrototypeOf(a1)); // false console.log(A.prototype.isPrototypeOf(a2)); // true B.prototype = new A(); var a3 = new B(); console.log(a3 instanceof A); // true console.log(a3 instanceof B); // true console.log(B.prototype.isPrototypeOf(a3)); // true console.log(A.prototype.isPrototypeOf(a3)); // true
經過以上實例能夠總結以下:
object instanceof constructor
運算符,用來檢測 constructor.prototype是否存在於參數object的原型鏈。雖然,右操做數是構造函數,但實際上檢測了對象的繼承關係,而不是檢測建立對象的構造函數。
prototypeObj.isPrototypeOf(object)
檢查一個對象是否存在於另外一個對象的原型鏈上。能夠理解爲object對象是否繼承自prototypeObj.prototype。