JS大雜燴 — 原型和原型鏈

@小小聲明一下,不會給大家堆概念,正常說話,說人話,大家能聽懂的大白話😄
@俺滴所有文章僅供參考及娛樂,歡迎探討
(噴子請自行遠離,不喜請前面左拐)

好了,廢話不多說,開幹,開始之前我們先來討論一句話…

JS中萬物皆對象?

我們經常說js中萬物皆對象,我想並非如此,至少還不夠嚴謹 😄
上代碼:

function getType(data){
  return Object.prototype.toString.call(data)
}

//Chrome Browser
var a = 10;
var b = '20';
var c = true;
var d = [];
var e = {};

console.log(getType(a)); // [object Number]
console.log(getType(b)); // [object String]
console.log(getType(c)); // [object Boolean]
console.log(getType(d)); // [object Array]
console.log(getType(e)); // [object Object]

//哎 輸出的是對象啊,傻眼了?no 繼續往下看
//大家應該都知道只要是對象,想添加屬性用 . 就可以了,好 我們來試一下

var a = 10;
var b = '20';
var c = true;
var d = [];
var e = {};

a.name = "100";
b.name = "200";
c.name = "false";
d.name = "array";
e.name = "obj";

console.dir(a); //10
console.dir(b); //20
console.dir(c); //true
console.dir(d); //{name : array} 請親自看一下
console.dir(e); //{name : obj} 請親自看一下

//哎 打印的類型是object 爲啥a,b,c變量不能加屬性呢 
//ok 爲了避免出錯,再用中括號的形式給對象加一下屬性

a["name"] = "200";
b["name"] = "300";
c["name"] = "false2";
d["name"] = "array2";
e["name"] = "obj2";

console.dir(a); //10
console.dir(b); //20
console.dir(c); //true
console.dir(d); //{name : array2} 請親自看一下
console.dir(e); //{name : obj2} 請親自看一下

//好吧  還是不行,難道是瀏覽器的原因,解釋的不一樣嗎!  
//菜鳥是無法相信大神們寫代碼這麼不嚴謹的,肯定是硬件有問題 對 換瀏覽器 ^_^

//safari Browser
var a = 10;
var b = '20';
var c = true;
var d = [];
var e = {};

console.log(getType(a)); // [object Number]
console.log(getType(b)); // [object String]
console.log(getType(c)); // [object Boolean]
console.log(getType(d)); // [object Array]
console.log(getType(e)); // [object Object]

//好吧 真想幫大神扳回一局 可惜小弟辦不到 ^*^ 
//不對 大神肯定沒錯,是我遺漏了啥 不行 繼續探坑

//我們換種寫法
var ac = new Number();
ac.name = "500";

console.log(getType(ac)); // [object Number]
console.dir(ac); //{name : 500}

//哎 竟然可以了 ..... 不對啊,爲啥數組[]和對象{}就不用手動 new 呢!
//你可能會說 就是不用new啊,解釋引擎允許這麼寫啊! 哈哈  
//好吧,那請問爲啥偏偏number string boolean 類型的要手動 new 一下才行呢,
//並且返回給我們的類型竟然是 [object Number]

// **************** 華麗的分割線 ****************

//好吧,大家可自由發揮一下了 ^_^
//不過我也爲大家強行腦補了一波兒,純屬虛構(不喜可略) 哈哈
//首先如果允許 a = 10; 可以直接a.name = "100"的話,那麼
//1:在內存中存儲一個對象,會用到棧和堆,它們之間用指針來讀取數據; 
//如果存儲的是基本類型的話,只需要用到棧 所以當a = 10 時,棧中就會生成a變量名和對應的值
//如果直接a.name,之前並沒有分配堆內存,所以就不會生效,就算自動給我們轉成對象存儲的方式,
//那初始值 a = 10該如何處置呢?直接刪掉?還是弄一個default屬性,再取一次?算了 
//還是保持初心好,不過你可以手動 new Number() 顯示的分配堆和棧給Number類型的變量,
//直接寫 a = 10 ,是不會自動分配堆內存的,必須堅持原則 ^_^;
//2:之所以是object 感覺和原型鏈有點關係, 既然可以 new Number 那就說明有原型鏈
//既然有原型鏈就可以稱爲對象,既然是對象就可以是object ,那number 即能表示爲純number類型
//也可以表示爲object number 爲啥不選擇後者呢
//好了,js中萬物皆對象,就討論到這兒吧 最後皮一下^_^
var test = new Number();
test.name = "二百五";
test = 250;
test.age = 250;

console.log(getType(test)); //[object Number]
console.dir(test); //猜一下輸出啥?

好了,接下來到我們互相傷害的環節了 —— 原型和原型鏈
先來一副神圖,以示尊敬 😂
原型鏈示意圖.jpg

原型(prototype)

直接上代碼:

//ok,我想大部分人知道原型這個詞 是通過“js中的三座大山是啥?"吧
//第一個問題:啥是原型,它在哪?
//ok,其實很常見,當我們創建一個函數的時候,其自身固有很多屬性,
//其中有一個屬性叫prototype,翻譯過來就是原型的意思,沒錯就是它,就是它
//啥叫固有屬性?就像我們生下來就有嘴、耳朵、鼻子 一樣,天生的/固有的
//ok,那我們看一下代碼

function people(){}
console.dir(people); //自身攜帶prototype屬性

//此時還有一個坑 需要提前說明:只有函數和函數對象有此屬性,普通對象是沒有的
//啥?不懂我在說啥?那就對了,我也不懂 哈哈
//不過這裏面提到2個概念 就是函數對象和普通對象;
//詳看(https://www.jianshu.com/p/dee9f8b14771)

//先不管這倆概念的正確與否,能自圓其說,解釋明白就行 
//ok,繼續看代碼:

var o1 = new Object();
console.dir(o1); //沒有prototype屬性

var f1 = new Function('test','console.log(test)');
console.dir(f1); //有prototype屬性

//好,那我們再看一個例子
function f2(){};
var o2 = new f2();
console.dir(o2); //沒有prototype屬性

//哎,不對啊,1:f2是個函數啊,怎麼new一個新的實例後,實例就是普通對象了呢?
//2:f1函數也是new一個函數實例啊,它怎麼還是函數對象呢?
//ok,我們先來解決第二個疑問,上代碼:

console.dir(Object); //ƒ Object()
console.dir(Number); //ƒ Number()
console.dir(String); //ƒ String()
console.dir(Function); //ƒ Function()

var o = new Object();
console.dir(o);
var n = new Number();
console.dir(n);
var s = new String();
console.dir(s);
var f = new Function();
console.dir(f);

var o2 = new o(),n1 = new n(), s1 = new s(), f11 = new f();
console.dir(o2); //(index):208 Uncaught TypeError: o is not a constructor
console.dir(n1); //(index):208 Uncaught TypeError: n is not a constructor
console.dir(s1); //(index):208 Uncaught TypeError: s is not a constructor
console.dir(f11); //正確無誤

//ok 我們先打印了底層的東西,輸出的結果說明底層的東西都是一個函數而已,然後我們分別創建了它們的實例,
//最後 我們在它們的實例上面再創建一個實例(重點 重點 重點) 然後打印 哦哦哦 報錯了,
//is not a constructor ,但是Function是沒有報錯的
//OK,那就是說基於底層的對象再創建二次實例,就會報錯是不允許的,而如果是基於Function的是允許的
//OK,那我是不是可以得出這樣一個假設啊;在底層層面 對象和函數的處理是有區分的,對象就是對象,函數就是函數
//並非我們認爲的js中萬物皆“對象”,至少對象之間也有不同的待遇^_^
//ok 我們回到前面,解決第一個疑問(f2是個函數啊,怎麼new一個新的實例後,示例就是普通對象了呢?)
//這個呢,就和 new 有莫大的關係了
//請看如下代碼:

var obj = new Base(); //這個new背後到底幹了什麼?
//詳見(http://web.jobbole.com/91017)


//壞事兒如下:
var obj  = {}; //這句和 new Object 是一個意思
obj.__proto__ = Base.prototype;
Base.call(obj);

//是不是感覺,有點意思(真是變態 竟然還覺得有意思)🙄️
//好了,廢話少說,原型說完了,該說原型鏈了(prototype chains)
原型鏈(prototype chains)

上代碼:

//前方警告
//以下內容請不要煩躁,一遍不懂,那就再來一遍 ^_^

//第一個問題:原型鏈是什麼?它在哪?
//ok 其實原型鏈是個抽象概念,沒聽錯,我竟然再說抽象的東西(飄了)
//它就像經濟機器,很重要但是無法具體到某一個東西身上
//如果非要說原型鏈是啥,那可能在js中只有__proto__能代表它了 ^_^
//它無處不在,猶如經濟機器在無形中運行着
//有人可能會問
//1:原型和原型鏈是什麼關係?有啥區別 爲啥叫原型鏈呢,怎麼不叫狗鏈,貓鏈?
//2: __proto__是啥玩意?
//OK 一步一步來
//我們之前已經說過原型了 and 它是函數對象獨有的,那麼還有一個普通對象呢,
//它有沒有一個獨有的東西呢? 答案是並沒有; 不過都身爲對象,它們卻有一個共有的東西
//那就是__proto__屬性,OK 那它是啥呢?
//看一段代碼吧

var obj2 = new Object();
console.dir(obj2.__proto__); //object 

//ok 我們看到輸出的竟然是個對象,沒錯,它的確是個對象,而且以後用處極大,是革命型的產物
//它的名字就叫原型的對象吧 ^_^  (prototype和__proto__好像很像哦)
//我們再來看一段代碼

function Earth(){
    this.name = "地球";
    this.rain = function(){
        console.log("要下雨嘍,回家收衣服啦");
    }
}

var city = new Earth();
console.dir(Earth.prototype); //object
console.dir(city.__proto__); //object 
console.log(Earth.prototype === city.__proto__); //true

//我們看到Earth的prototype === city的__proto__ ok 我們看到了它們的關係了,
//不是很明白?沒關係,慢慢來,不過先總結2條規則
//1:只有函數或函數對象有prototype屬性
//2:只要是對象就有__proto__屬性
//ok 我們來一句一句解釋上面的代碼

//這個不用說,創建一個普通函數而已(先不扯構造函數啥的)
function Earth(){
    this.name = "地球";
    this.rain = function(){
        console.log("要下雨嘍,回家收衣服啦");
    }
}

//new 一個Earth函數的實例
var city = new Earth();

//基於規則第一條,我們看一下Earth函數的prototype屬性
console.dir(Earth.prototype) //object
//基於規則第二條,我們看一下city實例的__proto__屬性
console.dir(city.__proto__) //object
//最後我們比對了Earth.prototype和city.__proto__的對象是否是同一塊內存,結果是true
console.log(Earth.prototype === city.__proto__);
//ok 說明這2個object是同一個對象,那我們可以得出一個簡單的關係結論
//如果一個對象是基於某函數所創建的,那麼該對象的__proto__和某函數的prototype的值是相同的,
//並且是一個對象類型的值

//啊?還是不太明白它們的關係? 對 太牽強了,解釋的太粗糙了,好吧 我們繼續
//還是舉上面的代碼吧:

//其實這個Earth函數叫構造函數,就是個普通函數,那爲啥叫構造函數呢?因爲它的函數名首字母大寫了;
//大寫了就叫構造函數了,是的,沒錯,就是這麼神奇
//也可以不大寫,不過總要和普通函數區分開吧,因爲它乾的事兒和普通函數不一樣 所以.... 你懂的
function Earth(){
    this.name = "地球";
    this.rain = function(){
        console.log("要下雨嘍,回家收衣服啦");
    }
}
console.dir(Earth);
//ok ,我們知道了,啥是構造函數了,我們再回過頭看一下函數自身的prototype屬性下面
//有哪些固有屬性,因爲我們知道它是一個對象,所以有可能存在屬性的
// 上代碼:

console.dir(Earth.prototype); //constructor , __proto__

//ok,我們看到它下面有2個屬性,其中__proto__已經說過了,不用多說,
//那我們看一下constructor這個屬性吧,翻譯過來就是 構造函數的意思 
//我去 好巧啊,那和之前說的首字母大寫的函數是一個意思嗎?
//答案是:是的 ,不僅意思一樣,而且這個constructor屬性 就表示Earth函數本身
//哇 是不是感覺好亂,什麼和什麼啊,不要煩躁, 就當它是個規定吧,像紅燈停,綠燈行一樣
//好,我們繼續往下說,知道了這個constructor屬性後,就可以解釋一些東西了
//首先函數A有個固有的prototype屬性,值是object,我們叫它原型的對象 ,簡稱原型對象X
//然後原型對象X下面又有一個constructor,然而這個constructor表示的就是函數A
//夠亂了吧,這才哪兒到哪兒,繼續ing,在函數A的基礎上,創建一個實例B,
//這個實例B帶有一個固有屬性__proto__其值和prototype的值是一樣的,也就是說__proto__
//的值也是原型對象X,ok ,重點來了 ,請問__proto__的原型對象X的constructor是誰?
//沒錯,就是函數A,也就是說函數A是允許實例B訪問其內部的變量和方法的,爲啥呢,因爲實例B有一個
//__proto__屬性指向同一個原型對象X
//你可能會問指向同一個怎麼了?指向同一個就能訪問內部變量和方法了?好吧,瞞不了你們,
//不過這屬於作用域和作用域鏈的知識了,暫切先不管,之後再補上相關內容

**************** 華麗的分割線到這兒來了 😂****************

//靠 鋪墊了這麼多,終於輪到解釋原型鏈了
//顧名思義 原型鏈,原型鏈 精髓就在 "鏈" 這個字上,鏈是啥,鏈就是鏈條白,還能是啥
//Ok 既然是鏈條,根據社會經驗來看,鏈條必定是要把一些東西串聯起來,或者是捆綁起來
//OK 這就簡單了,在js中它的作用就是串聯對象 ^_^
//上代碼:

function Vtest(){
    this.name = "123"
}

var v1 = new Vtest();
v1.name="456";

//我們這裏用到一個方法toLocaleUpperCase(把字符串轉換爲大寫)
console.log(v1.name.toLocaleUpperCase());

//艾 有沒有想過爲什麼能使用toLocaleUpperCase這麼一個方法,我們並沒有創建這個函數啊
//可能有些細心的朋友猜到了,原型的作用
//沒錯,就是它,通過__proto__這個屬性,能夠讓我們訪問其它對象中的屬性和方法
//當我們訪問對象的一個屬性或方法時,它會先在對象自身中尋找
//如果有則直接使用,如果沒有則會去原型對象中尋找
//如果找到則直接使用。如果沒有則去原型的原型中尋找
//直到找到Object對象的原型,Object對象的原型是null
//如果在Object原型中依然沒有找到,則返回undefined
//好了,原型和原型鏈我們就討論到這兒吧,最後皮一下^_^
function Vtest(){
    this.name = "123"
}

var v1 = new Vtest();
console.log(v1.name); //猜一下輸出啥?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章