JavaScript學(xué)習(xí)(1)之JavaScript基礎(chǔ)
由于工作原因,開發(fā)語(yǔ)言逐漸以JavaScript為主,所以,抽空學(xué)習(xí)了下JavaScript語(yǔ)法。等現(xiàn)階段的工作穩(wěn)定之后,陸續(xù)會(huì)分享下自己在學(xué)習(xí)和開發(fā)過程中的一些經(jīng)驗(yàn)總結(jié)。本著"技多不壓身"的原則以及對(duì)各種編程語(yǔ)言的熱愛,雖然筆者一直從事游戲開發(fā)工作,也愉快而又義無反顧的加入了JavaScript陣營(yíng)。
1、JavaScript概述以應(yīng)用范圍
1.1 JavaScript概述
首先,JavaScript是一門動(dòng)態(tài)類型的編程語(yǔ)言。支持面向?qū)ο蟆⒑瘮?shù)式等編程范式。同時(shí),它也是運(yùn)行在宿主環(huán)境下的輕量級(jí)的腳本語(yǔ)言,例如:瀏覽器,JavaScript代碼可嵌入到HTML頁(yè)面中。當(dāng)然,也有應(yīng)用基于Node.js的服務(wù)器環(huán)境。可以說它是主流瀏覽器都支持的腳本語(yǔ)言,這也造就了JavaScript在PC、手機(jī)、平板等環(huán)境處理與網(wǎng)頁(yè)交互時(shí)的天然優(yōu)勢(shì)。隨著HTML5的推廣與廣泛應(yīng)用,出現(xiàn)了大量基于JavaScript的跨平臺(tái)框架和游戲引擎,是通往全棧開發(fā)很值得學(xué)習(xí)的一門編程語(yǔ)言。正如在編程語(yǔ)言界流行著“世界終將是JS的”的槽點(diǎn),足以可見JavaScript的強(qiáng)大。
1.2 JavaScript應(yīng)用范圍
web前端:最近比較火的Vue.js、React、 Angular等等前端框架層出不窮。
手機(jī)app:React Native、PhoneGap、Weex等跨平臺(tái)移動(dòng)框架等,以及微信小程序等。
游戲引擎:Cocos2d-js、Unity、egret等引擎都支持JavaScript腳本語(yǔ)言。
服務(wù)器:pomelo、Node.js等。
2、基本概念
2.1 語(yǔ)法
JavaScript語(yǔ)法中大量借鑒了C和Java、Perl等語(yǔ)言的語(yǔ)法,因此熟悉這些語(yǔ)言的人再來學(xué)習(xí)JavaScript會(huì)感覺似曾相識(shí)。但JavaScript與Java是兩回事,JavaScript完全是蹭了當(dāng)年Java的熱度,來推動(dòng)自己。
一個(gè)完整的JavaScript實(shí)現(xiàn)由下面三個(gè)不同的部分組成:
- 核心(ECMAScript)
- 文檔對(duì)象模型(DOM)
- 瀏覽器對(duì)象模型(BOM)
ECMAScript:
定義了語(yǔ)言的基礎(chǔ),規(guī)定并對(duì)該語(yǔ)言在語(yǔ)法、類型、語(yǔ)句、關(guān)鍵字、保留字、操作符、對(duì)象等方面作了具體描述,并不包含輸入和輸出。
DOM:
文檔對(duì)象模型(Document Object Model)是XML和HTML的編程接口。DOM會(huì)把整個(gè)頁(yè)面映射為一個(gè)多層節(jié)點(diǎn)結(jié)構(gòu)樹,程序可以對(duì)結(jié)構(gòu)樹進(jìn)行添加、刪除、替換或修改任何節(jié)點(diǎn)。
BOM:
瀏覽器對(duì)象模型(Browser Object Model)支持訪問和操作瀏覽器窗口。
注:以下描述就不區(qū)分ECMAScript和JavaScript,也不簡(jiǎn)稱js,均統(tǒng)稱JavaScript。
2.2 標(biāo)識(shí)符
這里的標(biāo)識(shí)符,統(tǒng)指變量名、函數(shù)名、屬性名以及函數(shù)參數(shù)的名字。那么,標(biāo)識(shí)符的命名有以下規(guī)則:(這些規(guī)則跟C、Java等語(yǔ)言類似)
- 第一個(gè)字符必須是一個(gè)字母、下劃線_或者美元符號(hào)$
- 其他字符則可以是字母、數(shù)字、下劃線和美元符號(hào)。
- 不能使用關(guān)鍵字、保留字來定義標(biāo)識(shí)符。例如:if、for等。
注:在JavaScript中,變量名、函數(shù)名甚至操作符等是區(qū)分大小寫的。即name和Name代表的是兩個(gè)不同的變量。
2.3 注釋
單行注釋使用//。例如:
// 這是一個(gè)單行注釋
多行注釋使用/***/結(jié)構(gòu)。例如:
/*
* 這是一個(gè)多行注釋
*/
2.4 嚴(yán)格模式
啟用嚴(yán)格模式,會(huì)對(duì)一些不確定的行為或不安全的操作拋出錯(cuò)誤。在腳本的頂部添加如下字符串:
"use strict";
也可以添加到函數(shù)內(nèi)部,來指定該函數(shù)在嚴(yán)格模式下執(zhí)行。
2.5 語(yǔ)句
JavaScript中每條語(yǔ)句都是以分號(hào)結(jié)尾,但分號(hào)不是必需的。但建議是不要省略分號(hào),來避免如不完整的輸入等問題。語(yǔ)句塊用花括號(hào)包含。
2.6 變量
可以使用var
關(guān)鍵字來定義變量,JavaScript的變量可以用來保存任何類型的數(shù)據(jù)。例如:
var name = "AlphaGL";
name = 18;
name = function() {
};
也可以使用逗號(hào)來定義多個(gè)變量。例如:
var name = "AlphaGL",
age = 18,
work = true;
雖然JavaScript支持這種寫法,一般不推薦。不僅可讀性差,而且易出錯(cuò)。
在函數(shù)內(nèi)聲明的變量,那么該變量的作用域?yàn)樵摵瘮?shù)內(nèi)。例如:
function test() {
var name = "AlphaGL";
}
test();
console.log(name); // ReferenceError: name is not defined
雖然可以去掉var聲明來解決這個(gè)問題,例如:
function test() {
name = "AlphaGL";
}
test();
console.log(name);
這時(shí),name就成了全局變量。一般不推薦這種做法。全局變量難以維護(hù),而且容易導(dǎo)致結(jié)構(gòu)混亂。
3、數(shù)據(jù)類型
JavaScript中數(shù)據(jù)類型主要有:Number、String、Boolean、Object、undefined、null這幾種類型。以及涉及到類型判斷時(shí)會(huì)用到的操作有:typeof、instanceof、Object.prototype.toString。
3.1 Number
(1)JavaScript中并不區(qū)分整型或浮點(diǎn)數(shù),所有數(shù)字均統(tǒng)一采用64位浮點(diǎn)數(shù)表示。可用Number.MIN_VALUE和Number.MAX_VALUE來獲取Number類型的值的范圍。
console.log(Number.MIN_VALUE); // 5e-324
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
(2)NaN
非數(shù)值(Not a Number)是一個(gè)特殊的數(shù)值。當(dāng)算術(shù)運(yùn)算返回一個(gè)未定義或無法表示的值時(shí),就產(chǎn)生NaN了。
console.log(typeof(NaN)); // number
console.log(0 / 0); // NaN
console.log("hello" / 5); // NaN
console.log(NaN + 1); // NaN
注:0除以0會(huì)返回NaN。其它數(shù)除以0不會(huì)。
判斷一個(gè)變量是否是非數(shù)值,可以使用isNaN()函數(shù)。
console.log(isNaN(NaN)); // true
console.log(NaN == NaN); // false
console.log(isNaN("hello")); // true,"hello"不能轉(zhuǎn)換成數(shù)值
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(負(fù)無窮)
JavaScript中還有兩個(gè)特殊的數(shù)值Infinity和-Infinity。
console.log(typeof(Infinity)); // number
console.log(typeof(-Infinity)); // number
console.log(1 / 0); // Infinity
console.log(1 / -0); // -Infinity
判斷一個(gè)變量是否是有窮數(shù),可以使用isFinite()函數(shù)。
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類型字符串是由單引號(hào)或雙引號(hào)包括的零個(gè)或多個(gè)字符序列。
var key = "hello";
var value = 'world';
(2)字符串可以像數(shù)組一樣訪問,但一旦字符串的值確定了,就不能改變。
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
還有一些常規(guī)的String操作的api,可以查閱JavaScript的String api文檔。
3.3 Boolean
該類型只有兩個(gè)字面值:true和false。需要強(qiáng)調(diào)的是,true不一定是1,false也不一定是0。其它類型轉(zhuǎn)換成Boolean類型時(shí),有一下規(guī)則:
- Boolean
true轉(zhuǎn)換成true,false轉(zhuǎn)換成false。 - String
任何非空字符串轉(zhuǎn)換成true,空字符串("")轉(zhuǎn)換成false。 - Number
任何非零數(shù)字值(包括無窮大)轉(zhuǎn)換成true,0和NaN轉(zhuǎn)換成false。 - Object
任何對(duì)象轉(zhuǎn)換成true,null轉(zhuǎn)換成false。 - Undefined
undefined轉(zhuǎn)換成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
綜上:當(dāng)值為0,null,false,NaN,undefined,空字符串("")轉(zhuǎn)換成Boolean類型時(shí)值都為false,其它任何值都為true。
以下實(shí)例:
(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的對(duì)象, 包括值為false的Boolean對(duì)象, 在條件語(yǔ)句中,其值都將作為true來判斷。
3.4 Object
對(duì)象是JavaScript中重要的數(shù)據(jù)類型,除數(shù)字、true、false、null、undefined和字符串外,所有的值都是對(duì)象。在JavaScript中對(duì)象是一組無序的鍵值對(duì)集合,類似其它語(yǔ)言的HashMap、Dictionary等數(shù)據(jù)結(jié)構(gòu)。下面會(huì)有單獨(dú)一小節(jié)來專門講述下對(duì)象。
3.5 undefined
在使用var聲明變量但未對(duì)其初始化時(shí),該變量的值即為:undefined。undefined類型也只有這一個(gè)值。
字面值undefined主要用于比較。
var i = undefined;
console.log(i == undefined); // true
未初始化的變量與未聲明的變量
var foo;
console.log(foo); // undefined
console.log(bar); // 報(bào)錯(cuò)
var foo;
console.log(typeof(foo)); // undefined
console.log(typeof(bar)); // undefined
使用typeof操作符來檢測(cè)未初始化的變量與未聲明的變量,都返回undefined。
3.6 null
與undefined類似,null類型也只有一個(gè)值null。空值null表示一個(gè)空的對(duì)象指針。
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是動(dòng)態(tài)類型的,因此就需要一種手段來檢測(cè)給定的變量的具體數(shù)據(jù)類型。
(1)typeof
typeof操作符返回的值是以下某個(gè)字符串:
- "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:下文會(huì)討論。
(3)Object.prototype.toString:下文會(huì)討論。
4、流程控制與運(yùn)算符
4.1 流程控制
流程控制主要有條件語(yǔ)句和循環(huán)語(yǔ)句。條件語(yǔ)句又分為:if語(yǔ)句,if-else語(yǔ)句,switch語(yǔ)句等。循環(huán)語(yǔ)句分為:while循環(huán),for循環(huán),do-while循環(huán)。
4.1.1 條件語(yǔ)句
(1)if語(yǔ)句
if (condition1) {
statement1;
}else if(condition2) {
statement2;
}else {
statement3;
}
該結(jié)構(gòu),當(dāng)條件1(condition1為true)滿足時(shí),執(zhí)行語(yǔ)句1(statement1),否則條件2(condition2為true)滿足時(shí),執(zhí)行語(yǔ)句2,以上都不滿足則執(zhí)行語(yǔ)句3(statement3)。
這里的else if
可以有零個(gè)或多個(gè),甚至else
都是可選的,根據(jù)實(shí)際情況來做實(shí)際判斷。
if (i > 0) {
console.log("i大于0");
}else if(i < 0) {
console.log("i小于0");
}else {
console.log("i等于0");
}
(2)switch語(yǔ)句
switch是一種多分支結(jié)構(gòu),與if結(jié)構(gòu)類似,也是一種普遍使用的流程控制語(yǔ)句。
switch(expression) {
case value1:
statement1;
break;
case value2:
statement2;
break;
default:
statement3;
}
當(dāng)表達(dá)式(expression)的值,等于對(duì)應(yīng)case的值(value),則會(huì)執(zhí)行對(duì)應(yīng)的語(yǔ)句塊(statement),break用于跳出該switch結(jié)構(gòu),若省略break,則會(huì)繼續(xù)執(zhí)行下一個(gè)case。default用于以上表達(dá)式的值都不滿足條件。
表達(dá)式(expression)可以使用任何數(shù)據(jù)類型,包括字符串、對(duì)象。case的值(value)也可以是變量、表達(dá)式,不一定是常量。這點(diǎn)與其他有的語(yǔ)言不同,只能使用數(shù)值。
4.1.2 循環(huán)語(yǔ)句
(1)while語(yǔ)句
while循環(huán)包含循環(huán)條件和循環(huán)體,當(dāng)循環(huán)條件滿足條件時(shí),才會(huì)執(zhí)行循環(huán)體內(nèi)的代碼塊。
while(expression) {
statement;
}
while(i < 100) {
i++;
}
(2)for語(yǔ)句
for語(yǔ)句一般包含初始條件、循環(huán)條件、循環(huán)遞增(遞減)語(yǔ)句三部分。
for(initialization; expression; post-loop-expression) {
statement;
}
for(var i = 0; i < 100; i++) {
console.log(i);
}
(3)for-in語(yǔ)句
該語(yǔ)句類似于其他語(yǔ)言的foreach語(yǔ)句,可以用了遍歷容器結(jié)構(gòu)或?qū)ο蟮膶傩浴?/p>
for (property in expression) {
statement;
}
var o = [1, 2, 3];
for(var i in o) {
console.log(o[i]);
}
(4)do-while語(yǔ)句
do-while語(yǔ)句與while語(yǔ)句類似,不同的時(shí),do-while語(yǔ)句會(huì)先執(zhí)行循環(huán)體,再檢測(cè)循環(huán)條件,這就意味著該循環(huán)語(yǔ)句至少會(huì)執(zhí)行一次。
do {
statement;
}while(expression);
(5)break與continue
break和continue都可用于控制循環(huán)中代碼的執(zhí)行。
break:立即退出循環(huán)。
continue:跳出當(dāng)前循環(huán),繼續(xù)執(zhí)行下次循環(huán)。
4.2 運(yùn)算符
JavaScript中運(yùn)算符大體可分為,算術(shù)運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符以、位運(yùn)算符以及其它操作符等,當(dāng)然也包括運(yùn)算符重載。
- 算術(shù)運(yùn)算符:
+
,-
,*
,/
,%
,++
,--
,+=
,-=
,*=
,/=
,%=
等。 - 關(guān)系運(yùn)算符:
<
,>
,<=
,>=
,==
,===
,!=
,!==
。 - 邏輯運(yùn)算符:
&&
,||
,!
- 位運(yùn)算符:
~
,&
,|
,^
,<<
,>>
,>>>
- 其他操作符:
=
,,
,?=
4.2.1 算術(shù)運(yùn)算符
(1)加法操作符(+)
加減法操作符時(shí)JavaScript中最常用得操作符之一,與其他語(yǔ)言不同得是,在JavaScript中有一系列特殊得行為,使用時(shí)應(yīng)當(dāng)注意。
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 (中國(guó)標(biāo)準(zhǔn)時(shí)間)
console.log(1 + {name: "AlphaGL"}); // 1[object Object]
綜上,使用加法操作符可以總結(jié)為如下規(guī)則:
- 如果兩個(gè)操作數(shù)都是數(shù)值,則執(zhí)行常規(guī)得加法計(jì)算。這里需要注意浮點(diǎn)數(shù)的加法。
- 如果一個(gè)操作數(shù)為字符串,則將另外一個(gè)操作數(shù)也轉(zhuǎn)化成字符串類型,再執(zhí)行字符串的拼接。
- 如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作是Infinity,則加法結(jié)果為Infinity。如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是-Infinity,則加法結(jié)果為-Infinity。如果是Infinity加-Infinity,則加法結(jié)果為NaN。如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是NaN,則加法結(jié)果為NaN。
- 如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是boolean,null類型,則先將boolean和null類型轉(zhuǎn)行成原始值,再執(zhí)行加法運(yùn)算。
- 如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是對(duì)象,則會(huì)先調(diào)用對(duì)象的
valueOf
方法轉(zhuǎn)化成原始值,如果對(duì)象沒有valueOf
方法,則調(diào)用toString
方法。
(2)減法運(yùn)算符(-)
減法的運(yùn)算規(guī)則與加法類似,這里就不再詳細(xì)介紹了。
(3)乘法運(yùn)算符(*)
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
綜上,使用減法操作符可以總結(jié)為如下規(guī)則:
- 兩個(gè)正數(shù)或兩個(gè)負(fù)數(shù)相乘,結(jié)果為正數(shù)。其它有一個(gè)操作數(shù)為負(fù)數(shù),那結(jié)果也為負(fù)數(shù)。如果結(jié)果超出數(shù)值的表示范圍,則結(jié)果為Infinity或-Infinity。
- 如果有一個(gè)操作數(shù)為NaN或undefined,則結(jié)果為NaN。
- 如果一個(gè)非0數(shù)值與Infinity或-Infinity相乘,則結(jié)果為Infinity或-Infinity,符號(hào)取決于操作數(shù)的符號(hào)和Infinity還是-Infinity。0與Infinity或-Infinity,則結(jié)果為NaN。
- 如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是boolean或者字符串,則先將該操作數(shù)轉(zhuǎn)化為原始值,如果轉(zhuǎn)化后的值不是數(shù)值,則結(jié)果為NaN,否則執(zhí)行常規(guī)乘法運(yùn)算。
- 如果一個(gè)操作數(shù)是數(shù)值,另外一個(gè)操作數(shù)是對(duì)象,則結(jié)果為NaN。如果是Date對(duì)象,則乘以基于當(dāng)前到1970年1月1日起的毫米數(shù)。
(4)除法操作數(shù)(/)
除法的運(yùn)算規(guī)則與乘法類似,同樣,這里就不再詳細(xì)介紹了。
(5)模(求余)運(yùn)算(%)
該運(yùn)算符是求得除法運(yùn)算后的余數(shù)。
console.log(10 % 3); // 1
console.log(-10 % 3); // -1
console.log(10 % -3); // 1
console.log(10 % 3.14); // 0.5799999999999996
綜上,模運(yùn)算規(guī)則如下:
- 模運(yùn)算的結(jié)果的符號(hào),與第一個(gè)操作數(shù)相同。模運(yùn)算用于浮點(diǎn)數(shù)時(shí),結(jié)果會(huì)有誤差。
(6)自增(++)與自減(--)
自增和自減有分為前置和后置。
var x = 5;
var y = ++x - 2
/* 等價(jià)于
* 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;
/* 等價(jià)于
* var x = 5;
* var y = x - 2;
* x = x + 1;
*/
console.log(x); // 6
console.log(y); // 3
前置自增與后置自增的區(qū)別是,前置自增先執(zhí)行自增,再執(zhí)行后續(xù)的運(yùn)算,后置自增是先執(zhí)行運(yùn)算,再執(zhí)行自增。同理,自減原理也一樣,就不在贅述了。
(7)x op= y操作
這里把+=
,-=
,*=
,/=
,%=
等復(fù)合運(yùn)算統(tǒng)稱為op=
,那么:
x op= y
大多數(shù)情況下等價(jià)于:
x = x op y
其中,下面這個(gè)表達(dá)式中x計(jì)算了兩次,在x含有副作用的表達(dá)式時(shí),二者就不等價(jià)了。
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 關(guān)系運(yùn)算符
用來判斷一個(gè)操作數(shù)是否大于或小于或等于另外一個(gè)操作數(shù)。
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
綜上,關(guān)系運(yùn)算符返回的都是boolean值,有以下規(guī)則:
- 如果比較的兩個(gè)操作數(shù)都是數(shù)值,則執(zhí)行數(shù)值比較。如果只有一個(gè)操作數(shù)是數(shù)值,則將另外一個(gè)操作數(shù)轉(zhuǎn)換為數(shù)值,再執(zhí)行數(shù)值比較。
- 如果比較的兩個(gè)操作數(shù)都是字符串,則依次比較字符串每個(gè)字符的Unicode值。
- 如果有一個(gè)操作數(shù)是NaN,則執(zhí)行結(jié)果為false,執(zhí)行不相等操作時(shí),執(zhí)行結(jié)果為true。
- null和undefined相等。但不能將null和undefined轉(zhuǎn)化為其它任何值。
- 如果有一個(gè)操作數(shù)是對(duì)象,另外一個(gè)操作數(shù)不是,則會(huì)調(diào)用對(duì)象的valueOf方法得到原始值,再應(yīng)用上面的規(guī)則。
- 當(dāng)兩個(gè)操作數(shù)的值相同,類型也相同,并且都不是NaN時(shí),則兩個(gè)操作數(shù)全等(===)。當(dāng)比較的兩個(gè)操作數(shù)轉(zhuǎn)換為相同類型后的值相等,則兩個(gè)操作數(shù)相等(==)。
4.2.3 邏輯運(yùn)算符
(1)邏輯與(&&)
在boolean環(huán)境下當(dāng)邏輯與的兩個(gè)操作數(shù)同時(shí)為true時(shí),結(jié)果才為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
綜上,邏輯與的使用規(guī)則可以總結(jié)如下:
- 如果第一個(gè)操作數(shù)能轉(zhuǎn)換成false,則返回第一個(gè)操作數(shù),否則返回第二個(gè)操作數(shù)。在boolean環(huán)境中使用時(shí),兩個(gè)操作數(shù)結(jié)果都為true時(shí),返回true,否則返回false。
- 能夠轉(zhuǎn)換為false的值有,0,"",null,undefined。
短路操作:
在執(zhí)行邏輯與操作時(shí),當(dāng)?shù)谝粋€(gè)操作數(shù)的結(jié)果為false時(shí),就不在執(zhí)行第二個(gè)操作數(shù)的求值了。因?yàn)闊o論第二個(gè)操作數(shù)為何值,其結(jié)果都不可能為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環(huán)境下當(dāng)邏輯或的兩個(gè)操作數(shù)任意一個(gè)為true時(shí),結(jié)果都為true。一般,可用來給變量設(shè)置默認(rèn)值。
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
綜上,邏輯或的使用規(guī)則可以總結(jié)如下:
- 如果第一個(gè)操作數(shù)能轉(zhuǎn)換成true,則返回第一個(gè)操作數(shù),否則返回第二個(gè)操作數(shù)。在boolean環(huán)境中使用時(shí),兩個(gè)操作數(shù)任意一個(gè)為true時(shí),返回true,否則返回false。
- 能夠轉(zhuǎn)換為false的值有,0,"",null,undefined。
短路操作:
在執(zhí)行邏輯或操作時(shí),當(dāng)?shù)谝粋€(gè)操作數(shù)的結(jié)果為true時(shí),就不在執(zhí)行第二個(gè)操作數(shù)的求值了。因?yàn)闊o論第二個(gè)操作數(shù)為何值,其結(jié)果都不可能為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)邏輯非(!)
無論操作數(shù)是什么類型的數(shù)據(jù),該操作都會(huì)返回一個(gè)boolean。邏輯非會(huì)先將操作數(shù)轉(zhuǎn)換為一個(gè)boolean,再對(duì)齊求反。
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
綜上,邏輯非的使用規(guī)則可以總結(jié)如下:
- 如果操作數(shù)能轉(zhuǎn)換為true的話,則返回false,否則返回false。
- 能夠轉(zhuǎn)換為false的值有,0,"",null,undefined。
4.2.4 位運(yùn)算符
位運(yùn)算是比較低層次的運(yùn)算,按內(nèi)存中表示數(shù)值的位來操作數(shù)值。JavaScript中所有的數(shù)值都是以64位格式存儲(chǔ),而位操作符是先將64位的值轉(zhuǎn)換成32位的整數(shù),然后執(zhí)行操作,最后再將結(jié)果轉(zhuǎn)換回64位。
對(duì)于有符號(hào)的整數(shù),32中的前31位表示整數(shù)的值,第32位表示數(shù)值的符號(hào),用0表示整數(shù),1表示負(fù)數(shù),因此第32位也叫符號(hào)位。其中,正數(shù)是以二進(jìn)制格式存儲(chǔ)的,負(fù)數(shù)二進(jìn)制補(bǔ)碼的形式存儲(chǔ)的。
(1)原碼、反碼和補(bǔ)碼
原碼,是該數(shù)值的符號(hào)位與絕對(duì)值的二進(jìn)制表示。例如:
2[原碼]: 0000 0000 0000 0000 0000 0000 0000 0010
-2[原碼]: 1000 0000 0000 0000 0000 0000 0000 0010
反碼,正數(shù)的反碼是其原碼。負(fù)數(shù)的反碼,是符號(hào)位不變,其余各位取反,即1變成0,0變成1。例如:
2[反碼]: 0000 0000 0000 0000 0000 0000 0000 0010
-2[反碼]:1111 1111 1111 1111 1111 1111 1111 1101
補(bǔ)碼,正數(shù)的補(bǔ)碼是其原碼。負(fù)數(shù)的補(bǔ)碼,是其反碼加1。例如:
2[補(bǔ)碼]: 0000 0000 0000 0000 0000 0000 0000 0010
-2[補(bǔ)碼]:1111 1111 1111 1111 1111 1111 1111 1110
(2)按位與(&)
按位于,是將兩個(gè)操作數(shù)的二進(jìn)制位對(duì)齊,當(dāng)兩個(gè)數(shù)值的位都為1時(shí),結(jié)果為1,任意一個(gè)為0,則結(jié)果為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)按位或(|)
按位或,是將兩個(gè)操作數(shù)的二進(jìn)制位對(duì)齊,當(dāng)兩個(gè)數(shù)值的位任意一個(gè)為1時(shí),結(jié)果為1,兩個(gè)都為0,則結(jié)果為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)按位非(~)
按位非,是得到該數(shù)值的反碼。
console.log(~3); // -4
3 = 0000 0000 0000 0000 0000 0000 0000 0011
~ = 1111 1111 1111 1111 1111 1111 1111 1100
(5)按位異或(^)
按位異或,是將兩個(gè)操作數(shù)的二進(jìn)制位對(duì)齊,當(dāng)兩個(gè)數(shù)值的位其中只有一個(gè)為1時(shí),結(jié)果為1,兩個(gè)都為0或都為1,則結(jié)果為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)左移(<<)
左移,是將操作數(shù)的所有位移動(dòng)指定的位數(shù),右側(cè)多出的位用0填充。左移不影響操作數(shù)的符號(hào)位。
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)有符號(hào)右移(>>)
有符號(hào)右移,是將操作數(shù)的所有位移動(dòng)指定的位數(shù),并保留符號(hào)位。左側(cè)多出的位用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)無符號(hào)右移(>>>)
無符號(hào)右移,是將操作數(shù)的所有位移動(dòng)指定的位數(shù)。對(duì)于正數(shù),無符號(hào)右移與有符號(hào)右移結(jié)果相同,負(fù)數(shù)會(huì)以補(bǔ)碼的形式右移指定的位。
console.log(12 >>> 2); // 3
console.log(-12 >>> 2); // 1073741821
4.2.5 其它運(yùn)算符
(1)賦值運(yùn)算符(=)
賦值可以和其他運(yùn)算符組合使用。例如:
var x = 3;
console.log(x += 5); // 8
(2)逗號(hào)運(yùn)算符(,)
逗號(hào)運(yùn)算符,可以再一條語(yǔ)句中執(zhí)行多個(gè)操作。如果,逗號(hào)運(yùn)算符用于賦值,則返回表達(dá)式中的最后一項(xiàng)。
var x = 2, y = 3, z = 5;
var pos = (2, 3, 5);
console.log(z); // 5
console.log(pos); // 5
(3)三目運(yùn)算符(?=)
三目運(yùn)算符,格式形如:
variable = boolean_expression ? true_value : false_value
當(dāng)表達(dá)式boolean_expression的值位true時(shí),則返回true_value的值,否則,返回false_value的值。
console.log(1 > 2 ? 1 + 2 : 1 - 2); // -1
5、對(duì)象
在介紹數(shù)據(jù)類型的時(shí)候提到過,在JavaScript中對(duì)象是一組無序的鍵值對(duì)集合,類似其它語(yǔ)言的HashMap、Dictionary等數(shù)據(jù)結(jié)構(gòu)。除數(shù)字、true、false、null、undefined和字符串外,所有的值都是對(duì)象。JavaScript內(nèi)置了Object、Date、Array、Function、RegExp等對(duì)象。所有對(duì)象繼承Object對(duì)象。
5.1 對(duì)象的創(chuàng)建
對(duì)象的創(chuàng)建分為兩種方式:
(1)使用new操作符,后面緊跟構(gòu)造函數(shù)
var student = new Object(); // 等價(jià)于 var student = {};
student.name = "AlphaGL";
student.age = 18;
student.print = function () {
console.log("hello AlphaGL");
}
(2)使用對(duì)象字面量表示法。由若干名/值對(duì)中間用冒號(hào)分割,每對(duì)名/值對(duì)間用逗號(hào)分隔組成的映射表。
var student = {
name : "AlphaGL",
age : 18
print: function () {
console.log("hello AlphaGL");
},
};
5.2 讀取屬性
可以通過點(diǎn)(.)或者中括號(hào)([])的方式獲取對(duì)象屬性的值。
(1)通過點(diǎn)(.)來獲取
var student = {
name : "AlphaGL",
age : 18
};
console.log("name = " + student.name); // name = AlphaGL
(2)通過中括號(hào)訪問屬性的值,中括號(hào)內(nèi)可以是變量且計(jì)算結(jié)果必須是字符串的表達(dá)式。如果屬性名包含回導(dǎo)致語(yǔ)法錯(cuò)誤的字符,或者屬性名使用的是關(guān)鍵字或者保留字,也可以使用中括號(hào)表示。
var name = "nick name";
student[name] = "AlphaGL"; // 等價(jià)于 student["nick name"] = "AlphaGL";
一般推薦使用點(diǎn)的方式去獲取對(duì)象屬性。
5.3 檢測(cè)屬性
(1)hasOwnProperty()方法可以檢測(cè)給定屬性存在于對(duì)象實(shí)例中時(shí),則返回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操作符會(huì)訪問對(duì)象的給定屬性,無論該屬性是存在于實(shí)例中還是原型中都返回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運(yùn)算符可以用來刪除對(duì)象的自有屬性,不會(huì)刪除原型的同名屬性,刪除不存在的屬性在對(duì)象上,delete將不會(huì)起作用,但仍會(huì)返回true。成功刪除的時(shí)候會(huì)返回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對(duì)象
JavaScript中,數(shù)組算是最常用的類型。數(shù)組的大小可以動(dòng)態(tài)調(diào)整,每一項(xiàng)可以保存任何類型的數(shù)據(jù),起始項(xiàng)從0開始。還可以實(shí)現(xiàn)堆棧,隊(duì)列等數(shù)據(jù)結(jié)構(gòu)。
(1)數(shù)組的創(chuàng)建
- 使用Array構(gòu)造函數(shù)創(chuàng)建。
var nums = new Aarray(3); var names = new Array("foo", "bar") var colors = Array("R", "G", "B")
- 使用數(shù)組字面量表示法。即使用中括號(hào),并將每個(gè)元素用逗號(hào)隔開。
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)數(shù)組元素的訪問。
var a = ["AlphaGL", 18, true];
console.log(a[0]); // AlphaGL
console.log(a[1]); // 18
console.log(a[2]); // true
//indexOf返回在數(shù)組中可以找到一個(gè)給定元素的第一個(gè)索引,如果不存在,則返回-1。類似的還有l(wèi)astIndexOf方法。
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數(shù)組下標(biāo)從0開始。
可以使用負(fù)數(shù)或非整數(shù)來索引數(shù)組。這時(shí),數(shù)值將會(huì)轉(zhuǎn)換為字符串,而該索引被當(dāng)作對(duì)象的常規(guī)屬性。如果,使用了非負(fù)整數(shù)的字符串,則它會(huì)被當(dāng)作數(shù)組索引訪問,而不是對(duì)象屬性訪問。
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
由此可知,數(shù)組的訪問只是對(duì)象訪問的一種特殊形式,當(dāng)訪問不存在的屬性時(shí),javascript也不會(huì)報(bào)錯(cuò),只會(huì)返回undefined值。因此,javascript中數(shù)組不存在數(shù)組越界的問題。
(3)數(shù)組元素的添加與刪除
添加元素:
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。從數(shù)組中刪除最后一個(gè)元素,并返回該元素的值
console.log(a.length); // 2
console.log(a.shift()) // AlphaGL。從數(shù)組中刪除第一個(gè)元素,并返回該元素的值
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個(gè)元素開始,刪除0個(gè)元素,即添加元素haha
console.log(a); // [ 'AlphaGL', 18, 'haha', true ]
a.splice(1, 2); // 從第1個(gè)元素開始,刪除2個(gè)元素。
console.log(a); // [ 'AlphaGL', true ]
a.splice(0, 1, "haha"); // 從第0個(gè)元素開始,刪除1個(gè)元素,并添加haha元素。
console.log(a); // [ 'haha', true ]
注:刪除數(shù)組元素與將數(shù)組元素賦值為undefined值類似。使用delete不是修改數(shù)組的length屬性,也不會(huì)移動(dòng)后繼元素位置。其它操作方法基本都會(huì)移動(dòng)數(shù)組元素和改變數(shù)組length值。也可以直接操作數(shù)組的length屬性來達(dá)到輸出元素的目的。push和pop方法提供了類似棧結(jié)構(gòu)的操作。push和shift方法則提供了類似隊(duì)列結(jié)構(gòu)的操作。splice有替換數(shù)組中任意數(shù)量的項(xiàng)的作用。
(4)數(shù)組的檢測(cè)
var a = ["AlphaGL", 18, true];
console.log(Array.isArray(a)); // true
console.log(a instanceof Array); // true
注:當(dāng)存在兩個(gè)以上的全局執(zhí)行環(huán)境時(shí),即存在兩個(gè)以上不同版本的Array構(gòu)造函數(shù),instanceof則只能在單一的全局執(zhí)行環(huán)境有效。
(5)數(shù)組的排序
var a = [1, 11, 57, 7, 23];
a.sort(function (p1, p2) { // 使用比較函數(shù)來對(duì)數(shù)組元素進(jìn)行排序。返回的值小于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(); // 逆序數(shù)組。
console.log(a); // [ true, 18, 'AlphaGL' ]
(6)數(shù)組的遍歷與迭代
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方法會(huì)對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)的每一項(xiàng)都返回true,則結(jié)果才為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方法會(huì)對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定的函數(shù),并返回該函數(shù)會(huì)返回為true的項(xiàng)組成的新數(shù)組。
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方法同樣會(huì)對(duì)數(shù)組中每一項(xiàng)運(yùn)行給定的函數(shù)。該方法沒有返回值。
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方法會(huì)將每次運(yùn)行給定的函數(shù)返回的值,組成一個(gè)新的數(shù)組。
var a = [1, 11, 57, 7, 23];
var t1 = a.some(function (element, index, array) {
return element > 50;
});
console.log(t1); // true
注:map方法同樣會(huì)對(duì)數(shù)組中的每一項(xiàng)都運(yùn)行給定的函數(shù),如果該函數(shù)的任一項(xiàng)結(jié)果為true,則返回true。
(7)其它
當(dāng)然,數(shù)組還有一些其它的用法和函數(shù)。這里就不一一介紹了。感興趣的,可以參考文末列舉的參考鏈接。
6、函數(shù)
函數(shù),簡(jiǎn)單描述就是可重復(fù)調(diào)用多次的功能模塊。在JavaScript中,每個(gè)函數(shù)都是Function類型的實(shí)例,因此也一樣具有屬性和方法。函數(shù)名也是對(duì)象,可以把函數(shù)當(dāng)作值來使用,這樣就提供極大的靈活性。
6.1 函數(shù)的定義
在JavaScript中,函數(shù)的定義有如下幾種實(shí)現(xiàn)方式:
(1)function關(guān)鍵字+函數(shù)名+參數(shù)列表+花括號(hào)構(gòu)成的語(yǔ)句塊,例如:
function foo(p1, p2) {
return p1 + p2;
}
console.log(typeof(foo)); // function
console.log(foo(3, 4)); // 7
(2)使用Function構(gòu)造函數(shù)。一般,不推薦這種使用方法。
var foo = new Function("p1", "p2", "return p1 + p2");
console.log(foo(3, 4)); // 7
(3)函數(shù)表達(dá)式
// 聲明了一個(gè)匿名函數(shù),并賦值給foo變量。
var foo = function(p1, p2) {
return p1 + p2;
}
console.log(foo(3, 4)); // 7
// 函數(shù)表達(dá)式也可以包含名稱
var bar = function sum(p) {
if(p <= 1) {
return 1;
}else {
return p + sum(p - 1);
}
}
console.log(bar(5)); // 15
// 聲明即調(diào)用
var sum = function(p1, p2) {
return p1 + p2;
}(3, 4);
console.log(sum); // 7
6.2 函數(shù)的參數(shù)與內(nèi)部屬性
JavaScript中函數(shù)定義并未指定函數(shù)參數(shù)的類型,調(diào)用時(shí)也未對(duì)實(shí)參的值做類型檢查,同樣也不檢查參數(shù)個(gè)數(shù)。
6.2.1 函數(shù)的參數(shù)
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
當(dāng)形參與實(shí)參的個(gè)數(shù)不匹配時(shí),少的參數(shù)將被置為undefined,多的參數(shù)將被丟棄。
6.2.2 函數(shù)的內(nèi)部屬性
在函數(shù)內(nèi)部,有個(gè)特殊的對(duì)象arguments。該對(duì)象用來保存函數(shù)參數(shù),可以像數(shù)組樣使用數(shù)字索引來訪問參數(shù),同樣它也包含length屬性。但它并不是真正的數(shù)組。另外,該對(duì)象還包含callee屬性,該屬性指向擁有這個(gè)arguments對(duì)象的函數(shù)。
function foo(p1, p2, p3) {
console.log(arguments.length); // 3
console.log(arguments[0]); // 第一個(gè)參數(shù),即:1
console.log(arguments[1]); // 第二個(gè)參數(shù),即:2
console.log(arguments[2]); // 第三個(gè)參數(shù),即: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 函數(shù)的屬性
前面提到過,每個(gè)函數(shù)都是Function類型的實(shí)例,因此也一樣具有屬性和方法。函數(shù)有以下比較常用的屬性。
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:函數(shù)的名稱。
foo.length:形參的個(gè)數(shù)。
arguments.length:實(shí)參的個(gè)數(shù)。
arguments.callee.length:形參的個(gè)數(shù)。
6.4 閉包
閉包(closure)是函數(shù)型語(yǔ)言的一個(gè)重要的特性,許多高級(jí)特性都依賴閉包來實(shí)現(xiàn)。閉包,是創(chuàng)建在一個(gè)函數(shù)內(nèi)部的函數(shù),能夠訪問函數(shù)內(nèi)部的變量,并保存在內(nèi)存中,記錄上次運(yùn)行的結(jié)果,即保存了創(chuàng)建時(shí)的上下文環(huán)境信息。因此,可以簡(jiǎn)單總結(jié)為:
閉包=函數(shù)內(nèi)部創(chuàng)建的函數(shù) + 該函數(shù)創(chuàng)建時(shí)的上下文環(huán)境信息
例如:
function counter() {
var count = 0;
return function() {
return count++;
}
}
var foo = counter();
console.log(foo()); // 0
console.log(foo()); // 1
console.log(foo()); // 2
閉包的這種機(jī)制,就實(shí)現(xiàn)面向?qū)ο蟮姆庋b提供了支持。將私有變量封裝在內(nèi)部,提供外包接口函數(shù),來訪問該變量。
構(gòu)造函數(shù)
函數(shù)內(nèi)部屬性
函數(shù)的作用域
reduce
7、面向?qū)ο?/h3>
前面提到過,JavaScript中所有的都是對(duì)象。在面向?qū)ο缶幊讨校瑢?duì)象是類的實(shí)例,而類是具有相同屬性和行為的一類對(duì)象的抽象和集合。例如:獅子對(duì)象是動(dòng)物這一類型中的一個(gè)實(shí)例。面向?qū)ο缶幊逃腥筇匦裕悍庋b,繼承和多態(tài)。
7.1 構(gòu)造函數(shù)
前面提到過,使用new關(guān)鍵字調(diào)用構(gòu)造函數(shù)可以創(chuàng)建一個(gè)新對(duì)象。
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關(guān)鍵字指向了,當(dāng)前要?jiǎng)?chuàng)建的對(duì)象。
7.2 原型與繼承
每個(gè)對(duì)象都有一個(gè)私有屬性(prototype)原型,該屬性指向該對(duì)象的原型對(duì)象。可以理解為其他編程語(yǔ)言中的,指向基類或者父類的作用。當(dāng)然,該原型對(duì)象同樣有一個(gè)自己的prototype,層層向上直到該對(duì)象的原型為null。null沒有原型。JavaScript中幾乎所有的對(duì)象都是位于原型鏈頂端的Object的實(shí)例,同樣可以理解為,都是Object的子類。因此,使用原型對(duì)象可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。
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
上面,創(chuàng)建兩個(gè)不同的對(duì)象實(shí)例,getName實(shí)現(xiàn)了相同的功能,卻每個(gè)對(duì)象中都保留了一份,造成不必要的浪費(fèi)。這就需要通過原型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 訪問屬性規(guī)則
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); // 小趙
當(dāng)訪問對(duì)象的某個(gè)屬性時(shí),會(huì)根據(jù)給定的屬性名稱來查找。如果,在該對(duì)象的實(shí)例中找到該屬性,則返回屬性的值;否則,則繼續(xù)查找該對(duì)象的原型對(duì)象,在原型對(duì)象中查找該屬性,依次層層向上搜索,直到搜索到原型鏈的末尾。因此,對(duì)象的屬性會(huì)覆蓋同名的該對(duì)象的原型對(duì)象的同名屬性。
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
通過以上實(shí)例可以總結(jié)如下:
object instanceof constructor
運(yùn)算符,用來檢測(cè) constructor.prototype是否存在于參數(shù)object的原型鏈。雖然,右操作數(shù)是構(gòu)造函數(shù),但實(shí)際上檢測(cè)了對(duì)象的繼承關(guān)系,而不是檢測(cè)創(chuàng)建對(duì)象的構(gòu)造函數(shù)。prototypeObj.isPrototypeOf(object)
檢查一個(gè)對(duì)象是否存在于另一個(gè)對(duì)象的原型鏈上。可以理解為object對(duì)象是否繼承自prototypeObj.prototype。