摘要:構(gòu)造函數(shù)構(gòu)造操作符調(diào)用的函數(shù)就是構(gòu)造函數(shù)。其和其構(gòu)造函數(shù)的指向相同。而構(gòu)造函數(shù)屬性指向的對(duì)象帶有屬性,指向函數(shù)自身。,回歸構(gòu)造函數(shù)繼承,仔細(xì)看看誕生的嘻嘻和哈哈兩位同學(xué)可以看到兩個(gè)實(shí)例都擁有了和兩個(gè)屬性,因?yàn)榉椒ǖ倪\(yùn)行類似于執(zhí)行了和。
最近在看《JavaScript設(shè)計(jì)模式》,然后開篇復(fù)習(xí)了JavaScript中的幾種繼承方式,自己似乎也沒有怎么仔細(xì)探究過,目前自己沒怎么碰到過應(yīng)用的場(chǎng)景(噗),所以借這次機(jī)會(huì)好好來屢屢思路。
方式1 類式繼承例子
function Person() { this.telephone = ["000-0000-0000"]; } function Student(className) { this.className = className; } Student.prototype = new Person(); var Haha = new Student(1); var Xixi = new Student(2);
創(chuàng)建好父類和子類。聯(lián)系他們的方式是把學(xué)生的prototype指向一個(gè)人的實(shí)例。
問:prototype是什么?
幾乎任何對(duì)象有一個(gè)[[prototype]]屬性,在標(biāo)準(zhǔn)中,[[prototype]]一個(gè)隱藏屬性,指向的是這個(gè)對(duì)象的原型。而它的指向是由構(gòu)造該對(duì)象的方法決定的:
1.對(duì)象字面量構(gòu)造:其[[prototype]]指向Object.prototype。
var person = {};
2.構(gòu)造函數(shù)構(gòu)造:new操作符調(diào)用的函數(shù)就是構(gòu)造函數(shù)。其[[prototype]]和其構(gòu)造函數(shù)的prototype指向相同。而構(gòu)造函數(shù)prototype屬性指向的對(duì)象帶有constructor屬性,指向函數(shù)自身。
function Person(){} var person = new Person();
此圖為Person的prototype內(nèi)容,可以看到constructor屬性實(shí)際指向的就是Person()函數(shù)。(小綠色框框內(nèi)和外面綠色框框其實(shí)是同一個(gè)內(nèi)容)。
3.Object.create構(gòu)造的。
var person = {}; var Haha = Object.create(person);
這里對(duì)象Haha的[[prototype]]指向?qū)ο髉erson。也可以寫null,此時(shí)對(duì)象Haha就沒有原型。
首先要分清楚類和實(shí)例,在控制臺(tái)顯示中,只有類才會(huì)有prototype屬性,而實(shí)例是擁有一個(gè)名為_proto_的屬性,它會(huì)指向構(gòu)造它函數(shù)的原型,兩者本質(zhì)都是一個(gè)指針。
function Person() { this.telephone = ["000-0000-0000"]; } var Hehe = new Person(); console.log(Person.prototype); console.log(Hehe);
以上代碼運(yùn)行結(jié)果:
可以瞧見,這里Hehe的_proto_是指向了Person.prototype。
問:new關(guān)鍵字的作用是什么?
new關(guān)鍵字運(yùn)作的過程如下,引用自《JavaScript》高級(jí)程序設(shè)計(jì):
1、創(chuàng)建一個(gè)空對(duì)象,并且 this 變量引用該對(duì)象,同時(shí)還繼承了該函數(shù)的原型。 2、屬性和方法被加入到 this 引用的對(duì)象中。
3、新創(chuàng)建的對(duì)象由 this 所引用,并且最后隱式的返回 this。
簡(jiǎn)單來說,它創(chuàng)建了一個(gè)空對(duì)象,指定了原型,把屬性方法進(jìn)行拷貝,并把this指向進(jìn)行了改變。假如我們把上面的代碼改成:
function Person() { this.telephone = ["000-0000-0000"]; } var Hehe = Person(); console.log(Hehe.telephone);
去掉new關(guān)鍵詞賦予Person(),會(huì)報(bào)錯(cuò),而輸出window.telphone得到的就是["000-0000-0000"]。因?yàn)楹瘮?shù)的返回值(沒有返回值所以是undefined)賦予給了Hehe,嘗試去讀取undefined的屬性,報(bào)錯(cuò)了。而此時(shí)函數(shù)運(yùn)行中的this是全局變量window。
So,回歸類式繼承,仔細(xì)看看誕生的嘻嘻和哈哈兩位同學(xué)
會(huì)發(fā)現(xiàn),各自都有自己的班級(jí)名屬性,但是原型指向的是同一個(gè)Person實(shí)例,所以如果嘻嘻有兩個(gè)號(hào)碼,或者他要更改自己的號(hào)碼,那哈哈的電話號(hào)碼也會(huì)發(fā)生變化,他們只能共享這個(gè)電話號(hào)碼。
方式2 構(gòu)造函數(shù)繼承例子
function Person(name) { this.name = name; } Person.prototype.showName = function() { console.log(this.name); } function Student(name, className) { this.className = className; Person.call(this, name); } var Haha = new Student("Haha", 1); var Xixi = new Student("Xixi", 2);
問:call函數(shù)的運(yùn)作過程?
call函數(shù)和apply函數(shù)的作用相同,不同之處就是apply函數(shù)只能傳入2個(gè)參數(shù),而call函數(shù)可以有多個(gè)。F.call(thisArg,[arg1……]) 函數(shù)的運(yùn)作過程如下(來源網(wǎng)絡(luò)):
1.先判斷F是否為一個(gè)函數(shù),如果不是一個(gè)函數(shù),那么將拋出TypeError異常。
2.創(chuàng)建一個(gè)內(nèi)部類型空列表list
3.然后如果參數(shù)除去thisArg外還有其他參數(shù)的話,就將這些值添加到list中
4.thisArg和list作為F內(nèi)部屬性[[Call]]的參數(shù)傳入調(diào)用進(jìn)行函數(shù)的執(zhí)行操作
簡(jiǎn)而言之就是它把一個(gè)函數(shù)的對(duì)象上下文改成了由 thisArg指定的新對(duì)象。
So,回歸構(gòu)造函數(shù)繼承,仔細(xì)看看誕生的嘻嘻和哈哈兩位同學(xué)
可以看到兩個(gè)實(shí)例都擁有了className和name兩個(gè)屬性,因?yàn)閏all方法的運(yùn)行類似于執(zhí)行了Haha.name="Haha"和Xixi.name="Xixi"。
但是因?yàn)闆]有與父類的原型相聯(lián)系,所以父類原型中的方法,不能得到繼承。運(yùn)行Haha.showName()會(huì)得到報(bào)錯(cuò)。
例子
function Person(name) { this.name = name; } Person.prototype.showName = function() { console.log(this.name); } function Student(name, className) { this.className = className; Person.call(this, name); } Student.prototype = new Person(); Student.prototype.showClassName = function() { console.log(this.className); } var Haha = new Student("Haha", 1); var Xixi = new Student("Xixi", 2);
組合繼承綜合了類式繼承和構(gòu)造函數(shù)繼承,在把父類的屬性繼承后,把子類的原型指向了父類實(shí)例,這樣就可以繼承父類原型的方法了。
但是這里相當(dāng)于使用了兩次父類函數(shù),并且子類不是父類的實(shí)例,子類的原型是父類的實(shí)例,所以還會(huì)有更好的方法。
function inheritObject(o) { function F() {} F.prototype = o; return new F(); } var person = { name: "unknown", telephone: ["000-0000-0000"] } var Xixi = inheritObject(person); Xixi.name = "Xixi"; Xixi.telephone.push("111-1111-1111"); var Haha = inheritObject(person); Haha.name = "Haha";
仔細(xì)看看誕生的嘻嘻和哈哈兩位同學(xué)
這里.name給Xixi實(shí)例添加了一個(gè)自己的name屬性,而push操作是直接影響原型中引用變量,所以改進(jìn)之后又有了下面這種方式。
在這里我產(chǎn)生了一個(gè)疑問,為什么name屬性是自己添加新的,而telephone是采用原來的。于是添加了一個(gè)age屬性,執(zhí)行Xixi.age++操作。
這里可以看到實(shí)例重新添加了一個(gè)age屬性,所以我們可以說只要是改變?cè)蛯傩缘闹担蜁?huì)把新的屬性加在實(shí)例上,引用不改變是因?yàn)橐玫牡刂愤€沒有改變。
方式5 寄生式繼承寄生式繼承是在原型繼承的基礎(chǔ)之上,我們需要再添加一下代碼:
function createPerson(obj) { var o = inheritObject(obj); o.getName = function(){ console.log(name); } return o; }
這樣就給得到的對(duì)象添加了公共方法。
方式6 寄生組合式繼承寄生組合式繼承是為了彌補(bǔ)組合式繼承的缺點(diǎn),是在寄生式繼承+構(gòu)造函數(shù)繼承組合而成的:
function inheritObject(o) { function F() {} F.prototype = o; return new F(); } function inheritPrototype(subClass, superClass) { //復(fù)制一份父類原型 var p = inheritObject(superClass.prototype); //修正重寫子類原型導(dǎo)致constructor屬性被修改 p.constructor = subClass; //設(shè)置子類原型 subClass.prototype = p; } function Person(name) { this.name = name; } Person.prototype.showName = function() { console.log(this.name); } function Student(name, className) { this.className = className; Person.call(this, name); } inheritPrototype(Student, Person); Student.prototype.showClassName = function() { console.log(this.className); } var Xixi = new Student("Xixi",2); var Haha = new Student("Haha",1);
以下為嘻嘻和哈哈的內(nèi)容:
可以對(duì)比一下組合式繼承的結(jié)果:
不同的地方在于把子類原型的構(gòu)造函數(shù)改成了實(shí)例對(duì)應(yīng)的構(gòu)造函數(shù),在組合繼承中子類原型直屬并沒有constructor屬性。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/93124.html
摘要:為什么會(huì)產(chǎn)生閉包究其根本,是因?yàn)榇淼暮瘮?shù)包含的作用域。而在作用域鏈中,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象處于第三位直到作為作用域鏈終點(diǎn)的全局執(zhí)行環(huán)境。 前言 此文的內(nèi)容主要是來自看書的總結(jié)+小小的實(shí)踐哦~會(huì)不斷更新總結(jié)。 什么是閉包 書上是這樣定義閉包的: 有權(quán)訪問另一個(gè)函數(shù)作用域中變量的函數(shù)。 舉一個(gè)例子: function test(){ va...
摘要:?jiǎn)栴}起源以前一直就聽說圣杯布局,但是沒有怎么去用過,然后這次偶然接觸到了,就學(xué)習(xí)了一下。繼續(xù)試驗(yàn)我們可以嘗試改變的值,去看看位置的變化。為了方便我們計(jì)算,另外寫了一個(gè)類似的布局,內(nèi)容區(qū)的寬度是,三個(gè)的寬度也都是。 問題の起源 以前一直就聽說圣杯布局,但是沒有怎么去用過,然后這次偶然接觸到了,就學(xué)習(xí)了一下。這是一個(gè)我從別人寫的文章中復(fù)制過來的,關(guān)于圣杯布局的比較簡(jiǎn)單的說明 通過縮放頁面就...
摘要:官方默認(rèn)項(xiàng)目是存放了一個(gè)為的打開文件夾有一個(gè),還有一個(gè)名為組件的文件夾,里面放了一個(gè)文件。部分我們會(huì)發(fā)現(xiàn)這幾排字就是顯示在頁面的幾排文字部分這其中的這個(gè)文件引入了,還有上述的。結(jié)合查詢其他說法,就是說它會(huì)把是的元素以形式替換。 前言 我很早就想來學(xué)習(xí)學(xué)習(xí)vue.js啦,終于有了那么一些空閑的時(shí)間可以拿來學(xué)習(xí),于是從前天開始我就每天抽一個(gè)多小時(shí)來體驗(yàn)vue.js。當(dāng)然啦,因?yàn)槭切“兹腴T,...
閱讀 690·2021-11-25 09:43
閱讀 2959·2021-11-24 10:20
閱讀 1014·2021-10-27 14:18
閱讀 1086·2021-09-08 09:36
閱讀 3392·2021-07-29 14:49
閱讀 1791·2019-08-30 14:07
閱讀 2945·2019-08-29 16:52
閱讀 3055·2019-08-29 13:12