javascript高級(jí)程序第三版學(xué)習(xí)筆記(持續(xù)更新)

題記

從[我的博客]中搬運(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類型)
  • 第六章 面向?qū)ο蟪绦蛟O(shè)計(jì)

    • 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

6.3.4 原型式繼承

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容