JavaScript學習(1)之JavaScript基礎

JavaScript學習(1)之JavaScript基礎

因爲工做緣由,開發語言逐漸以JavaScript爲主,因此,抽空學習了下JavaScript語法。等現階段的工做穩定以後,陸續會分享下本身在學習和開發過程當中的一些經驗總結。本着"技多不壓身"的原則以及對各類編程語言的熱愛,雖然筆者一直從事遊戲開發工做,也愉快而又義無反顧的加入了JavaScript陣營。javascript

一、JavaScript概述以應用範圍

1.1 JavaScript概述

首先,JavaScript是一門動態類型的編程語言。支持面向對象、函數式等編程範式。同時,它也是運行在宿主環境下的輕量級的腳本語言,例如:瀏覽器,JavaScript代碼可嵌入到HTML頁面中。固然,也有應用基於Node.js的服務器環境。能夠說它是主流瀏覽器都支持的腳本語言,這也造就了JavaScript在PC、手機、平板等環境處理與網頁交互時的自然優點。隨着HTML5的推廣與普遍應用,出現了大量基於JavaScript的跨平臺框架和遊戲引擎,是通往全棧開發很值得學習的一門編程語言。正如在編程語言界流行着「世界終將是JS的」的槽點,足以可見JavaScript的強大。前端

1.2 JavaScript應用範圍

web前端:最近比較火的Vue.js、React、 Angular等等前端框架層出不窮。
手機app:React Native、PhoneGap、Weex等跨平臺移動框架等,以及微信小程序等。
遊戲引擎:Cocos2d-js、Unity、egret等引擎都支持JavaScript腳本語言。
服務器:pomelo、Node.js等。java

二、基本概念

2.1 語法

JavaScript語法中大量借鑑了C和Java、Perl等語言的語法,所以熟悉這些語言的人再來學習JavaScript會感受似曾相識。但JavaScript與Java是兩回事,JavaScript徹底是蹭了當年Java的熱度,來推進本身。web

一個完整的JavaScript實現由下面三個不一樣的部分組成:express

  • 核心(ECMAScript)
  • 文檔對象模型(DOM)
  • 瀏覽器對象模型(BOM)

ECMAScript:
定義了語言的基礎,規定並對該語言在語法、類型、語句、關鍵字、保留字、操做符、對象等方面做了具體描述,並不包含輸入和輸出。編程

DOM:
文檔對象模型(Document Object Model)是XML和HTML的編程接口。DOM會把整個頁面映射爲一個多層節點結構樹,程序能夠對結構樹進行添加、刪除、替換或修改任何節點。小程序

BOM:
瀏覽器對象模型(Browser Object Model)支持訪問和操做瀏覽器窗口。微信小程序

注:如下描述就不區分ECMAScript和JavaScript,也不簡稱js,均統稱JavaScript。api

2.2 標識符

這裏的標識符,統指變量名、函數名、屬性名以及函數參數的名字。那麼,標識符的命名有如下規則:(這些規則跟C、Java等語言相似)數組

  • 第一個字符必須是一個字母、下劃線_或者美圓符號$
  • 其餘字符則能夠是字母、數字、下劃線和美圓符號。
  • 不能使用關鍵字、保留字來定義標識符。例如:if、for等。

注:在JavaScript中,變量名、函數名甚至操做符等是區分大小寫的。即name和Name表明的是兩個不一樣的變量。

2.3 註釋

單行註釋使用//。例如:

// 這是一個單行註釋

多行註釋使用/***/結構。例如:

/*
 * 這是一個多行註釋
 */

2.4 嚴格模式

啓用嚴格模式,會對一些不肯定的行爲或不安全的操做拋出錯誤。在腳本的頂部添加以下字符串:

"use strict";

也能夠添加到函數內部,來指定該函數在嚴格模式下執行。

2.5 語句

JavaScript中每條語句都是以分號結尾,但分號不是必需的。但建議是不要省略分號,來避免如不完整的輸入等問題。語句塊用花括號包含。

2.6 變量

可使用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。

3.1 Number

(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

3.2 String

(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文檔。

3.3 Boolean

該類型只有兩個字面值:true和false。須要強調的是,true不必定是1,false也不必定是0。其它類型轉換成Boolean類型時,有一下規則:

  • Boolean
    true轉換成true,false轉換成false。
  • String
    任何非空字符串轉換成true,空字符串("")轉換成false。
  • Number
    任何非零數字值(包括無窮大)轉換成true,0和NaN轉換成false。
  • Object
    任何對象轉換成true,null轉換成false。
  • Undefined
    undefined轉換成false。
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來判斷。

3.4 Object

對象是JavaScript中重要的數據類型,除數字、true、false、null、undefined和字符串外,全部的值都是對象。在JavaScript中對象是一組無序的鍵值對集合,相似其它語言的HashMap、Dictionary等數據結構。下面會有單獨一小節來專門講述下對象。

3.5 undefined

在使用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。

3.6 null

與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

3.7 typeof、instanceof與Object.prototype.toString

因爲JavaScript是動態類型的,所以就須要一種手段來檢測給定的變量的具體數據類型。

(1)typeof
typeof操做符返回的值是如下某個字符串:

  • "undefined"
  • "boolean"
  • "string"
  • "number"
  • "object"
  • "function"
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:下文會討論。

四、流程控制與運算符

4.1 流程控制

流程控制主要有條件語句和循環語句。條件語句又分爲:if語句,if-else語句,switch語句等。循環語句分爲:while循環,for循環,do-while循環。

4.1.1 條件語句

(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)也能夠是變量、表達式,不必定是常量。這點與其餘有的語言不一樣,只能使用數值。

4.1.2 循環語句

(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:跳出當前循環,繼續執行下次循環。

4.2 運算符

JavaScript中運算符大致可分爲,算術運算符、關係運算符、邏輯運算符以、位運算符以及其它操做符等,固然也包括運算符重載。

  • 算術運算符:+-*/%++--+=-=*=/=%=等。
  • 關係運算符:<><=>======!=!==
  • 邏輯運算符:&&||!
  • 位運算符:~&|^<<>>>>>
  • 其餘操做符:=,?=
4.2.1 算術運算符

(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 ]
4.2.2 關係運算符

用來判斷一個操做數是否大於或小於或等於另一個操做數。

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時,則兩個操做數全等(===)。當比較的兩個操做數轉換爲相同類型後的值相等,則兩個操做數相等(==)。
4.2.3 邏輯運算符

(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。
4.2.4 位運算符

位運算是比較低層次的運算,按內存中表示數值的位來操做數值。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
4.2.5 其它運算符

(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對象。

5.1 對象的建立

對象的建立分爲兩種方式:
(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");
    },
};

5.2 讀取屬性

能夠經過點(.)或者中括號([])的方式獲取對象屬性的值。

(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";

通常推薦使用點的方式去獲取對象屬性。

5.3 檢測屬性

(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

5.4 刪除屬性

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

5.5 Array對象

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類型的實例,所以也同樣具備屬性和方法。函數名也是對象,能夠把函數看成值來使用,這樣就提供極大的靈活性。

6.1 函數的定義

在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

6.2 函數的參數與內部屬性

JavaScript中函數定義並未指定函數參數的類型,調用時也未對實參的值作類型檢查,一樣也不檢查參數個數。

6.2.1 函數的參數
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,多的參數將被丟棄。

6.2.2 函數的內部屬性

在函數內部,有個特殊的對象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

6.3 函數的屬性

前面提到過,每一個函數都是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:形參的個數。

6.4 閉包

閉包(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中全部的都是對象。在面向對象編程中,對象是類的實例,而類是具備相同屬性和行爲的一類對象的抽象和集合。例如:獅子對象是動物這一類型中的一個實例。面向對象編程有三大特性:封裝,繼承和多態。

7.1 構造函數

前面提到過,使用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關鍵字指向了,當前要建立的對象。

7.2 原型與繼承

每一個對象都有一個私有屬性(prototype)原型,該屬性指向該對象的原型對象。能夠理解爲其餘編程語言中的,指向基類或者父類的做用。固然,該原型對象一樣有一個本身的prototype,層層向上直到該對象的原型爲null。null沒有原型。JavaScript中幾乎全部的對象都是位於原型鏈頂端的Object的實例,一樣能夠理解爲,都是Object的子類。所以,使用原型對象可讓全部對象實例共享它所包含的屬性和方法。

7.2.1 原型的使用
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
7.2.2 訪問屬性規則
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);            // 小趙

當訪問對象的某個屬性時,會根據給定的屬性名稱來查找。若是,在該對象的實例中找到該屬性,則返回屬性的值;不然,則繼續查找該對象的原型對象,在原型對象中查找該屬性,依次層層向上搜索,直到搜索到原型鏈的末尾。所以,對象的屬性會覆蓋同名的該對象的原型對象的同名屬性。

7.2.3 isPrototypeOf與instanceof
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。

參考:

MDN JavaScript教程

相關文章
相關標籤/搜索