JavaScript學習(1)之JavaScript基礎

JavaScript學習(1)之JavaScript基礎

由于工作原因,開發語言逐漸以JavaScript為主,所以,抽空學習了下JavaScript語法。等現階段的工作穩定之后,陸續會分享下自己在學習和開發過程中的一些經驗總結。本著"技多不壓身"的原則以及對各種編程語言的熱愛,雖然筆者一直從事游戲開發工作,也愉快而又義無反顧的加入了JavaScript陣營。

1、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等。

2、基本概念

2.1 語法

JavaScript語法中大量借鑒了C和Java、Perl等語言的語法,因此熟悉這些語言的人再來學習JavaScript會感覺似曾相識。但JavaScript與Java是兩回事,JavaScript完全是蹭了當年Java的熱度,來推動自己。

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

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

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

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

BOM:
瀏覽器對象模型(Browser Object Model)支持訪問和操作瀏覽器窗口。

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

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就成了全局變量。一般不推薦這種做法。全局變量難以維護,而且容易導致結構混亂。

3、數據類型

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、流程控制與運算符

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

5、對象

在介紹數據類型的時候提到過,在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)其它
當然,數組還有一些其它的用法和函數。這里就不一一介紹了。感興趣的,可以參考文末列舉的參考鏈接。

6、函數

函數,簡單描述就是可重復調用多次的功能模塊。在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

7、面向對象

前面提到過,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教程

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • FreeCodeCamp - Basic JavaScript 寫在前面: 我曾經在進谷前刷過這一套題,不過當時只...
    付林恒閱讀 16,474評論 5 28
  • 標簽: 我的筆記 ---學習資料:http://javascript.ruanyifeng.com/ 1. 導論 ...
    暗夜的怒吼閱讀 834評論 0 1
  • 對于生活,你隨意就覺輕松,你嚴肅就覺就緊張。我們總是時不時的在要求自己,規劃自己,但總有“某種”原因半途而廢...
    流年_9df3閱讀 122評論 0 0
  • 天色漸晚,馬上下班了。 秋の限定柄が出てきたよ、コスモス(秋桜)。一聽到コスモス就想到大阪入管,(這么嚴肅的一個地...
    WoodSage閱讀 454評論 0 0
  • ??2017年8月16日 周三 晴 中午正吃外賣時,小歡發來一條微信。我打開一看,是一張照片。照片中的她正站在馬路...
    看笑話兒不嫌事大閱讀 299評論 2 1