JavaScript 編程:2.基本概念

語法

  • ECMAScript 中的一切(變量、函數名和操作符)都區分大小寫。
  • JavaScript 語句結尾的分號(;)不是必須的,但是建議任何時候都不要省略它。
  • 建議始終在控制語句中使用代碼塊,即使代碼塊中只有一條語句。

標識符

標識符的定義:變量、函數、屬性的名字,或者函數的參數。

標識符命名規則:

  1. 第一個字符,必須是任意 Unicode 字母(包括英文字母和其他語言的字母)、美元符號($)或下劃線(_)。
  2. 第二個字符及后面的字符,除了 Unicode 字母、美元符號和下劃線,還可以用數字 0-9。

最佳實踐:ECMAScript 標識符采用駝峰大小寫格式。

注釋

單行注釋:

// 單行注釋內容

塊級注釋/多行注釋:

/*
 * 這是一個多行
 * (塊級)注釋
 */

嚴格模式

嚴格模式為 JavaScript 定義了一種不同的解析與執行模型。在嚴格模式中,ECMAScript 3 中一些不確定的行為將得到處理,而且對某些不安全的操作也會拋出錯誤。

  1. 整個腳本啟用嚴格模式,在文件頂部添加:
"use strict" 

該語法是一個編譯指示,用于告訴支持的 JavaScript 引擎切換到嚴格模式。

  1. 函數體內啟用嚴格模式:
function doSomething() {
    "use strict";
    // 函數體
}

關鍵字和保留字

不要使用關鍵字和保留字作為標識符、屬性名或者函數名。

關鍵字:

break
case
catch
continue
default
delete
debugger (第 5 版新增)
do
else
finally
for
function
if
in
instanceof
new
return
switch
this
throw
try
typeof
var
void
while
with

保留字:

abstract
boolean
byte
char
class
const
debugger
double
enum
export
extends
final
float
goto
implements
import
int
interface
long
native
package
private
protected
public
short
static
super
synchronized
throws
transient
volatile
let
yield

變量

ECMAScript 的變量是松散類型/動態類型的,松散類型可以保存任何類型的數據。換句話說,每個變量僅僅是一個用于保存值的占位符而已。定義變量時要使用 var 操作符。

也就是說,變量的類型沒有限制,變量可以隨時更改類型。

  • 使用 var 操作符定義的變量將成為定義該變量的作用域中的局部變量。
function test() {
  var message = "hello"; // 局部變量
}
test();
console.log(message); // 錯誤!函數退出后,變量已經被銷毀了!
  • 省略 var 操作符會創建全局變量。
/*
* 不推薦!
* 嚴格模式下會報錯:ReferenceError: message is not defined
*/
function test() {
  message = "hello"; // 全局變量
}
test();
console.log(message); // "hello"
  • 可以使用一條語句定義多個變量,用逗號分隔開。
// 代碼中的換行和變量縮進不是必須的,但這樣做可以提高代碼可讀性。
var message = "hello",
      found = false,
        age = 24;

變量提升

JavaScript 引擎的工作方式是:先解析代碼,獲取所有被聲明的變量,然后再一行一行地運行。這樣的結果就是:所有的變量的聲明語句,都會被提升到代碼的頭部,這就叫做變量提升(hoisting)。

console.log(a); // undefined
var a = 1;

上面代碼首先使用 console.log 方法,在控制臺(console)顯示變量 a 的值。這時變量 a 還沒有聲明和賦值,所以這是一種錯誤的做法,但是實際上不會報錯。因為存在變量提升機制,真正運行的是下面的代碼:

var a;
console.log(a); // undefined
a = 1;

最后的結果是顯示 undefined,表示變量 a 已聲明,但還未賦值。

數據類型

ECMAScript 數據類型

Object 本質上是由一組無序的鍵值對組成的。另外值得注意的是,JavaScript 中的函數是對象,函數名只是指向該對象的指針。

typeof 操作符

typeof 操作符:檢測并返回給定變量的數據類型。

typeof 操作符返回的字符串 描述
"undefined" 未定義
"boolean" 布爾值
"string" 字符串
"number" 數值
"object" 對象或 null
"function" 函數

typeof 操作符檢測變量的數據類型:

var message = 'some string';
console.log(typeof message); // string

console.log(typeof 95); // number

// undefined 表示 value 變量沒有聲明,或者聲明了但是沒有賦初始化值。
console.log(typeof value); // undefined

?? 用 typeof 檢查 null 類型返回 object

typeof null // "object"

null 的類型是 object,這是由于歷史原因造成的。1995 年的 JavaScript 語言第一版,只設計了五種數據類型(對象、整數、浮點數、字符串和布爾值),沒考慮 null,只把它當作 object 的一種特殊值。后來 null 獨立出來,作為一種單獨的數據類型,為了兼容以前的代碼,typeof null 返回 object 就沒法改變了。

typeof 是一個操作符而不是函數

var message = "some string";
console.log(typeof message); // string
console.log(typeof (message)); // string
  • typeof 操作符的操作數可以是變量(message),也可以是數值字面量。
  • typeof 是一個操作符而不是函數,所以圓括號盡管可以使用,但不是必需的。

typeofinstanceof 的使用異同

  • typeof 操作符是確定一個變量是字符串、數值、布爾值,還是 undefined 的最佳工具(檢測基本類型)。
  • 如果變量是給定引用類型(根據它的原型鏈來識別)的實例,instanceof 操作符返回 true,而所有基本類型返回 false(檢測引用類型)。

確定一個值是哪種基本類型可以使用 typeof 操作符;
確定一個值是哪種引用類型可以使用 instanceof 操作符。

Undefined 類型

  • 在使用 var 聲明變量但未對其進行初始化時,這個變量的默認值就是 undefined
  • undefined 是一個特殊的值,表示“未定義”。
  • 未初始化未聲明的變量執行 typeof 操作符都會返回 undefined。

未聲明變量&未初始化變量的區別

var message; // 變量聲明后,如果未賦初始值,則默認值為 undefined

alert(message); // "undefined"
alert(age);     // 沒有聲明的變量直接使用會產生錯誤!

// ?? 沒有聲明過的變量只能使用 typeof 操作符檢測其數據類型
alert(typeof message); // "undefined"
alert(typeof age)      // "undefined"

undefined 常用場景

// 變量聲明了,但沒有賦值
var i;
i // undefined

// 調用函數時,應該提供的參數沒有提供,該參數等于 undefined
function f(x) {
  return x;
}
f() // undefined

// 對象沒有賦值的屬性
var  o = new Object();
o.p // undefined

// 函數沒有返回值時,默認返回 undefined
function f() {}
f() // undefined

??
即便未初始化的變量會自動被賦予 undefined 值,但顯式地初始化變量依然是明智的選擇。如果能夠做到這一點,那么當 typeoft 操作符返回 undefined 值時,我們就知道被檢測的變量還沒有被聲明,而不是尚未初始化。

Null 類型

  • Null 類型只有一個值 null。null 值表示一個空對象指針。因此,typeof null 會返回 "object"。
  • null 表示“空值”,即該處的值現在為空。調用函數時,某個參數未設置任何值,這時就可以傳入 null,表示該參數為空。

空對象指針&未經初始化變量

// 不推薦使用 undefined 顯式初始化變量,因為它默認就是 undefined。
// 而且,字面值 undefined 的主要目的是用于比較。
var message = undefined; // ?
var message;             // ?

// 但是如果定義的變量是對象,那么最好將該變量初始化為 null 而不是其他值。
var car = null;    // ?
alert(typeof car); // "object"

// 實際上,undefined 值派生自 null 值
alert(null == undefined); // true

Null 類型 & Undefined 類型的區別

  • Null 類型只有一個值 null,而 Undefined 類型只有一個值 undefined;
  • null 是一個表示 “空” 的對象,轉為數值時為 0;
  • undefined 是一個表示 "此處無定義" 的原始值,轉為數值時為 NaN。

參考:InfoQ:如何在 JavaScript 中處理 null 和 undefined?

Boolean 類型

  • Boolean 類型只有兩個字面值:truefalse。
  • 通常意義上來說,0 為假,非 0 為真。但是在 Boolean 類型中,true 不一定等于 1,而 false 也不一定等于 0
  • truefalse 區分大小寫。

轉型函數:Boolean()

ECMAScript 中所有類型的值都有與這兩個 Boolean 類型的值等價的值。要將一個值轉換為其對應的 Boolean 值,可以調用轉型函數 Boolean()

數據類型 轉換為 true 的值 轉換為 false 的值
Boolean true false
String 任何非空字符串 “” 或者 ''(空字符串)
Number 任何非零數字值(包括無窮大) 0 和 NaN
Object 任何對象 null
Undefined —— undefined

注意:空數組([])和空對象({})對應的布爾值,都是 true。

示例:將一個值轉換為其對應的 Boolean 值:

var message = "Hello World";
var messageAsBoolean = Boolean(message);
console.log(messageAsBoolean); // true

var message = null;
var messageAsBoolean = Boolean(message);
console.log(messageAsBoolean); // false

Number 類型

  • 與其他語言不同,ECMScript 沒有為整數和浮點數值分別定義不同的數據類型,Number 類型可用于表示所有數值。
  • Number 類型使用 IEEE754 格式來表示整數和浮點數值。
  • Number 類型支持的數值字面量格式:十進制、八進制、十六進制。
  • Number 類型中,八進制在嚴格模式下無效。
// 十進制
var intNum = 55; 

// 八進制,第一位必須為 0
var octalNum1 = 070 // 八進制的56
// 如果八進制的字面值超出了范圍(0~7),前導零將會被忽略,后面的數值將被當作十進制數值解析。
var octNum2 = 079; // 79
var octNum3 = 08; // 8

// 十六進制,前兩位必須為 0x
var hexNum =0xA // 十六進制的10

// 科學計數法(e 表示法)
var float Num = 3.125e7; // 31250000
var float Num = 3.125e-3; // 0.003125

// 自動轉換為科學記數法的情況
// 【1】小數點前的數字多于 21 位。
1234567890123456789012
// 1.2345678901234568e+21

123456789012345678901
// 123456789012345680000

// 【2】小數點后緊跟6個以上的零,就自動轉為科學計數法
0.0000003 // 3e-7
// 否則,就保持原來的字面形式
0.000003 // 0.000003

浮點數值

  • 浮點數值中必須包含一個小數點,并且小數點后面必須至少有一位數字。
  • 由于保存浮點數值需要的內存空間是保存整數值的兩倍,因此 ECMAScript 會不失時機地將浮點數值轉換為整數值。
var floatNum1 = 1.1; // 浮點數 1.1
var floatNum2 = 1.;  // 小數點后面沒有數字,自動解析為 1
var floatNum3 = 10.0 // 整數,自動解析為 10
  • 浮點數值的最高精度是17位小數,但在進行算術計算時其精確度遠遠不如整數。
  • 浮點數值計算會產生舍入誤差問題,因此,永遠不要測試某個特定的浮點數值
0.1 + 0.2 === 0.3 // false
// 實際上,0.1 + 0.2 = 0.30000000004

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

數值范圍

  • JavaScript 能夠表示的數值范圍為 21024 到 2-1023(開區間),超出這個范圍的數無法表示。
(2^{1024}, 2^{-1023})
  • 如果某次計算的結果超出了 JavaScript 數值范圍(Number.MIN_VALUE, Number.MAX_VALUE),那么這個數值會被自動轉換為 Infinity 值。而 Infinity 值不是能夠參與計算的數值。

?????? isFinite():在參數位于最小與最大數值之間會返回 true。

JavaScript 內部,所有數字都是以 64 位浮點數形式儲存,即使整數也是如此。所以,11.0 是相同的,是同一個數。

1 === 1.0 // true

這就是說,JavaScript 語言的底層根本沒有整數,所有數字都是小數(64 位浮點數)。容易造成混淆的是,某些運算只有整數才能完成,此時 JavaScript 會自動把 64 位浮點數,轉成 32 位整數,然后再進行運算。

JavaScript 提供的有效數字最長為 53 個二進制位。精度最多只能到 53 個二進制位,這意味著,絕對值小于 2 的 53 次方的整數,即 - 253 到 253,都可以精確表示。

所以,簡單的法則就是:JavaScript 對15位的十進制數都可以精確處理。

Math.pow(2, 53)
// 輸出:9007199254740992

// 多出的三個有效數字(最后三位的 111)都會無法保存,變成 0。
9007199254740992111
// 9007199254740992000

NaN

  • NaN(Not a Number),非數值,表示 本來要返回數值的操作數未返回數值的情況(這樣就不會拋出錯誤了)。
  • 在 ECMAScript 中,任何數值除以 0 會返回 NaN,因此不會影響其他代碼的執行。
  • 任何涉及 NaN 的操作都會返回 NaN。
  • NaN 與任何值都不相等,包括 NaN 本身。

?????? isNaN():確定傳入參數是否“不是數值”。無法被轉換為數值的值會返回 true。

// 不是數值/不能轉換為數值,返回 true
console.log(isNaN(NaN)); // true
console.log(isNaN(10));  // false(10是一個數值)
console.log(isNaN("10")); // false (可以被轉換為數值10)
console.log(isNaN("blue")); // true (不能被轉換為數值)
console.log(isNaN(true)); // false (可以被轉換為數值1)
console.log(isNaN("true")); // true

// 對于空數組和只有一個數值成員的數組,isNaN 返回 false。
// 因為內部會默認先用 Number() 函數進行一次轉換再作判斷。
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false

使用 isNaN 之前,最好判斷一下數據類型:

function myIsNaN(value) {
  return typeof value === 'number' && isNaN(value);
}

NaN 不是獨立的數據類型,而是一個特殊數值,它的數據類型依然屬于 Number,使用 typeof 運算符可以看得很清楚。

typeof NaN // 'number'
NaN === NaN // false
Boolean(NaN) // false

isNaN() 也適用于對象。在基于對象調用 isNaN() 函數時,會首先調用對象的 valueOf() 方法,然后確定該方法返回的值是否可以轉換為數值。如果不能,則基于這個返回值再調用 toString() 方法,再測試返回值。

數值轉換

  • Number() 可以用于任何數據類型。
  • parseInt()、parseFloat() 專門用于把字符串轉換成數值。

1. Number() ,任何數據類型 —> 數值

// Boolean
var num1 = Number(true); // 1
var num2 = Number(false); // 0

// 數字 —> 數字

// null -> 0
var num4 = Number(null); // 0

// undefined -> NaN
var num5 = Number(undefined); // NaN

// 字符串
var num6 = Number("Hello world!"); // NaN
var num7 = Number("000011"); // 11
var num8 = Number(""); // 空字符串返回 0

parseInt('42 cats') // 42
Number('42 cats') // NaN

// 對象 ——> 字符串 ——> 數字
// 1.自動調用對象的 valueOf() 方法,再轉為數值;
// 2.自動調用對象的 toString() 方法,再轉為數值;

2. parseInt(),字符串 —> 整數

在處理整數的時候更常用的是 parseInt() 函數。

parseInt() 函數在轉換字符串時,更多的是看其是否符合數值模式。它會忽略字符串前面的空格,直至找到第一個非空格字符。
如果第一個字符不是數字字符或者負號,parseInt() 就會返回 NaN;也就是說,用 parseInt() 轉換空字符串會返回 NaNNumber() 對空字符返回 0)。
如果第一個字符是數字字符,parseInt()會繼續解析第二個字符,直到解析完所有后續字符或者遇到了一個非數字字符。

// 如果字符串頭部有空格,空格會被自動去除。
parseInt('   81') // 81

// 轉換字符串
var num1 = parseInt("1234abcd");   // 1234
var num2 = parseInt("1a2b3c4d5e"); // 1

// parseInt() 會忽略小數點,因為小數點是非數字字符。
var num3 = parseInt("22.5");       // 22
var num4 = parseInt("22.34.5");     // 22

// parseInt() 處理空字符串返回 NaN
var num5 = parseInt(""); // NaN
var num6 = Number(""); // 0

// 如果字符串以 0x 或 0X 開頭,parseInt 會將其按照十六進制數解析。
parseInt('0x10') // 16

// 如果字符串以 0 開頭,將其按照 10 進制解析。
parseInt('011') // 11

// 指定基數(即多少進制)
var num1 = parseInt("10", 2);         //2 – parsed as binary
var num2 = parseInt("10", 8);         //8 – parsed as octal
var num3 = parseInt("10", 10);        //10 – parsed as decimal
var num4 = parseInt("10", 16);        //16 – parsed as hexadecimal

// 對于那些會自動轉為科學計數法的數字,parseInt 會將科學計數法的表示方法視為字符串,因此導致一些奇怪的結果。
parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1

parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8

// 科學計數法應該使用 parseFloat()
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14

在 ECMAScript 5 JavaScript 引擎中,parseInt() 已經不具有解析八進制值的能力,因此前導的零會被認為無效,從而將這個值當成"70",結果就得到十進制的 70。在 ECMAScript 5 中,即使是在非嚴格模式下也會如此。

var num = parseInt("070"); // 70

為了避免錯誤的解析,建議無論在什么情況下都明確指定基數

var num = parseInt("070", 8); // 56

多數情況下,我們要解析的都是十進制數值,因此始終將 10 作為第二個參數是非常必要的。

3. parseFloat(),字符串 —> 浮點數

var num1 = parseFloat("12345blue"); // 12345
var num2 = parseFloat("0xA");       // 0
var num3 = parseFloat("22.5");      // 22.5

// 字符串中的第一個小數點是有效的,而第二個小數點是無效的。
var num4 = parseFloat("22.34.5");   // 22.34
var num5 = parseFloat("0980.5");    // 980.5
var num6 = parseFloat("3.125e7");   // 31250000

// parseFloat 方法會自動過濾字符串前導的空格。
parseFloat('\t\v\r12.34\n ') // 12.34

// 如果參數不是字符串,或者字符串的第一個字符不能轉化為浮點數,則返回 NaN。
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN
  • parseFloat()parseInt() 的區別:parseFloat() 中字符串中的第一個小數點是有效的。
  • parseFloat()parseInt() 的第二個區別: parseFloat() 始終都會忽略前導零。
  • parseFloat() 只解析十進制值,因此十六進制格式的字符串則始終會被轉換成 0。
  • 如果字符串包含的是一個可解析為整數的數(沒有小數點,或者小數點后都是零),parseFloat() 會返回整數。

總結

  • 比較兩個數字是否相等(在指定的誤差范圍內):Number.EPSILON
  • 整數的安全范圍:(Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER
  • 判斷一個值是否位于最小數值與最大數值之間:isFinite()
  • 檢測一個值是否是整數:Number.isInterger()
  • 檢測一個值是否是安全的整數:Number.isSafeInterger()
  • 判斷一個值是否不是一個數字、或是一個無效的數值(NaN):Number.isNaN()
  • 無窮數:Infinity
  • 判斷兩個值是否絕對相等:Object.is()
  • 另外,如果需要對大數值進行數學運算,目前需要借助相關工具庫。

String 字符串

String 類型用于表示由零或多個 16 位 Unicode 字符組成的字符序列,即字符串。字符串可以用雙引號(“)或者單引號(‘)表示。

由于 HTML 語言的屬性值使用雙引號,所以很多項目約定 JavaScript 語言的字符串只使用單引號。

ECMAScript 中的字符串是不可變的,也就是說,字符串一旦創建,它們的值就不能改變

Unicode 字符集

JavaScript 使用 Unicode 字符集。JavaScript 引擎內部所有字符都用 Unicode 表示。

每個字符在 JavaScript 內部都是以 16 位(即 2 個字節)的 UTF-16 格式儲存。也就是說,JavaScript 的單位字符長度固定為 16 位長度,即 2 個字節。

JavaScript 對 UTF-16 的支持是不完整的,由于歷史原因,只支持兩字節的字符,不支持四字節的字符。

對于碼點在 U+10000U+10FFFF 之間的字符,JavaScript 總是認為它們是兩個字符(length 屬性為 2)。所以處理的時候,必須把這一點考慮在內,也就是說,JavaScript 返回的字符串長度可能是不正確的。

字符字面量/轉義序列

字符字面量會被作為一個字符來解析。

字符字面量

toString() 方法

?? nullundefined 值沒有這個方法。

  • 在調用數值toString() 方法時,可以傳遞一個參數:指定輸出數值的基數,默認為十進制轉換。
  • 通過傳遞基數,toString() 可以輸出以二進制、八進制、十六進制,乃至其他任意有效進制格式表示的字符串值。
var num = 10;
alert(num.toString());       //"10"
alert(num.toString(2));      //"1010"
alert(num.toString(8));      //"12"
alert(num.toString(10));     //"10"
alert(num.toString(16));     //"a"

String () 方法

String () 函數可以將任何類型的值轉換為字符串。轉換規則如下:

  • 如果值有 toString() 方法,則調用該方法;
  • 如果值是 null,則返回 "null"
  • 如果值是 undefined ,則返回 "undefined"。
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;

alert(String(value1));     //"10"
alert(String(value2));     //"true"
alert(String(value3));     //"null"
alert(String(value4));     //"undefined"

length 屬性

length 屬性返回字符串的長度,該屬性也是無法改變的。

var s = 'hello';
s.length // 5

s.length = 3;
s.length // 5

s.length = 7;
s.length // 5

上面代碼表示字符串的 length 屬性無法改變,但是不會報錯。

Base64 編碼

JavaScript 原生提供兩個 Base64 相關的方法。

  • btoa():任意值轉為 Base64 編碼;
  • atob():Base64 編碼轉為原來的值;
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

注意,這兩個方法不適合非 ASCII 碼的字符,會報錯。

btoa('你好') // 報錯

要將非 ASCII 碼字符轉為 Base64 編碼,必須中間插入一個轉碼環節,再使用這兩個方法。

function b64Encode(str) {
  return btoa(encodeURIComponent(str));
}

function b64Decode(str) {
  return decodeURIComponent(atob(str));
}

b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"

Object 類型

  • ECMAScript 中的對象就是一組 “鍵值對”(key-value)的集合,是一種無序的復合數據集合。
  • 創建自定義對象:創建 Object 類型的實例并為其添加屬性和(或)方法。
// 通過 new 操作符創建對象。
var o = new Object();

// 定義一個對象,賦值給一個變量 obj,對象內部包含兩個鍵值對。
var obj = {
  foo: 'Hello',
  bar: 'World'
};

在 ECMAScript 中,Object 是所有對象的基礎,因此所有對象都具有下面這些基本的屬性和方法。

屬性或方法名 描述
Constructor 保存著用于創建當前對象的函數。
hasOwnProperty(propertyName) 檢查給定的屬性在當前對象實例中是否存在(而不是在實例的原型中)。
isPrototypeOf(object) 檢查當前對象是否是傳入對象的原型。
propertyIsEnumerable(propertyName) 檢查給定的屬性是否能夠使用 for-in 來枚舉。
toLoacleString() 返回對象的字符串表示,該字符串與執行環境的地區對應。
toString() 返回對象的字符串表示。
valueOf() 返回對象的字符串、數值或布爾值表示。

使用 for...in 循環枚舉對象的屬性

  • 它遍歷的是對象所有可遍歷(enumerable)的屬性,會跳過不可遍歷的屬性。
  • 它不僅遍歷對象自身的屬性,還遍歷繼承的屬性。
var person = { name: '老張' };

// 遍歷對象的屬性,也會遍歷繼承來的屬性
for (var key in person) {
  // 判斷某個屬性是否為對象自身的屬性
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}

數組

數組(array)是按次序排列的一組值。每個值的位置都有編號(從 0 開始),整個數組用方括號表示。

// 定義數組,同時賦初值
var arr = ['a', 'b', 'c'];

// 先定義,后賦值
var arr = [];
arr[0] = 'a';
arr[1] = 'b';
arr[2] = 'c';

本質上,數組屬于一種特殊的對象。typeof 運算符會返回數組的類型是 object。

typeof [1, 2, 3] // "object"

數組的 length 屬性

數組的 length 屬性,返回數組的成員數量:

['a', 'b', 'c'].length // 3

數組的 length 屬性是可寫的。如果人為設置一個小于當前成員個數的值,該數組的成員會自動減少到 length 設置的值。

length 屬性設為大于數組個數時,讀取新增的位置都會返回 undefined

清空數組的一個有效方法,就是將 length 屬性設為 0。

for...in 循環和數組的遍歷

for...in 循環不僅可以遍歷對象,也可以遍歷數組,畢竟數組只是一種特殊對象。

var a = [1, 2, 3];

for (var i in a) {
  console.log(a[i]);
}
// 1
// 2
// 3

但是,for...in 不僅會遍歷數組所有的數字鍵,還會遍歷非數字鍵。

var a = [1, 2, 3];
a.foo = true;

for (var key in a) {
  console.log(key);
}
// 0
// 1
// 2
// foo

上面代碼在遍歷數組時,也遍歷到了非整數鍵 foo。所以,不推薦使用 for...in 遍歷數組。

數組的遍歷可以考慮使用 for 循環或 while 循環。

var a = [1, 2, 3];

// for循環
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}

// while循環
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}

var l = a.length;
while (l--) {
  console.log(a[l]);
}

數組的 forEach 方法,也可以用來遍歷數組:

var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
  console.log(color);
});
// red
// green
// blue

數組的空位

數組的空位是可以讀取的,返回 undefined。

var a = [, , ,];
a[1] // undefined

使用 delete 命令刪除一個數組成員,會形成空位,并且不會影響 length 屬性。

var a = [1, 2, 3];
delete a[1];

a[1] // undefined
a.length // 3

如果是空位,使用數組的 forEach 方法、for...in 結構、以及 Object.keys 方法進行遍歷,空位都會被跳過。

操作符

一元操作符:只能操作一個值的操作符。


一元操作符

1?? 遞增和遞減操作符

遞增和遞減操作符遵循的規則:

  • 在應用于一個包含有效數字字符的字符串時,先將其轉換為數字值,再執行加減 1 的操作。字符串變量變成數值變量。
  • 在應用于一個不包含有效數字字符的字符串時,將變量的值設置為 NaN。 字符串變量變成數值變量。
  • 在應用于布爾值 false 時,先將其轉換為 0 再執行加減 1 的操作。布爾值變量變成數值變量。
  • 在應用于布爾值 true 時,先將其轉換為 1 再執行加減 1 的操作。布爾值變量變成數值變量。
  • 在應用于浮點數值時,執行加減 1 的操作。
  • 在應用于對象時,先調用對象的 valueOf()方法,以取得一個可供操作的值。然后對該值應用前述規則。如果結果是 NaN,則在調用 toString() 方法后再應用前述規則。對象變量變成數值變量。

前置型操作符

++i、--i

執行前置遞增和遞減操作時,變量的值都是在語句被求值以前改變的。

var age = 29;
var anotherAge = --age + 2; // age 先執行了減法操作

console.log(age); // 28
console.log(anotherAge); // 30

后置型操作符

i++、i--

后置型遞增和遞減操作是在包含它們的語句被求值之后才執行的。

var age = 29;
var anotherAge = age-- + 2; // age 后執行減法操作

console.log(age); // 28
console.log(anotherAge); // 31

2?? 一元加和減操作符

一元加和減操作符主要用于基本的算術運算,也可以用于轉換數據類型。

一元加操作符

  • 一元加操作符以一個加號(+)表示,放在數值前面,對數值不會產生任何影響。
  • 當一元加操作符作用于非數值時,該操作符會像 Number() 轉型函數一樣對這個值執行轉換。

示例:a = +a;

一元減操作符

  • 將一元減操作符應用于數值時,該值會變成負數。
  • 當一元減操作符作用于非數值時,一元減操作符遵循與一元加操作符相同的規則,最后再將得到的數值轉換為負數。

示例:a = -a;

3?? 位操作符

  • 位操作符按內存中表示數值的位來操作數值。
  • 位操作符并不直接操作 64 位的值。而是先將 64 位的值轉換成 32 位的整數,然后執行操作,最后再將結果轉換回 64 位。
  • 默認情況下,ECMAScript 中的所有整數都是有符號整數。
  • 符號位:對于有符號的整數,32 位中的前 31 位用于表示整數的值。第 32 位用于表示數值的符號:0 表示正數,1 表示負數。
  • 負數同樣以二進制碼存儲,但使用的格式是二進制補碼
  • 如果對非數值應用位操作符,會先使用 Number() 函數將該值轉換為一個數值(自動完成),然后再應用位操作。

在 ECMAScript 中,當對數值應用位操作符時,后臺會發生如下轉換過程:
64 位的數值被轉換成 32 位數值,然后執行位操作,最后再將 32 位的結果轉換回 64 位數值。
但這個轉換過程也導致了一個嚴重的副效應,即在對特殊的 NaNInfinity 值應用位操作時,這兩個值都會被當成 0 來處理。

1. 按位非(NOT)

按位非操作的本質:操作數的負值減 1。

var num1 = 25;             //binary 00000000000000000000000000011001
var num2 = ~num1;          //binary 11111111111111111111111111100110
alert(num2);               //-26

2. 按位與(AND)

第一個數值的位 第二個數值的位 結果
1 1 1
1 0 0
0 1 0
0 0 0

按位與操作只在兩個數值的對應位都是 1 時才返回 1,任何一位是 0,結果都是 0。

var result = 25 & 3;
alert(result);    //outputs 1

// 按位與原理:11得1,01得0
//  25 = 0000 0000 0000 0000 0000 0000 0001 1001
//   3 = 0000 0000 0000 0000 0000 0000 0000 0011
// ----------------------------------------------
// AND = 0000 0000 0000 0000 0000 0000 0000 0001

3. 按位或(OR)

第一個數值的位 第二個數值的位 結果
1 1 1
1 0 1
0 1 1
0 0 0
var result = 25 | 3;
alert(result);       //27

4. 按位異或(XOR)

第一個數值的位 第二個數值的位 結果
1 1 0
1 0 1
0 1 1
0 0 0
var result = 25 ^ 3;
alert(result);    //26

5. 左移

左移(<<):將數值的所有位向左移動指定的位數。
左移不會影響操作數的符號位。

6. 有符號右移

有符號右移(>>),保留符號位(即正負號標記),用符號位的值來填充所有空位。

7. 無符號右移

無符號右移(>>>),用 0 來填充空位。對正數來說,無符號右移的結果與有符號右移相同。
無符號右移操作符會把負數的二進制碼當成正數的二進制碼。

4?? 布爾操作符

布爾操作符一共有 3 個:非(NOT)、與(AND)和或(OR)。

邏輯非 !

  • 邏輯非操作符首先會將它的操作數轉換為一個布爾值,然后再對其求反。
  • 同時使用兩個邏輯非操作符(!!),實際上就會模擬 Boolean() 轉型函數的行為,即返回一個操作數的布爾值。

邏輯非操作符遵循下列規則:

  • 如果操作數是一個對象,返回 false;
  • 如果操作數是一個空字符串,返回 true;
  • 如果操作數是一個非空字符串,返回 false;
  • 如果操作數是數值 0,返回 true;
  • 如果操作數是任意非 0 數值(包括 Infinity),返回 false;
  • 如果操作數是 null,返回 true;
  • 如果操作數是 NaN,返回 true;
  • 如果操作數是 undefined,返回 true。

邏輯與 &&

邏輯與的真值表如下:

第一個操作數 第二個操作數 結果
true true true
true false false
false true false
false false false

邏輯與操作可以應用于任何類型的操作數,而不僅僅是布爾值。
在有一個操作數不是布爾值的情況下,邏輯與操作就不一定返回布爾值。此時,它遵循下列規則:

  • 如果第一個操作數是對象,則返回第二個操作數;
  • 如果第二個操作數是對象,則只有在第一個操作數的求值結果為 true 的情況下才會返回該對象;
  • 如果兩個操作數都是對象,則返回第二個操作數;
  • 如果有一個操作數是 null,則返回 null;
  • 如果有一個操作數是 NaN,則返回 NaN;
  • 如果有一個操作數是 undefined,則返回 undefined

邏輯與操作屬于短路操作,即如果第一個操作數能夠決定結果,那么就不會再對第二個操作數求值。
不能在邏輯與操作中使用未定義的值。

邏輯或 ||

邏輯或的真值表如下:

第一個操作數 第二個操作數 結果
true true true
true false true
false true true
false false false

與邏輯與操作相似,如果有一個操作數不是布爾值,邏輯或也不一定返回布爾值;此時,它遵循下列規則:

  • 如果第一個操作數是對象,則返回第二個操作數;
  • 如果第一個操作數的求值結果為 false,則返回第二個操作數;
  • 如果兩個操作數都是對象,則返回第一個操作數;
  • 如果兩個操作數都是 null,則返回 null;
  • 如果兩個操作數都是 NaN,則返回 NaN;
  • 如果兩個操作數都是 undefined,則返回 undefined。

邏輯或操作符也是短路操作符。也就是說,如果第一個操作數的求值結果為 true,就不會對第二個操作數求值了。

短路操作

邏輯與/邏輯或操作屬于短路操作。
即:如果第一個操作數能夠決定結果,那么就不會再對第二個操作數求值。

可以利用邏輯或的這一行為來避免為變量賦 nullundefined 值:

var myObject = preferredObject || backupObject;

/*
 * 變量 myObject 將被賦予等號后面兩個值中的一個。
 * 變量 preferredObject 中包含優先賦給變量 myObject 的值,
 * 變量 backupObject 負責在 preferredObject 中不包含有效值的情況下提供后備值。
 * 如果 preferredObject 的值不是 null,那么它的值將被賦給 myObject;
 * 如果是 null,則將 backupObject 的值賦給 myObject。
 */

node.js 示例代碼:

// 組裝參數
const payload = ctx.request.body || {};

5?? 乘性操作符

  • ECMAScript 定義了 3 個乘性操作符:乘法、除法和求模。
  • 如果參與乘性計算的某個操作數不是數值,后臺會先使用 Number() 轉型函數將其轉換為數值。

乘法 *

乘法用于計算兩個數值的乘積。

在處理特殊值的情況下,乘法操作符遵循下列特殊的規則:

  • 如果操作數都是數值,執行常規的乘法計算,即兩個正數或兩個負數相乘的結果還是正數,而如果只有一個操作數有符號,那么結果就是負數。如果乘積超過了 ECMAScript 數值的表示范圍,則返回 Infinity-Infinity;
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • 如果是 Infinity 與 0 相乘,則結果是 NaN;
  • 如果是 Infinity 與非 0 數值相乘,則結果是 Infinity-Infinity,取決于有符號操作數的符號;
  • Infinity * Infinity = Infinity;
  • 如果有一個操作數不是數值,則在后臺調用 Number() 將其轉換為數值,然后再應用上面的規則。

除法 /

除法執行第二個操作數除第一個操作數的計算。

除法操作符對特殊的值也有特殊的處理規則。這些規則如下:

  • 如果操作數都是數值,執行常規的除法計算,即兩個正數或兩個負數相除的結果還是正數,而如果只有一個操作數有符號,那么結果就是負數。如果商超過了 ECMAScript 數值的表示范圍,則返回 Infinity-Infinity;
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • Infinity / Infinity = NaN;
  • 0 / 0 = NaN;
  • 如果是非零的有限數被零除,則結果是 Infinity-Infinity,取決于有符號操作數的符號;
  • 如果是 Infinity 被任何非零數值除,則結果是 Infinity-Infinity,取決于有符號操作數的符號;
  • 如果有一個操作數不是數值,則在后臺調用 Number() 將其轉換為數值,然后再應用上面的規則。

求模(取余數) %

求模操作符會遵循下列特殊規則來處理特殊的值:

  • 如果操作數都是數值,執行常規的除法計算,返回除得的余數;
  • 如果被除數是無窮大值而除數是有限大的數值,則結果是 NaN;
  • 如果被除數是有限大的數值而除數是零,則結果是 NaN;
  • Infinity % Infinity = NaN;
  • 如果被除數是有限大的數值而除數是無窮大的數值,則結果是被除數;
  • 如果被除數是零,則結果是零;
  • 如果有一個操作數不是數值,則在后臺調用 Number() 將其轉換為數值,然后再應用上面的規則。

6?? 加性操作符

加法 +

(+0) + (+0) = (+0);
(-0) + (-0) = (-0);
(+0) + (-0) = (+0);
Infinity + Infinity = Infinity;
(-Infinity) + (-Infinity) = (-Infinity);
Infinity + (-Infinity) = NaN;
  • 字符串 + 字符串 = 拼接字符串。
  • 字符串 + 操作數 = 操作數轉換為字符串,再拼接兩個字符串。
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • 如果有一個操作數是對象、數值或布爾值,則調用它們的 toString() 方法取得相應的字符串值,然后再應用前面關于字符串的規則。對于 undefinednull,則分別調用 String() 函數并取得字符串"undefined"和"null"。
var result1 = 5 + 5;
console.log(result1); // 10

var result2 = 5 + "5";
console.log(result2); // 55

// 錯誤示例
var num1 = 5;
var num2 = 10;
var message1 = "The sum of 5 and 10 is " + num1 + num2;
console.log(message1);    // The sum of 5 and 10 is 510

// 正確示例
var num1 = 5;
var num2 = 10;
var message2 = "The sum of 5 and 10 is " + (num1 + num2);
console.log(message2);    // The sum of 5 and 10 is 15

減法 -

(+0) - (+0) = (+0);
(+0) - (-0) = (-0); 
(-0) - (-0) = (+0);

Infinity - Infinity = NaN;
(-Infinity) - (-Infinity) = NaN;
Infinity - (-Infinity) = Infinity;
(-Infinity) - Infinity = (-Infinity);

ECMAScript 中的減法操作符在處理各種數據類型轉換時,同樣需要遵循一些特殊規則:

  • 如果兩個操作符都是數值,則執行常規的算術減法操作并返回結果;
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • 如果有一個操作數是字符串、布爾值、nullundefined,則先在后臺調用 Number() 函數將其轉換為數值,然后再根據前面的規則執行減法計算。如果轉換的結果是 NaN,則減法的結果就是 NaN;
  • 如果有一個操作數是對象,則調用對象的 valueOf() 方法以取得表示該對象的數值。如果得到的值是 NaN,則減法的結果就是 NaN。如果對象沒有 valueOf() 方法,則調用其 toString() 方法并將得到的字符串轉換為數值。

7?? 關系操作符

當關系操作符的操作數使用了非數值時,也要進行數據轉換或完成某些奇怪的操作。以下就是相應的規則。

  • 如果兩個操作數都是數值,則執行數值比較。
  • 如果兩個操作數都是字符串,則比較兩個字符串對應的字符編碼值。
  • 如果一個操作數是數值,則將另一個操作數轉換為一個數值,然后執行數值比較。
  • 如果一個操作數是對象,則調用這個對象的 valueOf() 方法,用得到的結果按照前面的規則執行比較。如果對象沒有 valueOf() 方法,則調用 toString() 方法,并用得到的結果根據前面的規則執行比較。
  • 如果一個操作數是布爾值,則先將其轉換為數值,然后再執行比較。
  • 任何操作數與 NaN 進行比較,結果都是 false。
// 兩個操作數都是字符串, 而字符串比較的是字符編碼("2"的字符編碼是 50,而"3"的字符編碼是 51)。
var result = "23" < "3" // true

// 字符串"23"會被轉換成數值 23,然后再與 3 進行比較
var result = "23" < 3   // false

// 字母"a"不能轉換成合理的數值,因此就被轉換成了 NaN。
var result = "a" < 3; // false

8?? 相等操作符

  • 相等和不相等——先轉換再比較;
  • 全等和不全等——僅比較而不轉換;

相等(==)和不相等(!=

在轉換不同的數據類型時,相等和不相等操作符遵循下列基本規則:

  • 如果有一個操作數是布爾值,則在比較相等性之前先將其轉換為數值——false 轉換為 0,而 true 轉換為 1。
  • 如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換為數值;
  • 如果一個操作數是對象,另一個操作數不是,則調用對象的 valueOf() 方法,用得到的基本類型值按照前面的規則進行比較;
  • nullundefined 是相等的。
  • 要比較相等性之前,不能將 nullundefined 轉換成其他任何值。
  • 如果有一個操作數是 NaN,則相等操作符返回 false,而不相等操作符返回 true。重要提示:即使兩個操作數都是 NaN,相等操作符也返回 false;因為按照規則,NaN 不等于 NaN
  • 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等操作符返回 true;否則,返回 false。
null == undefined // true,類似的值
null === undefined // false,不同類型的值
"NaN" == NaN    // false
5 == NaN        // false
NaN == NaN      // false
NaN != NaN      // true
false == 0      // true
true == 1       // true
true == 2       // false
undefined == 0  // false
null == 0       // false
"5" == 5        // true

全等(===)和不全等(!==

  • 相等(==):如果兩個操作數類型不同,會自動強制轉換為相等類型的操作數,再進行比較(強制轉型)。
  • 全等(===):兩個操作數未經轉換就相等的情況下才返回 true。

9?? 條件操作符

條件操作符也常稱為三元運算符:

variable = boolean_expression ? true_value : false_value;

?? 賦值操作符 =

復合賦值操作符:在等于號(=)前面再添加乘性操作符、加性操作符或位操作符。
*=、/=、%=、+=、-=、<<=、>>=、>>>=、

復合賦值操作符可以簡化賦值操作,但不會帶來任何性能的提升。

11. 逗號操作符

逗號操作符多用于聲明多個變量。

// 1.在一條語句中執行多個操作,聲明多個變量
var num1=1, num2=2, num3=3;

// 2.賦值,總是返回表達式中的最后一項
var num = (5, 1, 4, 8, 0); // num 的值為 0

語句

JavaScript 中的語句

if 語句

ECMAScript 會自動調用 Boolean() 轉換函數將 if 表達式中的條件語句轉換為一個布爾值。

if (m === 0) {
  // ...
} else if (m === 1) {
  // ...
} else if (m === 2) {
  // ...
} else {
  // ...
}

switch 語句

ECMAScript 中 switch 的特點:

  • 可以在 switch 語句中使用任何數據類型。
  • 每個 case 值可以是常量、變量、表達式。
  • switch 語句在比較值時使用的是全等操作符===),因此不會發生類型轉換。

while 語句

while 語句屬于前測試循環語句,也就是說,在循環體內的代碼被執行之前,就會對出口條件求值。因此,循環體內的代碼有可能永遠不會被執行。

do-while 語句

do-while 語句是一種后測試循環語句,即只有在循環體中的代碼執行之后,才會測試出口條件。換句話說,在對條件表達式求值之前,循環體內的代碼至少會被執行一次。

for 語句

for 語句也是一種前測試循環語句,但它具有在執行循環之前初始化變量和定義循環后要執行的代碼的能力。

由于 ECMAScript 中不存在塊級作用域,因此在循環內部定義的變量也可以在外部訪問到。

// 即使 i 是在循環內部定義的一個變量,但在循環外部仍然可以訪問到它。
var count = 10;
for (var i = 0; i < count; i++) {
    console.log(i);
}
console.log(i); // 10

for-in 語句

如果表示要迭代的對象的變量值為 nullundefined,for-in 語句會拋出錯誤,比如數組中間有一個對象為空。ECMAScript 5 更正了這一行為,對這種情況不再拋出錯誤,而只是不執行循環體。

?? 建議在使用 for-in 循環之前,先檢測確認該對象的值不是 nullundefined。

label 語句

  • JavaScript 語言可以在代碼中添加 label 標簽,相當于定位符,用于跳轉到程序的任意位置。
  • 標簽通常與 break 語句和 continue 語句配合使用,跳出特定的循環。
  • 建議如果使用 label 語句,一定要使用描述性的標簽,同時不要嵌套過多的循環。
// 語法
label: statement

with 語句

  • with 語句的作用是將代碼的作用域設置到一個特定的對象中。
  • 主要目的:簡化多次編寫同一個對象的工作。
// 語法
with (expression) statement;

// 示例:
// 把變量都包含在 location 對象中。
with(location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

??
大量使用 with 語句會導致性能下降,同時也會給調試代碼造成困難,因此在開發大型應用程序時,不建議使用 with 語句。

函數

通過函數可以封裝任意多條語句,而且可以在任何地方、任何時候調用執行。

ECMAScript 中的函數在定義時不必指定是否返回值。

// 定義
// 關鍵字 函數名(參數)
function functionName(arg0, arg1,...argN) {
    statments
}

// 函數聲明
function sayHi(num1, num2) {
    alert("Hello " + arguments[0] + "," + arguments[1]); // 用類似數組的方式訪問傳入的參數
    alert(arguments.length); // 傳入參數的個數

    if(arguments.length == 1) {
        alert(arguments[0] + 10);
    }else if {
        alert(arguments[0] + num2);
    }
}

// 函數體內通過 arguments 對象訪問參數數組。
// arguments 與 命名參數的內存空間是相互獨立的;
// ***特性:單向影響***:修改 arguments 的值會自動映射同步到命名參數上,反之則不能;
// arguments 對象的長度由傳入的參數個數決定;

參數

  • ECMAScript 函數的重要特點:命名的參數只是提供便利,但不是必須的。
  • ECMAScript 函數中的參數在內部是用一個數組來表示的。在函數體內可以通過 arguments 對象來訪問這個參數數組,從而獲取傳遞給函數的每一個參數。
  • arguments 對象的長度是由傳入的參數個數決定的,不是由定義函數時的命名參數的個數決定的。
  • arguments 對象可以與命名參數一起使用。而且 arguments 對象的值永遠與對應命名參數的值保持同步,但它們的內存空間是獨立的。
  • 沒有傳遞值的命名參數將自動被賦予 undefined 值。
  • 由于不存在函數簽名的特性,ECMAScript 函數不能重載。(就是函數名相同、參數的個數不同的函數)。
  • 無須指定函數的返回值,因為任何 ECMAScript 函數都可以在任何時候返回任何值。實際上,未指定返回值的函數返回的是一個特殊的 undefined 值。
  • 函數中 arguments.length 屬性可以返回傳入參數的個數,實際上,函數的 length 屬性與實際傳入的參數個數無關,只反映函數預期傳入的參數個數。
  • 嚴格模式對如何使用 arguments 對象做出了一些限制。首先,在函數體內直接對 arguments 對象中的元素賦值是無效的。也就是說,即使把 arguments[1] 設置為 10,該函數的參數值仍然還是 undefined。其次,重寫 arguments 的值會導致語法錯誤(代碼將不會執行)。
function howManyArgs () {
    console.log(arguments.length);
}

howManyArgs("string", 45);    //2
howManyArgs();                //0
howManyArgs(12);              //1

函數的傳值

函數參數如果是原始類型的值(數值、字符串、布爾值),傳遞方式是傳值傳遞(passes by value)。這意味著,在函數體內修改參數值,不會影響到函數外部。

如果函數參數是復合類型的值(數組、對象、其他函數),傳遞方式是傳址傳遞(pass by reference)。也就是說,傳入函數的原始值的地址,因此在函數內部修改參數,將會影響到原始值。

函數的作用域

函數執行時所在的作用域,是定義時的作用域,而不是調用時所在的作用域。

var a = 1;
// 函數 x 的作用域綁定在外層
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x(); // x 函數執行時,所處的是定義時的作用域。
}

f() // 1

立即調用的函數表達式(IIFE)

在定義函數之后,立即調用該函數:

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

推薦閱讀更多精彩內容