摘要:函數(shù)執(zhí)行一個(gè)函數(shù),被執(zhí)行有三種途徑或機(jī)制以及繼承中定義了一種對象,稱之為對象,其內(nèi)部實(shí)現(xiàn)包括以下說明該對象可以被執(zhí)行,具有屬性說明該對象可以接受操作,具有構(gòu)造器屬性指向?qū)ο蟮脑玩湣?/p>
JS函數(shù)執(zhí)行
一個(gè)JavaScript函數(shù)fn,被執(zhí)行有三種途徑:
fn()
new fn()
fn.call()或fn.apply()
new機(jī)制以及繼承JavaScript中定義了一種對象,稱之為ECMAScript對象,其內(nèi)部實(shí)現(xiàn)包括以下:
__call__: 說明該對象可以被執(zhí)行,具有function屬性
__construct__: 說明該對象可以接受new操作,具有構(gòu)造器屬性
__prototype__: 指向?qū)ο蟮脑玩湣τ诙x的函數(shù),會指向Function.prototype
注意:__prototype__是原型鏈,對所有對象都有的。prototype是原型,是函數(shù)才有的,就是一個(gè)普通的對象而已,目的是為了接受new后給生成的對象提供原型鏈的。
執(zhí)行fn就是調(diào)用__call__
執(zhí)行new fn()會進(jìn)行以下簡化過程:
新建一個(gè)對象,記作o
把o.__prototype__指向fn.prototype(如果fn.prototype不是一個(gè)Object,則指向Object.prototype)
執(zhí)行fn,并用o作為this(即內(nèi)部實(shí)現(xiàn)的fn.call(this))。如果fn返回是一個(gè)object,則返回object, 否則把o返回
fn.call(obj)或者fn.apply(obj)就是將obj作為this,執(zhí)行fn。本質(zhì)是調(diào)用__call__,只是傳入了obj作為this.
//定義一個(gè)函數(shù),正常函數(shù)會具有__call__, __construct__ //同時(shí)Parent.__proto__指向Function.prototype function Parent() { this.sayAge = function() { console.log("age is: " + this.age); } } //原型上添加一個(gè)方法 Parent.prototype.sayParent = function(){ console.log("this is Parent Method"); } //定義另一個(gè)函數(shù) function Child(firstname){ //這里就是調(diào)用Parent的__call__, 并且傳入this //而這里的this,是Child接受new時(shí)候生成的對象 //因此,這一步會給生成的Child生成的實(shí)例添加一個(gè)sayAge屬性 Parent.call(this); this.fname = firstname; this.age = 40; this.saySomething = function() { console.log(this.fname); this.sayAge(); } } //這一步就是new的調(diào)用,按原理分步來看 //1. 新建了個(gè)對象,記作o //2. o.__prototype__ = Parent.prototype, 因此o.sayParent會訪問到o.__prototype__.sayParent(原型鏈查找機(jī)制) //3. Parent.call(o), 因此o也會有個(gè)sayAge屬性(o.sayAge) //4. Child.prototype = o, 因此 Child.prototype 通過o.__prototype__ 這個(gè)原型鏈具有了o.sayParent屬性,同時(shí)通過o.sayAge 具有了sayAge屬性(也就是說Child.prototype上具有sayAge屬性,但沒有sayParent屬性,但是通過原型鏈,也可以訪問到sayParent屬性) Child.prototype = new Parent(); //這也是一步new調(diào)用 //1. 新建對象,記作s //2. s.__prototype__ = Child.prototype, 此時(shí)s會具有sayAge屬性以及sayParent這個(gè)原型鏈上的屬性 //3. Child.call(s), 執(zhí)行后, 增加了fname, age, saySomething屬性, 同時(shí)由于跑了Parent.call(s), s還具有sayAge屬性, 這個(gè)屬性是s身上的, 上面那個(gè)sayAge是Child.prototype上的, 即s.__prototype__上的。 //4. child = s var child = new Child("張") //child本身屬性就有,執(zhí)行 child.saySomething(); //child本身屬性沒有, 去原型鏈上看, child.__prototype__ = s.__prototype__ = Child.prototype = o, 這里沒找到sayParent, 繼續(xù)往上找, o.__prototype__ = Parent.prototype, 這里找到了, 執(zhí)行(第二層原型鏈找到) child.sayParent();
原理來看寫得有些繁瑣,本身其實(shí)是比較簡單的東西。
重點(diǎn)是new的過程,原型prototype和原型鏈__prototype__
也正是new的原理,導(dǎo)致了原型鏈的繼承,本質(zhì)是生成的對象的__prototype__指向了函數(shù)的原型prototype
更復(fù)雜的調(diào)用繼承之類的,都可以通過這個(gè)原理來理解。說白了,原型鏈繼承就是復(fù)用了prototype而已。
本例來看,Child中的Parent.call(this)看似沒有必要,但本質(zhì)上是有區(qū)別的。如果去掉這一句,則Child的實(shí)例本身將沒有sayAge屬性,而Child.prototype具有sayAge屬性,因此實(shí)例的__prototype__具有sayAge屬性,因此還可以執(zhí)行。
但目的是為了繼承,因此屬性是需要對象上本身持有,而非是通過原型鏈上來訪問,所以加上這一句是原理上的嚴(yán)謹(jǐn)要求。可以通過下面的例子來檢驗(yàn):
function Parent() { this.sayAge = function() { console.log("age is: " + this.age); } } Parent.prototype.sayParent = function(){ console.log("this is Parent Method"); } function Child(firstname){ Parent.call(this); this.fname = firstname; this.age = 40; this.saySomething = function() { console.log(this.fname); this.sayAge(); } } Child.prototype = new Parent(); var child = new Child("張") child.saySomething(); child.sayParent(); console.log(child.hasOwnProperty("sayAge")); // true child.sayAge(); //能調(diào)用,此時(shí)調(diào)用的是自身的屬性 delete child.sayAge; // delete只能刪除自身的屬性,不能刪除原型鏈屬性 console.log(child.hasOwnProperty("sayAge")); // false,自身沒有這個(gè)屬性了 child.sayAge(); //還能調(diào)用,此時(shí)調(diào)用的是原型鏈上的方法
如果刪掉Parent.call(this), 上面兩句child.hasOwnProperty("sayAge"), 都將返回false
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88876.html
摘要:操作符或調(diào)用函數(shù)時(shí)傳入?yún)?shù)的操作都會導(dǎo)致關(guān)聯(lián)作用域的賦值操作。此外可以使用和來設(shè)置對象及其屬性的不可變性級別。忽視這一點(diǎn)會導(dǎo)致許多問題。使用調(diào)用函數(shù)時(shí)會把新對象的屬性關(guān)聯(lián)到其他對象。 前言 《你不知道的 javascript》是一個(gè)前端學(xué)習(xí)必讀的系列,讓不求甚解的JavaScript開發(fā)者迎難而上,深入語言內(nèi)部,弄清楚JavaScript每一個(gè)零部件的用途。本書介紹了該系列的兩個(gè)主題:...
摘要:忍者級別的函數(shù)操作對于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
摘要:防止類的構(gòu)造函數(shù)以普通函數(shù)的方式調(diào)用。這個(gè)函數(shù)的主要作用是通過給類添加方法,其中將靜態(tài)方法添加到構(gòu)造函數(shù)上,將非靜態(tài)的方法添加到構(gòu)造函數(shù)的原型對象上。 Class是ES6中新加入的繼承機(jī)制,實(shí)際是Javascript關(guān)于原型繼承機(jī)制的語法糖,本質(zhì)上是對原型繼承的封裝。本文將會討論:1、ES6 class的實(shí)現(xiàn)細(xì)2、相關(guān)Object API盤點(diǎn)3、Javascript中的繼承實(shí)現(xiàn)方案盤點(diǎn)...
摘要:面向?qū)ο缶幊虒ο蟮脑椒ǚ殖蓛深愖陨淼姆椒o態(tài)方法和的實(shí)例方法。的靜態(tài)方法方法與,參數(shù)是對象,返回一個(gè)數(shù)組,數(shù)組的值是改對象自身的所有屬性名區(qū)別在于返回可枚舉的屬性,返回不可枚舉的屬性值。 面向?qū)ο缶幊?Objects對象的原生方法分成兩類:Object自身的方法(靜態(tài)方法)和Object的實(shí)例方法。注意Object是JavaScript的原生對象,所有的其他對象都是繼承自O(shè)bjec...
摘要:目錄導(dǎo)語理解對象和面向?qū)ο蟮某绦蛟O(shè)計(jì)創(chuàng)建對象的方式的繼承機(jī)制原型對象原型鏈與原型對象相關(guān)的方法小結(jié)導(dǎo)語前面的系列文章,基本把的核心知識點(diǎn)的基本語法標(biāo)準(zhǔn)庫等章節(jié)講解完本章開始進(jìn)入核心知識點(diǎn)的高級部分面向?qū)ο蟮某绦蛟O(shè)計(jì),這一部分的內(nèi)容將會對對象 目錄 導(dǎo)語 1.理解對象和面向?qū)ο蟮某绦蛟O(shè)計(jì) 2.創(chuàng)建對象的方式 3.JavaScript的繼承機(jī)制 3.1 原型對象 3.2 原型鏈 3.3 與...
閱讀 1836·2023-04-26 00:59
閱讀 3134·2021-11-15 18:10
閱讀 3080·2021-09-22 16:02
閱讀 768·2021-09-02 15:15
閱讀 3719·2019-08-30 15:56
閱讀 1921·2019-08-30 15:54
閱讀 2862·2019-08-29 16:31
閱讀 2040·2019-08-29 16:10