摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。
寫在前面
如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:
"練成「九陽神功」后,會易筋洗髓;生出氤氳紫氣;內力自生速度奇快,無窮無盡,普通拳腳也能使出絕大攻擊力;防御力無可匹敵,自動護體,反彈外力攻擊,成就金剛不壞之軀;習者速度將受到極大加成;更是療傷圣典,百病不生,諸毒不侵。至陽熱氣全力施展可將人焚為焦炭,專門克破所有寒性和陰毒內力。"可見其功法強大。
那么,如何修煉好js中的九陽神功呢?真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個bug,解決的速度取決于你對底層的理解。什么是底層?我目前個人的理解是“在你寫每一行代碼的時候,它將如何在相應的虛擬機或者V8引擎中是如何運行的,更厲害的程序員甚至知道每條數據的操作是在堆里面還是在棧里面,都做到了然于胸,這是JavaScript的內功最高境界(反正我現在是做不到,我不知道你們能不能,哈哈哈)”。
**理解原型鏈之前首先要了解js的基本類型和引用類型:
1、基本類型
基本類型有Undefined、Null、Boolean、Number 和String。這些類型在內存中分別占有固定大小的空間,他們的值保存在棧空間,
我們通過按值來訪問的。
基本類型:簡單的數據段,存放在棧內存中,占據固定大小的空間。
2、引用類型
引用類型,值大小不固定,棧內存中存放地址指向堆內存中的對象。是按引用訪問的。
存放在堆內存中的對象,變量實際保存的是一個指針,這個指針指向另一個位置。每個空間大小不一樣,要根據情況開進行特定的分配。
當我們需要訪問引用類型(如對象,數組,函數等)的值時,首先從棧中獲得該對象的地址指針,然后再從堆內存中取得所需的數據。**
js的原型鏈說簡單也簡單,說難也難。
首先說明:函數(Function)才有prototype屬性,對象(除了Object)擁有_proto_.
原型鏈的頂層就是Object.prototype,而這個對象的是沒有原型對象的。
可以在Chrome輸入:
Object.__proto__
輸出的是:
? () { [native code] }
可以看到這個沒有.prototype屬性。
二、prototype和_proto_的區別我們知道原型是一個對象,其他對象可以通過它實現屬性繼承。
var a = {}; console.log(a.prototype); //undefined console.log(a.__proto__); //Object {} var b = function(){} console.log(b.prototype); //b {} console.log(b.__proto__); //function() {}
/*1、字面量方式*/ var a = {}; console.log(a.__proto__); //Object {} console.log(a.__proto__ === a.constructor.prototype); //true /*2、構造器方式*/ var A = function(){}; var a = new A(); console.log(a.__proto__); //A {} console.log(a.__proto__ === a.constructor.prototype); //true /*3、Object.create()方式*/ var a1 = {a:1} var a2 = Object.create(a1); console.log(a2.__proto__); //Object {a: 1} console.log(a.__proto__ === a.constructor.prototype); //false(此處即為圖1中的例外情況)
var A = function(){}; var a = new A(); console.log(a.__proto__); //A {}(即構造器function A 的原型對象) console.log(a.__proto__.__proto__); //Object {}(即構造器function Object 的原型對象) console.log(a.__proto__.__proto__.__proto__); //nullinstanceof究竟是運算什么的?
我曾經簡單理解instanceof只是檢測一個對象是否是另個對象new出來的實例(例如var a = new Object(),a instanceof Object返回true),但實際instanceof的運算規則上比這個更復雜。
//假設instanceof運算符左邊是L,右邊是R L instanceof R //instanceof運算時,通過判斷L的原型鏈上是否存在R.prototype L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否則返回false
注意:instanceof運算時會遞歸查找L的原型鏈,即L.__proto__.__proto__.__proto__.__proto__...直到找到了或者找到頂層為止。
所以一句話理解instanceof的運算規則為:
instanceof檢測左側的__proto__原型鏈上,是否存在右側的prototype原型。
圖解構造器Function和Object的關系我們再配合代碼來看一下就明白了:
//①構造器Function的構造器是它自身 Function.constructor=== Function;//true //②構造器Object的構造器是Function(由此可知所有構造器的constructor都指向Function) Object.constructor === Function;//true //③構造器Function的__proto__是一個特殊的匿名函數function() {} console.log(Function.__proto__);//function() {} //④這個特殊的匿名函數的__proto__指向Object的prototype原型。 Function.__proto__.__proto__ === Object.prototype//true //⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函數 Object.__proto__ === Function.prototype;//true Function.prototype === Function.__proto__;//true當構造器Object和Function遇到instanceof
Function.__proto__.__proto__ === Object.prototype;//true Object.__proto__ === Function.prototype;//true
如果看完以上,你還覺得上面的關系看暈了的話,只需要記住下面兩個最重要的關系,其他關系就可以推導出來了:
1、所有的構造器的constructor都指向Function
2、Function的prototype指向一個特殊匿名函數,而這個特殊匿名函數的__proto__指向Object.prototype
function、Function、Object和{}我們知道,在Js中一切皆為對象(Object),但是Js中并沒有類(class);Js是基于原型(prototype-based)來實現的面向對象(OOP)的編程范式的,但并不是所有的對象都擁有prototype這一屬性:
var a = {}; console.log(a.prototype); //=> undefined var b = function(){}; console.log(b.prototype); //=> {} var c = "Hello"; console.log(c.prototype); //=> undefined
prototype是每個function定義時自帶的屬性,但是Js中function本身也是對象,我們先來看一下下面幾個概念的差別:
function是Js的一個關鍵詞,用于定義函數類型的變量,有兩種語法形式:
function f1(){ console.log("This is function f1!"); } typeof(f1); //=> "function" var f2 = function(){ console.log("This is function f2!"); } typeof(f2); //=> "function"
如果用更加面向對象的方法來定義函數,可以用Function:
var f3 = new Function("console.log("This is function f3!");"); f3(); //=> "This is function f3!" typeof(f3); //=> "function" typeof(Function); //=> "function"
實際上Function就是一個用于構造函數類型變量的類,或者說是函數類型實例的構造函數(constructor);與之相似有的Object或String、Number等,都是Js內置類型實例的構造函數。比較特殊的是Object,它用于生成對象類型,其簡寫形式為{}:
var o1 = new Object(); typeof(o1); //=> "object" var o2 = {}; typeof(o2); //=> "object" typeof(Object); //=> "function"prototype VS_proto_
prototype和length是每一個函數類型自帶的兩個屬性,而其它非函數類型并沒有(開頭的例子已經說明),這一點之所以比較容易被忽略或誤解,是因為所有類型的構造函數本身也是函數,所以它們自帶了prototype屬性:
除了prototype之外,Js中的所有對象(undefined、null等特殊情況除外)都有一個內置的[[Prototype]]屬性,指向它“父類”的prototype,這個內置屬性在ECMA標準中并沒有給出明確的獲取方式,但是許多Js的實現(如Node、大部分瀏覽器等)都提供了一個__proto__屬性來指代這一[[Prototype]],我們通過下面的例子來說明實例中的__proto__是如何指向構造函數的prototype的:
var Person = function(){}; Person.prototype.type = "Person"; Person.prototype.maxAge = 100; var p = new Person(); console.log(p.maxAge); p.name = "rainy"; Person.prototype.constructor === Person; //=> true p.__proto__ === Person.prototype; //=> true console.log(p.prototype); //=> undefined
圖示解釋上面的代碼:
Person是一個函數類型的變量,因此自帶了prototype屬性,prototype屬性中的constructor又指向Person本身;通過new關鍵字生成的Person類的實例p1,通過__proto__屬性指向了Person的原型。這里的__proto__只是為了說明實例p1在內部實現的時候與父類之間存在的關聯(指向父類的原型),在實際操作過程中實例可以直接通過.獲取父類原型中的屬性,從而實現了繼承的功能。
核心圖解var Obj = function(){}; var o = new Obj(); o.__proto__ === Obj.prototype; //=> true o.__proto__.constructor === Obj; //=> true Obj.__proto__ === Function.prototype; //=> true Obj.__proto__.constructor === Function; //=> true Function.__proto__ === Function.prototype; //=> true Object.__proto__ === Object.prototype; //=> false Object.__proto__ === Function.prototype; //=> true Function.__proto__.constructor === Function;//=> true Function.__proto__.__proto__; //=> {} Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true o.__proto__.__proto__.__proto__ === null; //=> true
從上面的例子和圖解可以看出,prototype對象也有__proto__屬性,向上追溯一直到null
new關鍵詞的作用就是完成上圖所示實例與父類原型之間關系的串接,并創建一個新的對象;instanceof關鍵詞的作用也可以從上圖中看出,實際上就是判斷__proto__(以及__proto__.__proto__...)所指向是否父類的原型:
var Obj = function(){}; var o = new Obj(); o instanceof Obj; //=> true o instanceof Object; //=> true o instanceof Function; //=> false o.__proto__ === Obj.prototype; //=> true o.__proto__.__proto__ === Object.prototype; //=> true o.__proto__.__proto__ === Function; //=> false
原型鏈的結構
1.原型鏈繼承就是利用就是修改原型鏈結構( 增加、刪除、修改節點中的成員 ), 從而讓實例對象可以使用整個原型鏈中的所有成員( 屬性和方法 )
2.使用原型鏈繼承必須滿足屬性搜索原則
屬性搜索原則
1.構造函數 對象原型鏈結構圖
function Person (){}; var p = new Person();
2.{} 對象原型鏈結構圖
3.數組的原型鏈結構圖
4.Object.prototype對應的構造函數
總結:
從本質上理解:對象和函數都是保存在堆當中的引用類型,后面一系列的操作都是為了使用或者訪問其屬性,那么無論是prototype還是_proto_都是函數或者Object自帶的指針,允許外界的其他一些函數或者Object去使用自己的一些屬性。
更多的文章請關注公眾號:碼客小棧,每天不定時的更新web好文
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54039.html
摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。 寫在前面 如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:練成「九陽神功」后,會易筋洗髓;生出...
摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。 寫在前面 如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:練成「九陽神功」后,會易筋洗髓;生出...
摘要:希望能夠幫助到大家,減少在起步階段的油耗,集中精神突破技術。在平時寫代碼的時候你不一定會用到,但是他卻是你解決問題的思想源泉如果說算法是一個程序員的九陽神功,那么設計模式就是你的乾坤大挪移。 showImg(https://segmentfault.com/img/remote/1460000019249986); 不知怎么的,最近不少關注我的讀者都開始私信我怎么學好python?零基...
閱讀 3110·2021-11-24 09:39
閱讀 968·2021-09-07 10:20
閱讀 2389·2021-08-23 09:45
閱讀 2254·2021-08-05 10:00
閱讀 566·2019-08-29 16:36
閱讀 833·2019-08-29 11:12
閱讀 2813·2019-08-26 11:34
閱讀 1839·2019-08-26 10:56