題記
從[我的博客]中搬運(yùn)過來的,主要的目的是用于自己學(xué)習(xí)和整理思路,以及用到時(shí)查詢的作用。在hexo的博客主題中,實(shí)現(xiàn)了目錄導(dǎo)航定位的效果。不知道簡書上怎么樣?
目錄
-
第三章
- 3.1 語法
- 3.4 數(shù)據(jù)類型
- 3.5 操作符
- 3.6 語句
- 3.7 函數(shù)
-
[第五章 引用類型](#第五章 引用類型)
- 5.2 [數(shù)組類型](#5.2 數(shù)組類型)
- 5.3 [Date類型](#5.3 Date類型)
-
- 6.1 [理解對(duì)象](#6.1 理解對(duì)象)
- 6.2 [創(chuàng)建對(duì)象](#6.2 創(chuàng)建對(duì)象)
第二章 javascript簡介
- netscape創(chuàng)建LiveScript腳本語言。搭java順風(fēng)車就改為javascript。
- js三部分:ECMAScript,dom,bom
- script標(biāo)簽的defer和async屬性只針對(duì)外部文件,一般不用,沒有這兩者的時(shí)候,就會(huì)按照在頁面中出現(xiàn)的位置先后執(zhí)行的。
- js文件放在html外部的優(yōu)勢(shì):可維護(hù)性,可緩存,適應(yīng)未來。
- 如果瀏覽器不支持javascript,那么可以用noscript標(biāo)簽來寫出替代說明文字。
第三章 基本概念
3.1 語法
- 區(qū)分大小寫
- 標(biāo)識(shí)符
- 注釋
單行(//) 多行(/* */) - 嚴(yán)格模式 use strict
javascript的一種不同的解析與執(zhí)行模型。一些不確定的行為將得到處理以及某些不安全的操作會(huì)拋出錯(cuò)誤。 - 語句
3.2 關(guān)鍵字和保留字
全部關(guān)鍵字:
break,case,catct,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with
3.4 數(shù)據(jù)類型
- 五個(gè)基本的數(shù)據(jù)類型:
string,number,boolean,null,undefined - 一個(gè)復(fù)雜類型:
object
3.4.1 typeof操作符
var huang = "hzhaung";
var zhuang=null;
console.log(typeof(huang)); //string
console.log(typeof(95)); //number
console.log(typeof(1 == 2)); //booleam
console.log(typeof(a)); //undefined
console.log(typeof(zhuang)); //object 原因:null是被認(rèn)為是一個(gè)空的對(duì)象指針
3.4.2 Undefined類型
未申明的變量以及為初始化的變量都是undefined。
3.4.3 null類型
null值表示一個(gè)空的對(duì)象指針。
null == undefined 返回true
注意的是一般設(shè)置變量為null是為了用這個(gè)變量來保存對(duì)象,故而要寫出var xx=null。
3.4.4 boolean類型
- false,"",0+NAN,null,undefined都是false。換句話說就是其他的都是true了。
- 區(qū)別大小寫。
3.4.5 number類型
var num1 = 070; //八進(jìn)制
var num2 = 0xA; //十六進(jìn)制
var num3 = 10; //十進(jìn)制
- NUMBER.MIN_VALUE或者NUMBER.MAX_VALUE超出就會(huì)自動(dòng)轉(zhuǎn)換成infinity,如果是負(fù)的就在前面加上-。
ifFinity( ):判斷數(shù)值是否在有限數(shù)值范圍內(nèi),true表示在,false表示不在。
NaN(not a number)
- 任意涉及NaN的操作都會(huì)返回NaN;
- NaN與任何值都不相等,包含NaN本身。
isNaN( ):接受參數(shù)后會(huì)將其轉(zhuǎn)化成數(shù)值,不能轉(zhuǎn)化的話就會(huì)返回true。
console.log(isNaN("huang")); //true
console.log(isNaN(true)); //false
console.log(isNaN(NaN)); //true
數(shù)值轉(zhuǎn)換:
| 函數(shù) | 作用對(duì)象 |
|---|---|
| Number( ) | 可用于任何數(shù)據(jù)類型 |
| parseInt( ) | 針對(duì)字符串 |
| parseFloat( ) | 針對(duì)字符串 |
Number的轉(zhuǎn)換規(guī)則:
| 參數(shù) | 結(jié)果 |
|---|---|
| boolean | false-0;true-1 |
| number | 簡單的傳入換個(gè)返回 |
| null | 0 |
| undefined | NaN |
| string | “123”-123;“1.1”-1.1;“0xf”-15;“”-0;其他-NaN) |
| object | 調(diào)用valueOf( ),然后依照前面的規(guī)則 |
| NaN | 調(diào)用toString( ),然后依照前面的規(guī)則 |
parseInt( )示例:
console.log(parseInt("1234blue")); //1234
console.log(parseInt("0xA")); //0
console.log(parseInt("22.5")); //22
var num1 = parseInt("10",2); //2
var num2 = parseInt("10",8); //8
var num2 = parseInt("10",10); //10
var num2 = parseInt("10",16); //16
3.4.6 String類型
- toString( )除null和undefined,因?yàn)樗麄儧]有這個(gè)方法。一般是不用參數(shù)的,可以加一個(gè)參數(shù)來表示數(shù)值的基數(shù)。
- String( )規(guī)則:null返回"null",undefined返回“undefined”。(在不知道要轉(zhuǎn)化的值是不是null或者undefined)
3.4.7 Object類型
一組數(shù)據(jù)和功能的集合。每個(gè)實(shí)例具有以下的屬性和方法。對(duì)象是實(shí)例的基礎(chǔ)。
| 屬性 | 作用 |
|---|---|
| Constructor | 保存用于創(chuàng)建當(dāng)前對(duì)象的函數(shù),構(gòu)造函數(shù) |
| hasOwnproperty(propertyName) | 檢查給定的屬性在當(dāng)前的實(shí)例中是否存在。不是在實(shí)例的原型中。 |
| isPrototypeof(object) | 檢查對(duì)象是否是另一個(gè)對(duì)象的原型。 |
| propertyIsEnumerable | 檢查是否可以用for-in來枚舉。 |
| toLocaleString( ) | 返回對(duì)象的字符串表示。 |
| toString( ) | 返回對(duì)象的字符串表示。 |
| valueOf( ) | 返回對(duì)象的字符串,數(shù)值或布爾值表示,通常和上個(gè)方法的返回值相同。 |
3.5 操作符
在對(duì)對(duì)象使用操作符的時(shí)候,要調(diào)用valueOf( )或toString( )來獲得可以操作的值。
3.5.1 一元操作符
一元操作符:++,--,+,-。前置和后置的區(qū)別:前置的時(shí)候語句也改變。
遞增和遞減操作符的規(guī)則如下:
var str = "1";
var str1 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function(){
return -1;
}
};
console.log(++str); //2
console.log(++str1);//NaN
console.log(++b); //1
console.log(++f);//2.1
console.log(++o);//0
//如果是對(duì)象,則會(huì)先調(diào)用valueOf(),然后根據(jù)返回的值來確定加減之后的值。
3.5.2 位操作符
32位,最后一個(gè)位表示符號(hào)位,0位正1為負(fù)。 正數(shù)就是常見的,對(duì)于負(fù)數(shù)則是以補(bǔ)碼的形式儲(chǔ)存(絕對(duì)值,反碼,加1)。
但是輸出負(fù)數(shù)時(shí)候這些操作都是隱藏的,輸出的時(shí)候就是常見負(fù)數(shù)在前面加符號(hào)的形式。
var num = -18;
console.log(num.toString(2));//"-10010"
對(duì)于非數(shù)值使用位操作符,則先自動(dòng)調(diào)用Number( ),然后再應(yīng)用位操作。
- 按位非( ~)
返回?cái)?shù)值的反碼:操作數(shù)的負(fù)數(shù)減1。
var num = 29;
var num1 = ~ num;
console.log(num1); //-26
- 按位與
- 按位或
- 按位異或
- 左移(<<)
移出的空位就以0補(bǔ)充。 - 右移
3.5.3 布爾操作符
1.邏輯非
對(duì)象-false;空-true;非空-false;0-true;任意非0-false;null-true;NaN-true;undefined-true
2.邏輯與
var result = true && false;
3.邏輯或
3.5.4 乘性操作符
乘法,除法和求模
如果有一個(gè)不是數(shù)值,則會(huì)先自動(dòng)調(diào)用number()之后再返回結(jié)果。
......
3.5.5 加性操作符
加法特殊:
- 兩個(gè)字符串:就拼接起來。
- 只有一個(gè)字符串,則將另一個(gè)轉(zhuǎn)換成字符串,然后拼接。
- 有一個(gè)是對(duì)象,數(shù)值或者布爾值,則調(diào)用toString( ),然后再拼接。
- 對(duì)于undefined和null,則調(diào)用String( )取到"undefined"和"null"。
console.log(5+"5"); //"55"
減法特殊:
不是數(shù)值的調(diào)用Number( )就OK啦。
3.5.6 關(guān)系操作符
規(guī)則:如示例代碼
console.log(5 > 4);//true
console.log("h" > "z") //false 轉(zhuǎn)換為字符編碼比較。
console.log(5<"2") //false "2"自動(dòng)轉(zhuǎn)化成2
3.5.7 相等操作符
| 數(shù)據(jù)類型 | 說明 |
|---|---|
| 一個(gè)boolean | 先轉(zhuǎn)化成數(shù)值然后再判斷 |
| string+number | string轉(zhuǎn)化成數(shù)值先 |
| 對(duì)象+非對(duì)象 | 調(diào)用對(duì)象的valueOf( )方法,得到的值再比較 |
| null+undefined | 相等 |
| 有NaN | false |
| 對(duì)象+對(duì)象 | 比較是不是同一個(gè)人對(duì)象 |
全等和不全等:
- ===:未經(jīng)轉(zhuǎn)化的情況下相等才會(huì)返回true
null == undefined -----true
null === undefined -----false
3.5.8 條件操作符
variable = boolean_expression ? true_value : false_value ;
3.5.9 賦值操作符
3.5.10 逗號(hào)操作符
var num1,num2,num3;
3.6 語句
整理不常用的,if,for就整理啦。
3.6.2 do-while語句
只有在循環(huán)體重的代碼執(zhí)行之后,才會(huì)測(cè)試出口條件。即,循環(huán)體內(nèi)的代碼至少會(huì)執(zhí)行一次。
var i = 0;
do{
i += 2;
}while(i < 10);
3.6.5 for-in 語句
一種準(zhǔn)確的迭代語句,可以用來枚舉對(duì)象的屬性。
3.6.6 label語句
標(biāo)簽 labe:statement。這個(gè)標(biāo)簽可以通過break或者continue來引用,一般與for語句配合使用。
3.6.7 break和continue語句
- break會(huì)立即退出循環(huán)
- continue也會(huì)立即退出循環(huán),但是退出后會(huì)從循環(huán)的頂部繼續(xù)執(zhí)行。
var no = 0;
for(var i= 1;i<10;i++){
if(i % 5 == 0){
continue;
}
no++ ;
}
console.log(no);//8
var no = 0;
for(var i= 1;i<10;i++){
if(i % 5 == 0){
break;
}
no++ ;
}
console.log(no);//4
結(jié)合label可以說明是退出的那個(gè)循環(huán)。
var num = 0;
outermost:
for(var i=0;i<10;i++){
for(var j=0;j<10;j++){
if(i == 5 && j==5){
break outermost;
}
}
}
3.6.8 with語句
將代碼的作用域設(shè)置到一個(gè)特定的對(duì)象中。
with(location){
var gs = search.substring(1); //等價(jià)于var gs = location.search.substring(1);
var hostName = hostname;
var url = href;
}
3.7 函數(shù)
好處:封裝,任意時(shí)刻調(diào)用。
ECMAScript中函數(shù)可以通過return語句返回值。執(zhí)行return后就會(huì)停止。如果return語句不帶返回的值,則會(huì)默認(rèn)返回undefined。
注:eval和arguments不可以定義為變量。
函數(shù)的參數(shù):
- 函數(shù)內(nèi)部的參數(shù)是一個(gè)數(shù)組,arguments對(duì)象。 類似array,arguments[0]是合法的,而且也有l(wèi)ength屬性。故: 在定義函數(shù)的時(shí)候參數(shù)是非必須的,在調(diào)用的時(shí)候加上參數(shù)也不會(huì)出問題。這是區(qū)別其他語言的。
- arguments對(duì)象可以和命名的參數(shù)一起使用。但是兩者的內(nèi)存空間是不一樣的。
3.7.2 沒有重載
沒有重載:由于js函數(shù)的特點(diǎn),則是無法實(shí)現(xiàn)重載的,如果存在兩個(gè)則后取。
第五章 引用類型
5.2 Array類型
js的數(shù)組可以保存任何類型的數(shù)據(jù),也可以自動(dòng)增長。
// 創(chuàng)建方法一:Array構(gòu)造函數(shù)
var colors = new Array();
//創(chuàng)建長度是20
var colors = new Array(20);
// 創(chuàng)建方法二:數(shù)組字面兩表示法。
var colors = ["red", "yellow", "white"];
數(shù)組不僅僅是可讀的。如下:
var colors = ["red", "blue", "green"];
colors.length = 2;
alert(colors[2]); //undefined 第三項(xiàng)移除了
5.2.1檢測(cè)數(shù)組
if(value instanceof Array){
xxx;
}
但是這種方法是有問題:
- 假定單一的全局執(zhí)行環(huán)境,如果有多個(gè)框架,則就會(huì)有多個(gè)全局執(zhí)行環(huán)境。從而存在多個(gè)不同版本的構(gòu)造函數(shù)。
改進(jìn):isArray( )
if(Array.isArray(value)){
xxx;
}
5.2.1轉(zhuǎn)換方法
| 轉(zhuǎn)換方法 | 說明 |
|---|---|
| toLocaleString( ) | 類似toString( ) |
| toString( ) | 會(huì)返回由數(shù)組中每個(gè)值得字符串形式憑借而成的一個(gè)以逗號(hào)分隔的字符串。 |
| valueOf( ) | 返回的還是數(shù)值 |
eg:
var colors=["red","blcak","white"]
console.log(colors.valueOf());//["red", "blcak", "white"]
console.log(colors.toString());//red,blcak,white
join( ):方法講數(shù)組按照()內(nèi)的參數(shù)連接起來。
var colors=["red","blcak","white"]
var test_one = colors.valueOf().join();
var test_two = colors.valueOf().join("-");
console.log(test_one);//red,blcak,white
console.log(test_two);//red-blcak-white
5.2.3棧方法
數(shù)組可以表現(xiàn)的像棧一樣,棧是一種可以限制插入和刪除項(xiàng)的數(shù)據(jù)結(jié)構(gòu)。后進(jìn)先出。
| 方法 | 功能 |
|---|---|
| push( ) | 將參數(shù)里面的對(duì)象逐個(gè)添加到數(shù)組中,并返回?cái)?shù)組修改后的長度值。 |
| pop( ) | 與push的作用相反,但是返回值是數(shù)組的最后一項(xiàng) |
var colors=["red","blcak","white"]
console.log(colors.push("huang" , "zhuang"))//5;
5.2.4 隊(duì)列方法
隊(duì)列的訪問規(guī)則是“先進(jìn)先出”,表現(xiàn)在隊(duì)列的末端添加項(xiàng),在隊(duì)列的前端移除項(xiàng)。
| 方法 | 功能 |
|---|---|
| shift( ) | 移除數(shù)組中的第一項(xiàng),并返回該項(xiàng) |
| unshift( ) | 與shift的作用相反,可以在數(shù)組的前端添加任意個(gè)項(xiàng)并返回?cái)?shù)組的長度。 |
//模擬隊(duì)列的組合
push( ) + shift( )
unshift( ) + pop( )
5.2.5 重排序方法
| 方法 | 功能 |
|---|---|
| reverse( ) | 反轉(zhuǎn)數(shù)組項(xiàng)的排序 |
| sort( ) | 按照升序的方法排列數(shù)組項(xiàng),會(huì)調(diào)用每個(gè)項(xiàng)的toString( )轉(zhuǎn)型方法,比較的是字符串 |
var values = [0, 1, 5, 10, 15];
values.sort( );
console.log(values);//[0, 1, 10, 15, 5]因?yàn)槭潜容^的字符串,所以5在最后。
可見以上的sort( )的方法不夠完美。
sort( )可以接受一個(gè)比較函數(shù)作為參數(shù)來完善不足。
function compare(value1, value2) {
return value1 - value2;
}
values.sort(compare);
console.log(values);//[0, 1, 5, 10, 15]
可見,完美解決,達(dá)到排序的效果。
5.2.6 操作方法
| 方法 | 功能 |
|---|---|
| concat( ) | 基于當(dāng)前數(shù)組中的所有項(xiàng)創(chuàng)建一個(gè)新的數(shù)組(副本),然后將接受到的參數(shù)添加到這個(gè)副本的末尾。 |
| slice( ) | 基于當(dāng)前的數(shù)組中的一個(gè)或者多個(gè)創(chuàng)建一個(gè)新的數(shù)組 |
| splice( ) | 向數(shù)組的中部插入項(xiàng)。 |
// concat
var colors = ["red", "green", "yellow"];
var color_new = colors.concat("huang",["zhuang","hz"]);
console.log(color_new);//["red", "green", "yellow", "huang", "zhuang", "hz"]
// slice
// 接受一個(gè)或者兩個(gè)參數(shù)指定位置開始和到當(dāng)前數(shù)組末尾的所有項(xiàng)。不會(huì)影響原始數(shù)組。
var colors = ["red","green","blue","yellow","purple"];
var colors2 = colors.slice(1);
console.log(colors2);//["green", "blue", "yellow", "purple"]
var colors3 = colors.slice(1,4);
console.log(colors3);//["green", "blue", "yellow"]
splice( )詳解:
1.刪除
可以刪除任意數(shù)量的項(xiàng),指定兩個(gè)參數(shù):要?jiǎng)h除的第一項(xiàng)的位置和要?jiǎng)h除的項(xiàng)數(shù)。會(huì)返回刪除的項(xiàng)。返回的是一個(gè)數(shù)組。
var colors = ["red","green","blue","yellow","purple"];
var remove = colors.splice(0, 2);
console.log(colors);//["blue", "yellow", "purple"]
console.log(remove);//"red", "green"]
2.插入
可以添加任意數(shù)量的項(xiàng),提供3個(gè)參數(shù),起始位置,0和要插入的項(xiàng)。
var removed = colors.splice(1,0,"huang","zhuang");
console.log(colors);//["blue", "huang", "zhuang", "yellow", "purple"]
console.log(removed);//[ ]
3.替換
可以在指定的地方出入任意數(shù)量的項(xiàng),同事刪除任意數(shù)量的項(xiàng)。
var removed = colors.splice(1,1,"huang","zhuang");
console.log(colors);//["blue", "huang", "zhuang", "zhuang", "yellow", "purple"]
console.log(removed);//["huang"]
發(fā)現(xiàn):
- splice( )始終會(huì)返回一個(gè)數(shù)組。
- 返回的數(shù)組包含從原始數(shù)組中刪除的項(xiàng),如果沒有刪除過,就會(huì)返回一個(gè)空數(shù)組。
5.2.7 位置方法
| 方法 | 功能 |
|---|---|
| indexOf( ) | 返回項(xiàng)在數(shù)組中的位置 |
| lastIndexOf( ) | 同上 |
接受兩個(gè)參數(shù):第一個(gè)是要查找的項(xiàng),第二個(gè)參數(shù)表示的是表示起點(diǎn)位置的索引。
5.2.8 迭代方法
| 方法 | 功能 |
|---|---|
| every( ) | 對(duì)數(shù)組的每一項(xiàng)運(yùn)行給定函數(shù),全部是true就會(huì)返回true |
| filter( ) | 對(duì)數(shù)組的每一項(xiàng)運(yùn)行給點(diǎn)函數(shù),返回改函數(shù)會(huì)返回true的項(xiàng)組成的數(shù)組 |
| foeEach( ) | 對(duì)數(shù)組的每一項(xiàng)運(yùn)行給點(diǎn)函數(shù),無返回值 |
| map( ) | 對(duì)數(shù)組的每一項(xiàng)運(yùn)行給點(diǎn)函數(shù),返回每次調(diào)用函數(shù)的結(jié)果組成的數(shù)組 |
| some( ) | 對(duì)數(shù)組的每一項(xiàng)運(yùn)行給點(diǎn)函數(shù),如果該函數(shù)給任一項(xiàng)返回true,則就會(huì)返回true |
//filter
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item){
return (item >2);//[3, 4, 5, 4, 3]
});
console.log(filterResult);
//map
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item){
return item*2;
});
console.log(mapResult);//[2, 4, 6, 8, 10, 8, 6, 4, 2]
5.2.9 縮小方法
| 方法 | 功能 |
|---|---|
| reduce( ) | 迭代數(shù)組的所有項(xiàng),然后構(gòu)建一個(gè)最終返回的值,從第一項(xiàng)開始 |
| reduceRight( ) | 迭代數(shù)組的所有項(xiàng),然后構(gòu)建一個(gè)最終返回的值,從最后一項(xiàng)開始 |
作為參數(shù)的函數(shù)接受4個(gè)參數(shù):前一個(gè)值,當(dāng)前值,項(xiàng)的索引和數(shù)組對(duì)象。這個(gè)函數(shù)返回的任何值都會(huì)作為第一個(gè)參數(shù)自動(dòng)傳給下一項(xiàng)。第一次迭代發(fā)生在數(shù)組的第二項(xiàng),因此第一個(gè)參數(shù)是數(shù)組的第一項(xiàng),第二個(gè)參數(shù)就是數(shù)組的第二項(xiàng)。
var values =[1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
return prev+cur;
});
console.log(sum);//15
5.3 Date類型
第六章 面向?qū)ο蟮某绦蛟O(shè)計(jì)
內(nèi)容:
- 理解對(duì)象屬性
- 理解并創(chuàng)建對(duì)象
- 理解繼承
6.1 理解對(duì)象
6.1.1 屬性類型
內(nèi)部才有的特性,用[[...]]來表示的。
1.數(shù)據(jù)屬性
| 屬性 | 描述 |
|---|---|
| [[Configurable]] | 能否delete刪除屬性從而重新定義、能否修改屬性的特性、能否把屬性修改為訪問器屬性。 |
| [[Enumerable]] | 能否通過for-in枚舉。默認(rèn)是true |
| [[Eritable]] | 能否修改屬性的值 |
| [[Value]] | 包含這個(gè)屬性的數(shù)據(jù)值 |
<script>
var person = {
name:"HuangZhuang";
}
</script>
如上所示:創(chuàng)建一個(gè)對(duì)象,它的默認(rèn)內(nèi)部屬性前三個(gè)都是true,然后[[value]]被設(shè)置成了一個(gè)特定的值HuangZhuang。對(duì)象創(chuàng)建name屬性,[[Value]]被設(shè)置了,在對(duì)name屬性值得任何時(shí)候的修改都會(huì)反映在這個(gè)位置。
修改特性的默認(rèn)值,只能調(diào)用:
Object.defineProperty(屬性所在對(duì)象,"屬性名字","一個(gè)描述符對(duì)象")
如果要修改,則:
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "hz"
})
console.log(person.name); //hz
person.name= "hh";
console.log(person.name); //hz
有一點(diǎn)注意:就是當(dāng)Configurable是false時(shí),其他三個(gè)的特性是要受到限制的,而且設(shè)定Object.defineProperty后,這四個(gè)特性均是默認(rèn)設(shè)定成false的。
2.訪問器屬性
訪問器屬性不包含數(shù)組,但是包含一對(duì)getter和setter函數(shù)。
| 屬性 | 描述 |
|---|---|
| [[Configurable]] | 能否delete刪除屬性從而重新定義、能否修改屬性的特性、能否把屬性修改為訪問器屬性。 |
| [[Enumerable]] | 能否通過for-in枚舉。默認(rèn)是true |
| [[Get]] | 讀取屬性時(shí)調(diào)用的函數(shù) |
| [[Set]] | 寫入屬性時(shí)調(diào)用的函數(shù) |
訪問器屬性不可以直接定義,必須通過Object.defineProperty( )來定義。
var book = {
_year : 2004,
edition: 1
};
Object.defineProperty(book, "year",{
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004){
this._year = newValue;
this.edition += newValue - 2004;
}
}
})
book.year = 2005;
alert(book.edition); //2
下劃線表示只能通過對(duì)象方法訪問的屬性。
6.1.2 定義多個(gè)屬性
Object.defineProperties(第一個(gè)對(duì)象是要添加和修改其屬性的對(duì)象,第二個(gè)對(duì)象的屬性與第一個(gè)對(duì)象中要添加或修改的屬性一一對(duì)應(yīng))
6.1.3 讀取屬性的特性
一個(gè)函數(shù)Object.getOwnPropertyDescriptor( )針對(duì)屬性是數(shù)據(jù)類型還是訪問器類型返回出內(nèi)部屬性的值。
var a = Object.getOwnPropertyDescriptor(book, "year");
console.log(a);
6.2 創(chuàng)建對(duì)象
Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢詣?chuàng)建單個(gè)對(duì)象,但是這種方式是有明顯的缺點(diǎn)的:使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的額重復(fù)代碼。
6.2.1 工廠模式
用函數(shù)來封裝以特定的接口創(chuàng)建對(duì)象的細(xì)節(jié)。
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var o1 = createPerson("hz", 25, 'IT');
......
但是卻沒有解決對(duì)象識(shí)別的問題,即怎樣知道一個(gè)對(duì)象的類型。
6.2.2 構(gòu)造函數(shù)模式
特點(diǎn):
- 沒有顯示的創(chuàng)建對(duì)象
- 直接將屬性和方法賦值給this對(duì)象
- 沒有return語句
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
};
var Person1 = new Person("hz", 25, "IT");
構(gòu)造函數(shù)始終都應(yīng)該以一個(gè)大寫字母開頭,非構(gòu)造函數(shù)才是用小寫字母開頭。
以上的例子中,要?jiǎng)?chuàng)建一個(gè)Person的新實(shí)例,要使用new操作符。經(jīng)理四個(gè)步驟:
- 1創(chuàng)建一個(gè)新對(duì)象。
- 2將構(gòu)造函數(shù)打的作用域賦值給新對(duì)象,this就會(huì)指向新對(duì)象。
- 3執(zhí)行構(gòu)造函數(shù)中的代碼。
- 4返回新對(duì)象。
Person創(chuàng)建的實(shí)例,都有一個(gè)指向Person的Constructor屬性。
console.log(Person1.constructor == Person ); //true
構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別就是調(diào)用方式。new 操作符來調(diào)用函數(shù)就是構(gòu)造函數(shù),如果沒有就和普通函數(shù)沒有區(qū)別的。
// 作為構(gòu)造函數(shù)使用
var person = new Person("hz", 25, "IT");
person.sayName();//hz
//作為普通函數(shù)使用
Person("hz", 25, "IT");//添加到window對(duì)象
window.sayName();
// 在另一個(gè)對(duì)象中使用
var o = new Object();
Person.call(o, "hz", 25, "IT"); //在o對(duì)象的特殊作用域中調(diào)用,o就擁有了所有屬性和方法。
o.sayName();
構(gòu)造函數(shù)的問題就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。就是說不同實(shí)例上的同名函數(shù)實(shí)際上是不一樣的。如果把這些方法通過全局作用域的函數(shù)調(diào)出來可以解決,但是當(dāng)需要很多方法時(shí),封裝性太差。解決方法:原型模式。
var Person1 = new Person("hz", 25, "IT");
var Person2 = new Person("hz", 25, "IT");
console.log(Person1.sayName == Person2.sayName); //false
1.理解原型對(duì)象
任何時(shí)候創(chuàng)建新函數(shù),就會(huì)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。默認(rèn)情況下,原型對(duì)象會(huì)自動(dòng)獲得一個(gè)constructor屬性,這個(gè)屬性包含一個(gè)指向prototype屬性所在函數(shù)的指針。
雖然沒有辦法訪問到[[prototype]],但是通過isPrototypeOf( )方法可以確定對(duì)象之間是否存在這種關(guān)系。如果[[prototype]]指向調(diào)用isPrototypeOf( )方法的對(duì)象,就會(huì)返回true。
console.log(Person.prototype.isPrototypeOf(person1)); //true
而ECMAScript5新增加的一個(gè)方法Object.getPrototypeOf( )返回的對(duì)象實(shí)際就是這個(gè)對(duì)象的原型。Object.getPrototypeOf( )很方便的取得一個(gè)對(duì)象的原型。
console.log(Object.getPrototypeOf(person1) == Person.prototype); //true
幾點(diǎn)說明:
- 當(dāng)代碼讀到屬性時(shí)候,就會(huì)進(jìn)行一次搜索,先從實(shí)例開始,如果沒有搜到,則就會(huì)繼續(xù)搜索指針指向的原型。
- 實(shí)例中重新定義新的屬性和方法不會(huì)改變?cè)椭械膶傩院头椒?/em>,但是在調(diào)用該實(shí)例的時(shí)候會(huì)屏蔽原型的。
- delete刪除操作符可以完全刪除實(shí)例屬性。從而可以重新訪問原型中的屬性。
- hasOwnPrototype( )檢測(cè)一個(gè)屬性是否存在實(shí)例中,還是存在原型中。這個(gè)方法是繼承過來的,只有在實(shí)例設(shè)定屬性或者方法的時(shí)候才會(huì)返回true;
2.原型與in操作符
- 單獨(dú)使用in的時(shí)候,會(huì)在對(duì)象能夠訪問給定屬性時(shí)返回true,實(shí)例和原型均可。
console.log("name" in person1); //true
通過hasOwnPrototype( )和in結(jié)合就可以封裝一個(gè)檢測(cè)屬性是實(shí)例還是原型中的函數(shù):
function hasPrototypeProperty(object, name){
return !Object.hasOwnProperty(name) && (name in object);
}
原理:只要in操作符返回true而且.hasOwnProperty( )返回false就可以判斷屬性是存在原型中的。
- for-in循環(huán),返回的是所有能夠通過對(duì)象訪問的,可枚舉的屬性。包括在實(shí)例中的屬性以及原型中的屬性。屏蔽了原型中不可枚舉屬性的實(shí)例屬性也會(huì)返回。
Object.keys()方法可以返回所有可枚舉的實(shí)例屬性。參數(shù)如果是xxx.prototype則返回原型課枚舉的屬性。如果對(duì)于實(shí)例調(diào)用則只會(huì)返回實(shí)例的屬性和方法,不會(huì)返回原型的。
如果要得到所有屬性和方法,不管是不是可枚舉,則使用getOwnPrototypeNames()
var keys = Object.keys(Person.prototype);
console.log(keys);//["name", "age", "job", "sayName"];
var key = Object.getOwnPropertyNames(Person.prototype);
console.log(key);//["constructor", "name", "age", "job", "sayName"];
3.更簡單的原型方法
為減少不必要的代碼書寫,可以使用如下的方法:
function Person(){}
Person.prototype = {
constructor : Person,//一般沒有,是為了重新設(shè)置constructor才用設(shè)置,但是會(huì)帶來問題。
name : "hzhuang",
age : 25,
job : "IT",
sayName : function(){
console.log(this.name);
}
}
如上,Person.prototype設(shè)置成等于一個(gè)以對(duì)象字符量形式創(chuàng)建的新對(duì)象。但是值得注意的是此時(shí)constructor不再指向Person。此時(shí)的語法是完全重寫了prototype對(duì)象,所以constructor屬性就指向l構(gòu)造函數(shù)Object,不再是Person了。如下:
var friend = new Person();
console.log(friend.constructor == Person);//false
console.log(friend.constructor == Object);//true
如果想重新設(shè)定回去(讓friend.constructor == Person是true),那么就要在這個(gè)對(duì)象字面量里面添加一組condtructor的鍵值對(duì)。見3開始的例子。但是會(huì)帶來問題,會(huì)讓constructor屬性變成可枚舉的。
在兼容ECMAScript5的引擎中,可以用Object.definePeoperty來解決這個(gè)問題。
Object.defineProperty(Person.prototype, "constructor", {
enumerable : false,
value : Person
})
4.原型的動(dòng)態(tài)性
對(duì)原型對(duì)象所做的任何修改都可以立即在實(shí)例上反應(yīng)出來。即使是先創(chuàng)建實(shí)例然后修改原型。
但是如果是重寫原型,情況就大不一樣了。調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例添加一個(gè)執(zhí)行最初原型的[[prototype]]指針,而把原型修改成另一個(gè)對(duì)象就等于切斷了構(gòu)造函數(shù)與最初原型的聯(lián)系。實(shí)例中的指針只執(zhí)行原型,不指向構(gòu)造函數(shù)。
function Person(){}
var friend = new Person();
Person.prototype = {
constructor : Person,
name : "hzhuang",
age : 25,
job : "IT",
sayName : function(){
console.log(this.name);
}
}
friend.sayName();//Uncaught TypeError: friend.sayName is not a function
5.原生對(duì)象的原型
不建議修改原生對(duì)象的原型。
6.原型對(duì)象的問題
- 省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實(shí)例在默認(rèn)的情況下都將取得相同的屬性值。
- 最大的問題是有共享的本性造成的。
原型中的所有屬性都可以被很多實(shí)例共享,但是對(duì)于包含引用類型的屬性來說們就會(huì)出現(xiàn)很明顯的問題。
function Person(){}
Person.prototype = {
constructor : Person,
name : "hzhuang",
age : 25,
job : "IT",
friends : ["a", "b"],
sayName : function(){
console.log(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("c");
console.log(person2.friends);//["a", "b", "c"]
問題好明顯,我只是修改了實(shí)例1的friends,但是卻在所有的實(shí)例中反應(yīng)出來了?;诖?,單獨(dú)的原型模式一般是不常用的。
6.2.4 組合使用構(gòu)造函數(shù)模式和原型模式
- 構(gòu)造函數(shù)用于定于實(shí)例屬性;
- 原型模式用于定于方法和共享的屬性。
重寫之前有問題的例子:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["a", "b"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
console.log(this.name);
}
}
可見這種混合模式的優(yōu)點(diǎn):
- 每個(gè)實(shí)例都有自己的實(shí)例屬性的副本,同時(shí)又有共享的方法。
- 最大限度的節(jié)省了內(nèi)存
- 還支持構(gòu)造函數(shù)傳遞參數(shù)
6.2.5 動(dòng)態(tài)原型模式
把所有的信息封裝在構(gòu)造函數(shù)中,而通過在構(gòu)造函數(shù)中初始化原型,又保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn)。就是說可以通過檢測(cè)某個(gè)應(yīng)該存在的方法是否有效,來決定是否需要初始化原型。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["a", "b"];
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
console.log(this.name);
}
}
}
var friend = new Person("hzhuang", 25, "IT");
friend.sayName();
if( )部分只會(huì)在初次調(diào)用函數(shù)的時(shí)候執(zhí)行,之后原型就完成初始化。此時(shí)原型的修改都會(huì)反映在實(shí)例中。
6.2.6 寄生構(gòu)造函數(shù)模式
創(chuàng)建一個(gè)函數(shù),這個(gè)函數(shù)的作用就是封裝創(chuàng)建對(duì)象的代碼,然后再返回新創(chuàng)建的對(duì)象。和工廠模式一樣,區(qū)別只是這里是在一個(gè)函數(shù)里面并返回的這個(gè)對(duì)象。
6.2.7 穩(wěn)妥構(gòu)造函數(shù)模式
- 沒有公共屬性。
- 也不用this對(duì)象。
function Person(name, age, job){
var o = new Object();
o.sayName = function(){
alert(name); //不使用this
};
return o;
}
除了調(diào)用sayname( )方法外,沒有其他方法可以訪問到傳入到構(gòu)造函數(shù)中的原始數(shù)據(jù),保證了一種安全性。
6.3 繼承
js沒有接口繼承,只支持實(shí)現(xiàn)繼承。
6.3.1 原型鏈
原型鏈?zhǔn)菍?shí)現(xiàn)繼承的主要方法。
基本思路:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針,而每個(gè)實(shí)例都包含一個(gè)指向原型函數(shù)對(duì)象的內(nèi)部指針。如果將原型對(duì)象等于另一個(gè)類型的實(shí)例。則就會(huì)形成一個(gè)鏈。
function SuperType(){
this.property = true;
};
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
};
// 繼承SuperType( )
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
};
var instance = new SubType();
console.log(instance.getSuperValue());
幾點(diǎn)說明:
- instance.constructor指向SuperType,因?yàn)镾ubType的原型重寫了。
- instance指向SubType的原型
- SubType的原型又指向SuperType的原型
1.別忘記默認(rèn)的原型
所有的函數(shù)的默認(rèn)原型都是Object的實(shí)例,因此默認(rèn)原型都會(huì)包含一個(gè)內(nèi)部的指針,指向Object.prototype。這就是為什么函數(shù)可以繼承toString( )等默認(rèn)方法的根本原因。
2.確定原型和和實(shí)例的關(guān)系
兩種方式:
| 方式 | 描述 |
|---|---|
| instanceof | 只要用這個(gè)操作符來測(cè)試實(shí)例與原型鏈中出現(xiàn)的構(gòu)造函數(shù),就會(huì)返回true。 |
| isPrototypeOf( ) | 只要是原型鏈中出現(xiàn)過的原型,都可以說是該原生鏈所派生的實(shí)例的原型。 |
3.謹(jǐn)慎的定義方法
給原型添加方法的代碼一定要放在替換原型的語句之后。
必須在用要被繼承的構(gòu)造函數(shù)的實(shí)例替換當(dāng)前的原型之后,再定義方法。
通過原型鏈實(shí)現(xiàn)繼承的時(shí)候,不能使用對(duì)象字面量創(chuàng)建原型方法。因?yàn)檫@樣會(huì)重寫原型鏈。
// 繼承SuperType()
SubType.prototype = new SuperType();
SubType.prototype = {
getSubValue : function(){
return this.subproperty;
},
someOhterMethod : function(){
return false;
}
}
var instance = new SubType();
console.log(instance.getSuperValue()); //error
剛剛把SuperType的實(shí)例賦值給原型,接著又將原型替換成一個(gè)對(duì)象字面量而導(dǎo)致的問題。由于現(xiàn)在的原型包含的是一個(gè)Object的實(shí)例,而非SuperType的實(shí)例,因此我們?cè)O(shè)想的原型鏈已經(jīng)被切斷了。
4.原型鏈的問題
最大的問題是引用類型值的原型。還是共享問題。引用類型。
基于此,很少單獨(dú)使用原型鏈。
6.3.2 借用構(gòu)造函數(shù)
解決單獨(dú)原型鏈中引用類型帶來的問題,借用構(gòu)造函數(shù)的技術(shù)。
基本思想:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。函數(shù)是特定環(huán)境中執(zhí)行代碼的對(duì)象,可以使用apply( )和call( )方法在新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//繼承SuperType
SuperType.call(this, "hzhaung");//hzhuang是傳遞的參數(shù)。
}
var instance1 = new SubType();
instance1.colors.push("blcak");
console.log(instance1.colors);//["red", "blue", "green", "blcak"]
var instance2 = new SubType();
console.log(instance2.colors);//["red", "blue", "green"]
在新SubType對(duì)象上執(zhí)行SuperType( )函數(shù)中定義的所有對(duì)象初始化代碼。
1.傳遞參數(shù)
2.借用構(gòu)造函數(shù)的問題
方法在構(gòu)造函數(shù)中定義,所以就無法復(fù)用。
6.3.3 組合繼承
將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合在一起。
思路:使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。這樣在原型上定義的方法實(shí)現(xiàn)了函數(shù)的復(fù)用,又可以保證每個(gè)實(shí)例有它自己的屬性。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name, age){
//繼承SuperType
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new SubType("hzhuang", 25);
instance1.colors.push("blcak");
console.log(instance1.colors);//["red", "blue", "green", "blcak"]
instance1.sayName();//hzhuang
instance1.sayAge();//25
var instance2 = new SubType("hz", 26);
instance2.sayName();//hz
instance2.sayAge();//26