摘要:在這篇文章中,他介紹了一種實現繼承的方法,這種方法并沒有使用嚴格意義上的構造函數。的左值一般是一個對象,右值一般是一個構造函數,用來判斷左值是否是右值的實例。
__proto__ 探究
__proto__隱式原型與prototype顯式原型是個容易令人混淆的概念,簡而言之prototype是構造函數用來被自己的實例繼承的原型,而_proto_是實例用來繼承父類原型的載體。
1. 是什么 顯式原型 explicit prototype property每一個函數在創建之后都會擁有一個名為prototype的屬性,這個屬性指向函數的原型對象,定義了該構造函數創建的所有實例對象共享的屬性。
Note:通過Function.prototype.bind方法構造出來的函數是個例外,它沒有prototype屬性
NOTE Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties. ----- ECMAScript Language Specification隱式原型 implicit prototype link
JavaScript中任意對象都有一個內置屬性[[prototype]],在ES5之前沒有標準的方法訪問這個內置屬性,但是大多數瀏覽器都支持通過__proto__來訪問。ES5中有了對于這個內置屬性標準的Get方法,Object.getPrototypeOf() 。
__proto__是瀏覽器自實現的[[prototype]]
隱式原型指向創建這個對象的函數(構造函數constructor)的顯式原型prototype
function Person(name) {this.name = name} var person1 = new Person console.log(person1.__proto__ === Person.prototype) // true console.log(Object.getPrototypeOf(person1) === person1.__proto__) // true console.log(person1.prototype === person1.__proto__) // false console.log(Object.getPrototypeOf(Person) === Person.__proto__) // true console.log(Person.prototype===Person.__proto__) // false console.log(person1.constructor === Person) // true2. 作用是什么
顯示原型的作用:用來實現基于原型的繼承與屬性的共享。
ECMAScript does not use classes such as those in C++, Smalltalk, or Java. Instead objects may be created in various ways including via a literal notation or via constructors which create objects and then execute code that initialises all or part of them by assigning initial values to their properties. Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties.Objects are created by using constructors in new expressions; for example, new Date(2009,11) creates a new Date object. ---- ECMAScript Language Specification
隱式原型的作用:構成原型鏈,同樣用于實現基于原型的繼承。舉個例子,當我們訪問obj這個對象中的x屬性時,如果在obj中找不到,那么就會沿著__proto__依次查找。
Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s “prototype” ---- ECMAScript Language Specification3. __proto__的指向
__proto__的指向到底如何判斷呢?根據ECMA定義 "to the value of its constructor"s "prototype" " ----指向創建這個對象的函數(構造函數)的顯式原型。所以關鍵的點在于找到創建這個對象的構造函數,接下來就來看一下JS中對象被創建的方式,一眼看過去似乎有三種方式:對象字面量的方式 、new 的方式 、ES5中的Object.create() 但是我認為本質上只有一種方式,也就是通過new來創建。為什么這么說呢,首先字面量的方式是一種為了開發人員更方便創建對象的一個語法糖,本質就是 var o = new Object(); o.xx = xx;o.yy=yy; 再來看看Object.create(),這是ES5中新增的方法,在這之前這被稱為原型式繼承
道格拉斯在2006年寫了一篇文章,題為 Prototypal Inheritance In JavaScript。在這篇文章中,他介紹了一種實現繼承的方法,這種方法并沒有使用嚴格意義上的構造函數。他的想法是借助原型可以基于已有的對象創建新對象,同時還不比因此創建自定義類型,為了達到這個目的,他給出了如下函數:
function object(o){ function F(){} F.prototype = o; return new F() }
----- 《JavaScript高級程序設計》P169
所以從實現代碼 return new F() 中我們可以看到,這依然是通過new來創建的。不同之處在于由 Object.create() 創建出來的對象沒有構造函數,看到這里你是不是要問,沒有構造函數我怎么知道它的__proto__指向哪里呢,其實這里說它沒有構造函數是指在 Object.create() 函數外部我們不能訪問到它的構造函數,然而在函數內部實現中是有的,它短暫地存在了那么一會兒。假設我們現在就在函數內部,可以看到對象的構造函數是F, 現在
// 以下是用于驗證的偽代碼 var f = new F(); // 于是有 f.__proto__ === F.prototype // true // 又因為 F.prototype === o; // true // 所以 f.__proto__ === o;
因此由Object.create(o)創建出來的對象它的隱式原型指向o。好了,對象的創建方式分析完了,現在你應該能夠判斷一個對象的__proto__指向誰了。
好吧,還是舉一些一眼看過去比較疑惑的例子來鞏固一下。
構造函數的顯示原型的隱式原型:
內建對象(built-in object):比如Array(),Array.prototype.__proto__指向什么?Array.prototype也是一個對象,對象就是由 Object() 這個構造函數創建的,因此Array.prototype.__proto__ === Object.prototype //true,或者也可以這么理解,所有的內建對象都是由Object()創建而來。
自定義對象
默認情況下:
function Foo(){} var foo = new Foo() Foo.prototype.__proto__ === Object.prototype // true 理由同上
其他情況:
function Bar(){} //這時我們想讓Foo繼承Bar Foo.prototype = new Bar() Foo.prototype.__proto__ === Bar.prototype //true
//我們不想讓Foo繼承誰,但是我們要自己重新定義Foo.prototype Foo.prototype = { a:10, b:-10 } //這種方式就是用了對象字面量的方式來創建一個對象,根據前文所述 Foo.prototype.__proto__ === Object.prototype
Note: 以上兩種情況都等于完全重寫了Foo.prototype,所以Foo.prototype.constructor也跟著改變了,于是乎constructor這個屬性和原來的構造函數Foo()也就切斷了聯系。
構造函數的隱式原型
既然是構造函數那么它就是Function()的實例,因此也就指向Function.prototype,比如 Object.__proto__ === Function.prototype
4. instanceofinstanceof 操作符的內部實現機制和隱式原型、顯式原型有直接的關系。instanceof的左值一般是一個對象,右值一般是一個構造函數,用來判斷左值是否是右值的實例。它的內部實現原理是這樣的:
// 設 L instanceof R // 通過判斷 L.__proto__.__proto__ ..... === R.prototype ? // 最終返回true or false
也就是沿著L的__proto__一直尋找到原型鏈末端,直到等于R.prototype為止。知道了這個也就知道為什么以下這些奇怪的表達式為什么會得到相應的值了,所有構造函數都是Fucntion的實例,所有對象都是Object的實例
Function instanceof Object // true Object instanceof Function // true Function instanceof Function //true Object instanceof Object // true Number instanceof Number //false5. Js對象體系結構
每一個對象都有__proto__,誰創建的對象(繼承誰),__proto__就指向誰的prototype
所有的函數都是由Function()創建的,所以所有的函數的__proto__就指向Function的prototype,包括它自己
函數有prototype,普通實例對象沒有,且函數的prototype都有一個自有屬性constructor指向自己
所有的prototype也是對象,是由Object()創建(繼承Object)而來的,所以所有的prototype的__proto__都指向Object
6. 謹慎操作__proto__警告: 由于現代 JavaScript 引擎優化屬性訪問所帶來的特性的關系,更改對象的 [[Prototype]]在各個瀏覽器和 JavaScript 引擎上都是一個很慢的操作。其在更改繼承的性能上的影響是微妙而又廣泛的,這不僅僅限于 obj.__proto__ = ... 語句上的時間花費,而且可能會延伸到任何代碼,那些可以訪問任何[[Prototype]]已被更改的對象的代碼。如果你關心性能,你應該避免設置一個對象的 [[Prototype]]。相反,你應該使用 Object.create()來創建帶有你想要的[[Prototype]]的新對象。
網上的帖子大多深淺不一,甚至有些前后矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~
參考:
1、js中__proto__和prototype的區別和關系?
2、G小調的悲傷的博客
3、JavaScript instanceof 運算符深入剖析
PS:歡迎大家關注我的公眾號【前端下午茶】,一起加油吧~
另外可以加入「前端下午茶交流群」微信群,長按識別下面二維碼即可加我好友,備注加群,我拉你入群~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92021.html
摘要:另外常說的的構造函數,就是指這個。按照理解,可能會這樣的疑問指向構造函數再次提醒,是一個對象的,所以這個對象的構造函數是它自己自己創建自己么額。。。 本文由用途意義,進行腦測解析,從需求角度走一遍原型鏈的發展。 用對象模擬類的繼承 js中沒有類(沒有類,沒有類,重要的事情說3遍)只有對象,怎么才能做到繼承的效果? var a={x:1} var b={}; b.__proto__=a...
摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。 寫在前面 如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:練成「九陽神功」后,會易筋洗髓;生出...
摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。 寫在前面 如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:練成「九陽神功」后,會易筋洗髓;生出...
摘要:寫在前面如果說是一本武學典籍,那么原型鏈就是九陽神功。那么,如何修煉好中的九陽神功呢真正的功法大成的技術是從底層上去理解,那種工程師和碼農的區別就在于對底層的理解,當你寫完一行代碼,或者你遇見一個解決的速度取決于你對底層的理解。 寫在前面 如果說JavaScript是一本武學典籍,那么原型鏈就是九陽神功。在金庸的武俠小說里面,對九陽神功是這樣描述的:練成「九陽神功」后,會易筋洗髓;生出...
閱讀 3203·2023-04-26 03:06
閱讀 3692·2021-11-22 09:34
閱讀 1140·2021-10-08 10:05
閱讀 3035·2021-09-22 15:53
閱讀 3539·2021-09-14 18:05
閱讀 1402·2021-08-05 09:56
閱讀 1895·2019-08-30 15:56
閱讀 2130·2019-08-29 11:02