一、null
null是只有一個值得數(shù)據(jù)類型,這個特殊值是null
null值表示一個空對象指針,如果保存對象的變量還沒有真正的保存對象,就應(yīng)該明確的讓該變量保存null值
null與undefined都可以表示“沒有”,含義非常相似。將一個變量賦值為undefined或null,在if語句中,它們都會被自動轉(zhuǎn)為false,相等運算符(==)甚至直接報告兩者相等。
if (!undefined) {
console.log('undefined is false');
}
// undefined is false
if (!null) {
console.log('null is false');
}
// null is false
undefined == null
// true
Number(undefined) // NaN
5 + undefined // NaN
二、undefind
1、定義一個變量,但是沒有初始化,會得到undefined
2、變量未定義,會得到undefined
3、函數(shù)中return不帶任何返回值時,函數(shù)停止執(zhí)行會返回undefined
4、函數(shù)參數(shù)arguments,沒有傳遞值的命名參數(shù)將自動被賦予undefined
5、對象沒有賦值的屬性var o = new Object(); o.p // undefined
三、布爾值
該類型有2個字面量值true和false,經(jīng)常用在流程控制語句和選擇判斷語句,
常見false值,除了false值都基本都是true
轉(zhuǎn)換規(guī)則是除了下面六個值被轉(zhuǎn)為false,其他值都視為true
1.數(shù)字0、
2.NaN、
3.“ ”,空字符串
4.false
5.undefined
6.null
下列運算符會返回布爾值:
- 前置邏輯運算符: ! (Not)
- 相等運算符:===,!==,==,!=
- 比較運算符:>,>=,<,<=
注意:空數(shù)組([])和空對象({})對應(yīng)的布爾值,都是true
if ([]) {
console.log('true');
}
// true
if ({}) {
console.log('true');
}
// true
四、數(shù)值
1、浮點數(shù):所謂浮點數(shù),就是該數(shù)值中必須包含一個小數(shù)點,且小數(shù)點后面至少有一位數(shù)字,由于保存浮點數(shù)的內(nèi)存空間是保存整數(shù)的2倍,因此,如果小數(shù)點后面沒有任何數(shù)字,或者本身就是一個整數(shù)(1.0),那么該值會被轉(zhuǎn)化成整數(shù),但是浮點數(shù)計算會產(chǎn)生四舍五入誤差的問題。
2、數(shù)值精度:精度最多只能到53個二進制位,這意味著,絕對值小于2的53次方的整數(shù),即-253到253,都可以精確表示。
3、數(shù)值范圍:則 JavaScript 能夠表示的數(shù)值范圍為21024到2-1023(開區(qū)間),超出這個范圍的數(shù)無法表示。這時就會返回Infinity
Math.pow(2, 1024) // Infinity4、數(shù)值的表示法:小數(shù)點前的數(shù)字多于21位 或者 小數(shù)點后的零多于5個,JavaScript 會自動將數(shù)值轉(zhuǎn)為科學計數(shù)法表示,其他情況都采用字面形式直接表示
5、特殊數(shù)值:
正零和負零:幾乎所有場合,正零和負零都會被當作正常的0
(1 / +0) === (1 / -0) // false 除以正零得到+Infinity,除以負零得到-Infinity,這兩者是不相等的
NaN:NaN是 JavaScript 的特殊值,表示“非數(shù)字”(Not a Number),主要出現(xiàn)在將字符串解析成數(shù)字出錯的場合
NaN不是獨立的數(shù)據(jù)類型,而是一個特殊數(shù)值,它的數(shù)據(jù)類型依然屬于Numbertypeof NaN // 'number'
NaN不等于任何值,包括它本身。NaN === NaN // false
NaN與任何數(shù)(包括它自己)的運算,得到的都是NaN
Infinity:一個正的數(shù)值太大,或一個負的數(shù)值太小,或者另一種是非0數(shù)值除以0,得到Infinity
Infinity有正負之分,Infinity表示正的無窮,-Infinity表示負的無窮
由于數(shù)值正向溢出(overflow)、負向溢出(underflow)和被0除,JavaScript 都不報錯,所以單純的數(shù)學運算幾乎沒有可能拋出錯誤
Infinity大于一切數(shù)值(除了NaN),-Infinity小于一切數(shù)值(除了NaN)
Infinity與NaN比較,總是返回false6、與數(shù)值相關(guān)的全局方法
parseInt():將字符串轉(zhuǎn)為整數(shù)。
// parseInt方法用于將字符串轉(zhuǎn)為整數(shù)。
parseInt('123') // 123
// 如果字符串頭部有空格,空格會被自動去除。
parseInt(' 81') // 81
// 如果parseInt的參數(shù)不是字符串,則會先轉(zhuǎn)為字符串再轉(zhuǎn)換。
parseInt(1.23) // 1 // 等同于 parseInt('1.23') // 1
// 字符串轉(zhuǎn)為整數(shù)的時候,是一個個字符依次轉(zhuǎn)換,如果遇到不能轉(zhuǎn)為數(shù)字的字符,就不再進行下去,返回已經(jīng)轉(zhuǎn)好的部分
parseInt('15e2') // 15
// 如果字符串的第一個字符不能轉(zhuǎn)化為數(shù)字(后面跟著數(shù)字的正負號除外),返回NaN
parseInt('abc') // NaN
parseInt('.3') // NaN
// parseInt的返回值只有兩種可能,要么是一個十進制整數(shù),要么是NaN
// parseInt方法還可以接受第二個參數(shù)(2到36之間),表示被解析的值的進制,默認情況下,parseInt的第二個參數(shù)為10,即默認是十進制轉(zhuǎn)十進制
parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000
parseInt('1000', 2) // 8
parseFloat:將一個字符串轉(zhuǎn)為浮點數(shù)
// 如果字符串符合科學計數(shù)法,則會進行相應(yīng)的轉(zhuǎn)換。
parseFloat('314e-2') // 3.14
// 如果字符串包含不能轉(zhuǎn)為浮點數(shù)的字符,則不再進行往后轉(zhuǎn)換,返回已經(jīng)轉(zhuǎn)好的部分。
parseFloat('3.14more non-digit characters') // 3.14
// parseFloat方法會自動過濾字符串前導(dǎo)的空格。
parseFloat('\t\v\r12.34\n ') // 12.34
// 如果參數(shù)不是字符串,或者字符串的第一個字符不能轉(zhuǎn)化為浮點數(shù),則返回NaN。
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN
isNaN():用來判斷一個值是否為NaN
// isNaN只對數(shù)值有效,如果傳入其他值,會被先轉(zhuǎn)成數(shù)值。比如,傳入字符串的時候,字符串會被先轉(zhuǎn)成NaN,所以最后返回true,這一點要特別引起注意。也就是說,isNaN為true的值,有可能不是NaN,而是一個字符串。
isNaN('Hello') // true
// 相當于
isNaN(Number('Hello')) // true
// 使用isNaN之前,最好判斷一下數(shù)據(jù)類型。
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
// 判斷NaN更可靠的方法是,利用NaN為唯一不等于自身的值的這個特點,進行判斷。
function myIsNaN(value) {
return value !== value;
}
isFinite():isFinite方法返回一個布爾值,表示某個值是否為正常的數(shù)值。
// 除了Infinity、-Infinity、NaN和undefined這幾個值會返回false,isFinite對于其他的數(shù)值都會返回true
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true
五、字符串
字符串就是零個或多個排在一起的字符,放在單引號或雙引號之中
'key = "value"'
"It's a long journey"
// 如果要在單引號字符串的內(nèi)部,使用單引號,就必須在內(nèi)部的單引號前面加上反斜杠,用來轉(zhuǎn)義。雙引號字符串內(nèi)部使用雙引號,也是如此
'Did she say \'Hello\'?'
// 字符串默認只能寫在一行內(nèi),分成多行將會報錯。
'a
b
c'
// SyntaxError: Unexpected token ILLEGAL
// 連接運算符(+)可以連接多個單行字符串,將長字符串拆成多行書寫,輸出的時候也是單行
var longString = 'Long '
+ 'long '
+ 'long '
+ 'string';
字符串可以被視為字符數(shù)組,因此可以使用數(shù)組的方括號運算符,用來返回某個位置的字符(位置編號從0開始)
var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"
// 直接對字符串使用方括號運算符
'hello'[1] // "e"
// 如果方括號中的數(shù)字超過字符串的長度,或者方括號中根本不是數(shù)字,則返回undefined。
'abc'[3] // undefined
'abc'[-1] // undefined
'abc'['x'] // undefined
// 字符串內(nèi)部的單個字符無法改變和增刪,這些操作會默默地失敗
var s = 'hello';
delete s[0];
s // "hello"
s[1] = 'a';
s // "hello"
length屬性返回字符串的長度,該屬性也是無法改變的。
var s = 'hello';
s.length // 5
s.length = 3;
s.length // 5
六、對象
對象就是一組“鍵值對”(key-value)的集合,是一種無序的復(fù)合數(shù)據(jù)集合。
鍵名與鍵值之間用冒號分隔。
兩個鍵值對之間用逗號分隔。
鍵名:對象的所有鍵名都是字符串(ES6 又引入了 Symbol 值也可以作為鍵名),所以加不加引號都可以。
// 如果鍵名不符合標識名的條件(比如第一個字符為數(shù)字,或者含有空格或運算符),且也不是數(shù)字,則必須加上引號,否則會報錯
// 報錯
var obj = {
1p: 'Hello World'
};
// 不報錯
var obj = {
'1p': 'Hello World',
'h w': 'Hello World',
'p+q': 'Hello World'
};
對象的每一個鍵名又稱為“屬性”(property),它的“鍵值”可以是任何數(shù)據(jù)類型。如果一個屬性的值為函數(shù),通常把這個屬性稱為“方法”,它可以像函數(shù)那樣調(diào)用
var obj = {
p: function (x) {
return 2 * x;
}
};
obj.p(1) // 2
如果屬性的值還是一個對象,就形成了鏈式引用。
var o1 = {};
var o2 = { bar: 'hello' };
o1.foo = o2;
o1.foo.bar // "hello"
屬性可以動態(tài)創(chuàng)建,不必在對象聲明時就指定。
var obj = {};
obj.foo = 123;
obj.foo // 123
對象的引用:如果不同的變量名指向同一個對象,那么它們都是這個對象的引用,也就是說指向同一個內(nèi)存地址。修改其中一個變量,會影響到其他所有變量。
var o1 = {};
var o2 = o1;
o1.a = 1;
o2.a // 1
o2.b = 2;
o1.b // 2
// 這種引用只局限于對象,如果兩個變量指向同一個原始類型的值。那么,變量這時都是值的拷貝。
var x = 1;
var y = x;
x = 2;
y // 1
對象屬性操作
讀取:有兩種方法,一種是使用點運算符,還有一種是使用方括號運算符
var foo = 'bar';
var obj = {
foo: 1,
bar: 2
};
obj.foo // 1
obj[foo] // 2
// 引用對象obj的foo屬性時,如果使用點運算符,foo就是字符串;如果使用方括號運算符,但是不使用引號,那么foo就是一個變量,指向字符串bar
// 數(shù)字鍵可以不加引號,因為會自動轉(zhuǎn)成字符串
var obj = {
0.7: 'Hello World'
};
obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"
賦值:點運算符和方括號運算符,不僅可以用來讀取值,還可以用來賦值
var obj = {};
obj.foo = 'Hello';
obj['bar'] = 'World';
查看 :查看一個對象本身的所有屬性,可以使用Object.keys方法
var obj = {
key1: 1,
key2: 2
};
Object.keys(obj);
// ['key1', 'key2']
刪除:delete命令用于刪除對象的屬性,刪除成功后返回true
var obj = { p: 1 };
Object.keys(obj) // ["p"]
delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
屬性是否存在:in 運算符
// in運算符用于檢查對象是否包含某個屬性(注意,檢查的是鍵名,不是鍵值),如果包含就返回true,否則返回false
var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true
// in運算符的一個問題是,它不能識別哪些屬性是對象自身的,哪些屬性是繼承的。
// 這時,可以使用對象的hasOwnProperty方法判斷一下,是否為對象自身的屬性
var obj = {};
if ('toString' in obj) {
console.log(obj.hasOwnProperty('toString')) // false
}
屬性的遍歷:for...in 循環(huán)
// for...in循環(huán)用來遍歷一個對象的全部屬性
var obj = {a: 1, b: 2, c: 3};
for (var i in obj) {
console.log('鍵名:', i);
console.log('鍵值:', obj[i]);
}
// 鍵名: a
// 鍵值: 1
// 鍵名: b
// 鍵值: 2
// 鍵名: c
// 鍵值: 3
// 它遍歷的是對象所有可遍歷(enumerable)的屬性,會跳過不可遍歷的屬性。
// 它不僅遍歷對象自身的屬性,還遍歷繼承的屬性。
// 如果只想遍歷自身屬性,應(yīng)該結(jié)合使用hasOwnProperty方法
var person = { name: '老張' };
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
// name
七、函數(shù)
7.1 函數(shù)的聲明
1)function 命令
function 函數(shù)名(參數(shù)1,參數(shù)2) {
// 函數(shù)體
}
// 調(diào)用
函數(shù)名()
2)函數(shù)表達式
// 將一個匿名函數(shù)賦值給變量
var print = function(s) {
console.log(s);
};
// 采用函數(shù)表達式聲明函數(shù)時,function命令后面不帶有函數(shù)名。如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效
var print = function x(){
console.log(typeof x);
};
x
// ReferenceError: x is not defined
print()
// function
3)Function 構(gòu)造函數(shù)
// Function構(gòu)造函數(shù)接受三個參數(shù),除了最后一個參數(shù)是add函數(shù)的“函數(shù)體”,其他參數(shù)都是add函數(shù)的參數(shù)。
var add = new Function(
'x',
'y',
'return x + y'
);
// 等同于
function add(x, y) {
return x + y;
}
7.2 函數(shù)的重復(fù)聲明
如果同一個函數(shù)被多次聲明,后面的聲明就會覆蓋前面的聲明
function f() {
console.log(1);
}
f() // 2
function f() {
console.log(2);
}
f() // 2
7.3 return 語句
JavaScript 引擎遇到return語句,就直接返回return后面的那個表達式的值,后面即使還有語句,也不會得到執(zhí)行
return語句不是必需的,如果沒有的話,該函數(shù)就不返回任何值,或者說返回undefined
7.4 函數(shù)名的提升
7.5 函數(shù)的屬性和方法
1、name
name: 函數(shù)的name屬性返回函數(shù)的名字
如果是通過變量賦值定義的匿名函數(shù),那么name屬性返回變量名。
var f2 = function () {};
f2.name // "f2"
如果變量的值是一個具名函數(shù),那么name屬性返回function關(guān)鍵字之后的那個函數(shù)名
var f3 = function myName() {};
f3.name // 'myName'
name屬性的一個用處,就是獲取參數(shù)函數(shù)的名字。
var myFunc = function () {};
function test(f) {
console.log(f.name);
}
test(myFunc) // myFunc
2、length
函數(shù)的length屬性返回函數(shù)預(yù)期傳入的參數(shù)個數(shù),即函數(shù)定義之中的參數(shù)個數(shù)。
// 它的length屬性就是定義時的參數(shù)個數(shù)。不管調(diào)用時輸入了多少個參數(shù),length屬性始終等于2
function f(a, b) {}
f.length // 2
3、**toString() **
函數(shù)的toString方法返回一個字符串,內(nèi)容是函數(shù)的源碼
// 對于那些原生的函數(shù),toString()方法返回function (){[native code]}。
function f() {
a();
b();
c();
}
f.toString()
// function f() {
// a();
// b();
// c();
// }
7.6 函數(shù)作用域
作用域(scope)指的是變量存在的范圍。在 ES5 的規(guī)范中,JavaScript 只有兩種作用域:一種是全局作用域,變量在整個程序中一直存在,所有地方都可以讀?。涣硪环N是函數(shù)作用域,變量只在函數(shù)內(nèi)部存在。
對于頂層函數(shù)來說,函數(shù)外部聲明的變量就是全局變量(global variable),它可以在函數(shù)內(nèi)部讀取。
var v = 1;
function f() {
console.log(v);
}
f()
// 1
在函數(shù)內(nèi)部定義的變量,外部無法讀取,稱為“局部變量”
function f(){
var v = 1;
}
v // ReferenceError: v is not defined
函數(shù)內(nèi)部定義的變量,會在該作用域內(nèi)覆蓋同名全局變量。
var v = 1;
function f(){
var v = 2;
console.log(v);
}
f() // 2
v // 1
注意,對于var命令來說,局部變量只能在函數(shù)內(nèi)部聲明,在其他區(qū)塊中聲明,一律都是全局變量
if (true) {
var x = 5;
}
console.log(x); // 5
函數(shù)內(nèi)部的變量提升
與全局作用域一樣,函數(shù)作用域內(nèi)部也會產(chǎn)生“變量提升”現(xiàn)象。var命令聲明的變量,不管在什么位置,變量聲明都會被提升到函數(shù)體的頭部。
function foo(x) {
if (x > 100) {
var tmp = x - 100;
}
}
// 等同于
function foo(x) {
var tmp;
if (x > 100) {
tmp = x - 100;
};
}
函數(shù)本身的作用域
函數(shù)本身也是一個值,也有自己的作用域。它的作用域與變量一樣,就是其聲明時所在的作用域,與其運行時所在的作用域無關(guān)。
var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f() // 1
數(shù)執(zhí)行時所在的作用域,是定義時的作用域,而不是調(diào)用時所在的作用域
很容易犯錯的一點是,如果函數(shù)A調(diào)用函數(shù)B,卻沒考慮到函數(shù)B不會引用函數(shù)A的內(nèi)部變量。
var x = function () {
console.log(a);
};
function y(f) {
var a = 2;
f();
}
y(x)
// ReferenceError: a is not defined
函數(shù)體內(nèi)部聲明的函數(shù),作用域綁定函數(shù)體內(nèi)部。
function foo() {
var x = 1;
function bar() {
console.log(x);
}
return bar;
}
var x = 2;
var f = foo();
f() // 1
7.7 arguments 對象
函數(shù)運行的時候,有時需要提供外部數(shù)據(jù),不同的外部數(shù)據(jù)會得到不同的結(jié)果,這種外部數(shù)據(jù)就叫參數(shù)。
function square(x) {
return x * x;
}
square(2) // 4
square(3) // 9
函數(shù)參數(shù)如果是原始類型的值(數(shù)值、字符串、布爾值),傳遞方式是傳值傳遞.這意味著,在函數(shù)體內(nèi)修改參數(shù)值,不會影響到函數(shù)外部。
var p = 2;
function f(p) {
p = 3;
}
f(p);
p // 2
如果函數(shù)參數(shù)是復(fù)合類型的值(數(shù)組、對象、其他函數(shù)),傳遞方式是傳址傳遞(pass by reference)。也就是說,傳入函數(shù)的原始值的地址,因此在函數(shù)內(nèi)部修改參數(shù),將會影響到原始值。
var obj = { p: 1 };
function f(o) {
o.p = 2;
}
f(obj);
obj.p // 2
如果函數(shù)內(nèi)部修改的,不是參數(shù)對象的某個屬性,而是替換掉整個參數(shù),這時不會影響到原始值。
var obj = [1, 2, 3];
function f(o) {
o = [2, 3, 4];
}
f(obj);
obj // [1, 2, 3]
如果有同名的參數(shù),則取最后出現(xiàn)的那個值。
function f(a, a) {
console.log(a);
}
f(1, 2) // 2
在函數(shù)體內(nèi)部讀取所有參數(shù),就是arguments對象.
arguments對象包含了函數(shù)運行時的所有參數(shù),arguments[0]就是第一個參數(shù),arguments[1]就是第二個參數(shù),以此類推。這個對象只有在函數(shù)體內(nèi)部,才可以使用
var f = function (one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
// 1
// 2
// 3
正常模式下,arguments對象可以在運行時修改
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 5
嚴格模式下,arguments對象與函數(shù)參數(shù)不具有聯(lián)動關(guān)系。也就是說,修改arguments對象不會影響到實際的函數(shù)參數(shù)
var f = function(a, b) {
'use strict'; // 開啟嚴格模式
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 2
arguments對象的length屬性,可以判斷函數(shù)調(diào)用時到底帶幾個參數(shù)
function f() {
return arguments.length;
}
f(1, 2, 3) // 3
f(1) // 1
f() // 0
需要注意的是,雖然arguments很像數(shù)組,但它是一個對象.
如果要讓arguments對象使用數(shù)組方法,真正的解決方法是將arguments轉(zhuǎn)為真正的數(shù)組。下面是兩種常用的轉(zhuǎn)換方法:slice方法和逐一填入新數(shù)組。
var args = Array.prototype.slice.call(arguments);
// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
arguments對象帶有一個callee屬性,返回它所對應(yīng)的原函數(shù)。
var f = function () {
console.log(arguments.callee === f);
}
f() // true
可以通過arguments.callee,達到調(diào)用函數(shù)自身的目的。這個屬性在嚴格模式里面是禁用的,因此不建議使用。
八、數(shù)組
數(shù)組(array)是按次序排列的一組值。每個值的位置都有編號(從0開始),整個數(shù)組用方括號表示。var arr = ['a', 'b', 'c'];
定義時賦值,數(shù)組也可以先定義后賦值
任何類型的數(shù)據(jù),都可以放入數(shù)組
如果數(shù)組的元素還是數(shù)組,就形成了多維數(shù)組。
本質(zhì)上,數(shù)組屬于一種特殊的對象。typeof運算符會返回數(shù)組的類型是object typeof [1, 2, 3] // "object"
Object.keys方法返回數(shù)組的所有鍵名??梢钥吹綌?shù)組的鍵名就是整數(shù)0、1、2
數(shù)組的length屬性,返回數(shù)組的成員數(shù)量。
更詳細見后續(xù)數(shù)組詳細章節(jié)。
本文參考:JavaScript 教程