红宝书读书笔记 第八章
對(duì)象、類與面向?qū)ο缶幊?/h1>
屬性的類型
內(nèi)部屬性用兩個(gè)中括號(hào)如[ [ Enumerable ] ]
開發(fā)者不能直接訪問
數(shù)據(jù)屬性
數(shù)據(jù)屬性:數(shù)據(jù)屬性包含一個(gè)保存數(shù)據(jù)值的位置。值會(huì)從這個(gè)位置讀取,也會(huì)寫入到這個(gè)位置。數(shù)據(jù)屬性有四個(gè)特性描述他們的行為。
[[Configurable]]:表示睡醒是否可以通過delete刪除并重新定義,是否可以修改,是否可以把它修改為訪問器屬性 [[Enumberable]]: 屬性是否可以通過for-in循環(huán) [[Writable]]: 屬性的值是否可以修改 [[Value]]:包含屬性實(shí)際的值let person = {name: 'nnn' }//name是屬性,value等是name的特性,因?yàn)槭菙?shù)據(jù)//屬性所以有value //默認(rèn)前三個(gè)為true,[[value]]設(shè)為制定的值 //name就是數(shù)據(jù)屬性修改屬性的默認(rèn)屬性,需要 Object.defineProperty()let person = {} Object.defineProperty(person,'name',{writable: false,value: 'zhaosi' }); console.log(person.name) person.name = 'liunenng' console.log(person.name) 輸出 zhaosi zhaosi訪問器屬性
不包含數(shù)據(jù)值。包含一個(gè)獲取(getter)和設(shè)置(setter)函數(shù),但不是必需的
讀取訪問器屬性,調(diào)用獲取函數(shù),寫入訪問器屬性,調(diào)用設(shè)置函數(shù)。
定義多個(gè)屬性
Object.defineProperties()
讀取屬性的特性
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptors()
合并對(duì)象
合并(merge)對(duì)象,就是把源對(duì)象所有的本地屬性一起復(fù)制到目標(biāo)對(duì)象上。有時(shí)候這種操作也被稱為混入(mixin),因?yàn)槟繕?biāo)對(duì)象通過混入源對(duì)象得到了增強(qiáng)
Object.assign()
對(duì)象標(biāo)識(shí)及相等判定
Object.is()
console.log(Object.is(true,1)) console.log(Object.is({},{})) console.log(Object.is(NaN,NaN))增強(qiáng)的對(duì)象語法
//屬性值簡(jiǎn)寫 let name = 'matt' let person = {name: name } let person = {name//變量名和屬性名一樣 }//可計(jì)算屬性 const nameKey = 'name',ageKey = 'age',jobKey = 'job'; let person = {[nameKey]: 'matt',[ageKey]: 27,[jobKey]: 'software' }//簡(jiǎn)寫方法名 let person ={sayName: function(name) {console.log(name);} }//old version let person = {sayName(name) {console.log(name);} } person.sayName('mat')let person = {name_: '',get name() {return this.name_;},set name(name) {this.name_ = name;},sayName() {console.log(`my name is ${this.name_}`);} } person.name = 'matt' person.sayName() //簡(jiǎn)寫方法名對(duì)獲取函數(shù) 設(shè)置函數(shù)都是使用的 //簡(jiǎn)寫方法名與可計(jì)算屬性鍵相互兼容對(duì)象解構(gòu)
let person = {name: 'matt',age:21 } let {name:personName, age: personAge} = person; console.log(personName) console.log(personAge)let {name, age, job = 'software'} = person; console.log(name) console.log(age) console.log(job) let a,b; ({name:a, age:b}=person) console.log(a,b) //因?yàn)槭孪嚷暶髁薬b所以要在()里面創(chuàng)建對(duì)象
工廠模式
可以解決創(chuàng)建多個(gè)類似對(duì)象的問題,但沒有解決對(duì)象標(biāo)識(shí)問題(即新創(chuàng)建的對(duì)象是什么類型)
function createPerson(name,age,job) {let o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function () {console.log(this.name)}return o; } let preson1 = createPerson('nnn',22,'aaa') preson1.sayName()構(gòu)造函數(shù)模式
沒有顯式地創(chuàng)建對(duì)象
屬性和方法直接賦值給了this
沒有return
按照慣例,構(gòu)造函數(shù)的首字母是要大寫的,非構(gòu)造函數(shù)首字母小寫。
創(chuàng)建Person的實(shí)例,應(yīng)使用new操作符,會(huì)進(jìn)行如下操作。
1.在內(nèi)存中創(chuàng)建一個(gè)新對(duì)象
2.這個(gè)新對(duì)象內(nèi)部的[ [ Prototype ]]特性被賦值為構(gòu)造函數(shù)的prototype屬性
3.構(gòu)造函數(shù)內(nèi)部的this被賦值為這個(gè)新對(duì)象
4.執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼
5.返回該對(duì)象
構(gòu)造函數(shù)也可以使用函數(shù)表達(dá)式
構(gòu)造函數(shù)也是函數(shù)
如果有new,就是構(gòu)造函數(shù)
如果沒有就是普通函數(shù)
構(gòu)造函數(shù)的問題
不同的person都要定義相同的sayName
原型模式
每個(gè)函數(shù)都會(huì)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性是一個(gè)對(duì)象,包含應(yīng)該由特定引用類型的實(shí)例共享的屬性和方法。實(shí)際上,這個(gè)對(duì)象就是通過調(diào)用構(gòu)造函數(shù)創(chuàng)建的對(duì)象的原型。使用原型對(duì)象的好處是,在它上面定義的屬性和方法可以被對(duì)象實(shí)例共享。原來在構(gòu)造函數(shù)中直接賦值給對(duì)象實(shí)例的值,可以直接賦值給他們的原型
我理解:prototype這個(gè)屬性是對(duì)象,你賦值給它,就會(huì)讓所有對(duì)象實(shí)例都有賦值在上面的屬性和方法。這個(gè)對(duì)象是構(gòu)造函數(shù)創(chuàng)建的對(duì)象的原型,就是原材料、模板的感覺
function Person() {} Person.prototype.name = 'zhaosi' Person.prototype.age = 22; Person.prototype.job = 'soft' Person.prototype.sayName = function () {console.log(this.name) } let person1 = new Person(); person1.sayName()理解原型
函數(shù)有prototype屬性,指向原型對(duì)象
原型對(duì)象有constructor屬性,指向構(gòu)造函數(shù)
Person.prototype -> Person構(gòu)造函數(shù)的原型對(duì)象
Person.prototype.constructor -> Person
實(shí)例對(duì)內(nèi)部[ [Prototype ]]指針會(huì)賦值為構(gòu)造函數(shù)的原型對(duì)象,但是不能直接訪問。有些瀏覽器在對(duì)象(應(yīng)該不只是實(shí)例,原型對(duì)象應(yīng)該也有)上暴露__proto__屬性,通過這個(gè)屬性,可以訪問實(shí)例對(duì)象的原型對(duì)象。
所以,實(shí)例對(duì)象與原型對(duì)象有直接關(guān)系,與構(gòu)造函數(shù)并無直接關(guān)系
function Person() {} console.log(typeof Person.prototype)//object,是一個(gè)對(duì)象 console.log(Person.prototype) /* {constructor: ?}在瀏覽器上看,這不全*/ console.log(typeof Person.prototype.constructor)//function console.log(Person.prototype.constructor === Person)//true//正常的原型鏈都會(huì)終止于Object的原型對(duì)象,Object的原型對(duì)象是null console.log(Person.prototype.__proto__ === Object.prototype) //Person構(gòu)(造函數(shù)).prototype(原型對(duì)象).proto(的原型對(duì)象), //就是Object這個(gè)構(gòu)造函數(shù)的原型對(duì)象 //__proto__并不推薦使用,具體看MDN Object.prototype.__proto__console.log(Person.prototype.__proto__.constructor === Object) //Person這個(gè)構(gòu)造函數(shù)的原型對(duì)象的原型對(duì)象的構(gòu)造函數(shù)是Object console.log(Person.prototype.__proto__.__proto__ === null) //Person原型對(duì)象的原型對(duì)象(等于是Object的原型對(duì)象 )的原型對(duì)象是null //Object的原型對(duì)象 的原型對(duì)象是null console.log(Person.prototype.__proto__)構(gòu)造函數(shù)、原型對(duì)象和實(shí)例,是三個(gè)完全不同的對(duì)象
雖然不是所有實(shí)現(xiàn)都對(duì)外暴露了[ [Prototype ] ],但是可以使用isPrototypeOf(),會(huì)在傳入?yún)?shù)的[ [ Prototype]]指向調(diào)用它的對(duì)象時(shí)返回true
function Person() {} person1 = new Person() console.log(Person.prototype.isPrototypeOf(person1)) //true Object.getPrototypeOf(),返回參數(shù)的內(nèi)部特性[ [ Prototype]] 的值。 function Person() {} Person.prototype.name = 'liuneng' person1 = new Person() console.log(Object.getPrototypeOf(person1) == Person.prototype) console.log(Object.getPrototypeOf(person1).name)Object.setPrototypeOf()向?qū)嵗乃接刑匦?span id="ozvdkddzhkzd" class="token punctuation">[ [ Prototype]]寫入一個(gè)新值。這樣可以重寫一個(gè)對(duì)象的原型繼承關(guān)系 let biped = {numLegs:2 } let person = {name:'aaa' } Object.setPrototypeOf(person,biped) console.log(person.name) console.log(person.numLegs) console.log(Object.getPrototypeOf(person) === biped) //但不推薦使用這個(gè)方法Object.create()替代上面 let biped = {numLegs:2 } let person = Object.create(biped) person.name = 'aaa' console.log(person.name) console.log(person.numLegs) console.log(Object.getPrototypeOf(person) === biped)原型層級(jí)
就是使用一個(gè)屬性,會(huì)從實(shí)例開始,如果有就返回,如果沒有就找它的原型看有沒有,知道找到,或者到了最后還沒有
hasOwnProperty()實(shí)例對(duì)象是否有某個(gè)屬性(原型上面的不算)
Object.getOwnProperty()只對(duì)實(shí)例屬性有效。要取得原型對(duì)象的屬性,必須直接在原型對(duì)象上調(diào)用該方法
for-in and in
function Person() {} Person.prototype.name = 'nnn'; Person.prototype.age = 22; Person.prototype.job = 'aaa' Person.prototype.sayName = function () {console.log(this.name); }let person1 = new Person() let person2 = new Person() console.log(person1.hasOwnProperty('name')) console.log('name' in person1)person1.name = 'aaa' console.log(person1.hasOwnProperty('name'))in無論實(shí)例對(duì)象還是原型對(duì)象上,只要有就是true
function hasPrototypeProperty(object, name) {return !object.hasOwnProperty(name) && (name in object); } console.log(hasPrototypeProperty(person1,'name')) 可以判斷是否只存在于原型對(duì)象要想獲得對(duì)象上所有可枚舉的實(shí)例屬性,可以使用
Object.keys(),返回包含該對(duì)象所有可枚舉屬性名稱的字符串?dāng)?shù)組
只是自身的屬性
Object.getOwnPropertyNames()
枚舉 不枚舉 都可以獲得
Object.getOwnPropertySymbols()
針對(duì)符號(hào)為鍵的屬性
屬性枚舉順序
let k1 = Symbol('k1'),k2 = Symbol('k2'); let o = {1: 1,first: 'frist',[k1] : 'sym2',second: 'seconde',0: 0 } o[k2] = 'sum2' o[3] = 3; o.third = 'third' o[2] = 2; for(const i in o) {console.log(i) }for-in Object.keys()枚舉順序不確定
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.assign()枚舉順序確定,先以升序枚舉數(shù)值鍵、然后插入順序枚舉字符串和符號(hào)鍵。在對(duì)象字面量中定義的鍵以他們逗號(hào)分割的順序插入
對(duì)象迭代
const o = {foo: 'bar',baz: 1,qux: {} } console.log(Object.values(o)) console.log(Object.entries(o))輸出 [ 'bar', 1, {} ] [ [ 'foo', 'bar' ], [ 'baz', 1 ], [ 'qux', {} ] ]其他原型語法
為了避免每次創(chuàng)建一個(gè)實(shí)例對(duì)象,就需要在原型對(duì)象上添加相同的屬性和方法,所以以如下方式解決
function Person() {} Person.prototype = {name: 'aa',age: 22 } Object.defineProperty(Person.prototype,'constructor',{enumerable: false,value: Person }) //constructor 值改為Person,同時(shí)設(shè)置為不可枚舉原型的動(dòng)態(tài)性
function Person() {} let friend = new Person(); Person.prototype.sayHi = function () {console.log('hi') } friend.sayHi()這依然正確,因?yàn)閒riend有指向原型的指針,但如果重寫了整個(gè)原型對(duì)象,friend指針不能指向新的原型對(duì)象,此時(shí)就是錯(cuò)誤原生對(duì)象原型
console.log(typeof Array.prototype.sort) String.prototype.abc = function (text) {console.log(text)return 'success' } let msg = 'aaa' console.log(msg.abc('i am cool'))輸出 function i am cool success原型的問題
共享性,你改了一個(gè)可能就全改了;
弱化構(gòu)造函數(shù)傳遞初始化參數(shù)的能力
繼承(最復(fù)雜的一部分來了,個(gè)人目前覺得)
感覺書上這部分講的挺好,所以這部分筆記,打算主要采用費(fèi)曼學(xué)習(xí)法,把相關(guān)內(nèi)容講一下,看能不能自己把這些東西講明白了,別繞暈了。等以后再讀這本書,可能讀書筆記就以費(fèi)曼學(xué)習(xí)法為主,自己大量講解
繼承有接口繼承和實(shí)現(xiàn)繼承,而es只支持實(shí)現(xiàn)繼承,這主要通過原型鏈實(shí)現(xiàn)
原型鏈基本構(gòu)想:一個(gè)原型是另一個(gè)類型的實(shí)例,則這個(gè)原型本身有一個(gè)內(nèi)部指針指向另一個(gè)原型,另一個(gè)原型也有一個(gè)指針指向另一個(gè)構(gòu)造函數(shù),形成一個(gè)原型鏈
原型鏈
function SuperType() {this.property = true; } SuperType.prototype.getSuperValue = function() {return this.property } function SubType() {this.subproperty = false; } SubType.prototype = new SuperType() SubType.prototype.getSubValue = function () {return this.subproperty; } let instance = new SubType() console.log(instance.getSuperValue())首先構(gòu)造函數(shù)supertype,和構(gòu)造函數(shù)subtype,而super(后面簡(jiǎn)寫了)原型對(duì)象上有g(shù)etSuperValue這么一個(gè)方法。subtype的原型對(duì)象被賦值為supertype的實(shí)例對(duì)象,所以subtype.prototype就是supertype的一個(gè)實(shí)例,這個(gè)實(shí)例有一個(gè)指針,指向supertype.prototype,同時(shí),這個(gè)supertype.prototype有一個(gè)constructor屬性指向supertype這個(gè)構(gòu)造函數(shù)。現(xiàn)在,subtype的原型對(duì)象上添加一個(gè)
getSubValue的方法。然后,instance為subtype實(shí)例對(duì)象,他有一個(gè)指針指向subtype的原型對(duì)象,然后這個(gè)原型對(duì)象如上面所說,一直向上指。instance調(diào)用getSuperValue方法,返回subproperty,這個(gè)方法需要他一直往上找,找到supertype.prototype這個(gè)原型對(duì)象才能找到,同時(shí)這個(gè)方法返回property,這個(gè)屬性是實(shí)例屬性,在SubType.prototype這個(gè)實(shí)例上(他是Supertype的實(shí)例)
哦對(duì)了,他們的constructor都是supertype
默認(rèn)原型
所有引用類型都繼承自O(shè)bject。所以他們最后都能知道Object的原型對(duì)象,而toString()這些都在Object的原型對(duì)象上,所以自定義對(duì)象可以調(diào)用這些方法
原型與繼承關(guān)系
instance
isPrototypeOf()
都可以判斷繼承關(guān)系
關(guān)于方法
原型鏈的問題
1.共享性
2.子類型在實(shí)例化時(shí)不能給父類型的構(gòu)造函數(shù)傳參
盜用構(gòu)造函數(shù)(construct stealing 對(duì)象偽裝 經(jīng)典繼承)
子類構(gòu)造函數(shù)調(diào)用父類構(gòu)造函數(shù)
function SuperType() {this.colors = ['a','b','c'] } function SubType() {SuperType.call(this) } let ins1 = new SubType() ins1.colors.push('d') console.log(ins1.colors)let ins2 = new SubType() console.log(ins2.colors) 解決了共享性問題1.傳遞參數(shù)
可以在子類構(gòu)造函數(shù)向父類構(gòu)造函數(shù)傳參
2.問題
必須在構(gòu)造函數(shù)中定義方法,因此函數(shù)不能重用
子類也不能訪問父類原型上定義的方法
組合繼承(偽經(jīng)典繼承)
使用原型鏈繼承原型上的屬性和方法,而通過盜用構(gòu)造函數(shù)繼承實(shí)例屬性。讓原型上的方法得以實(shí)現(xiàn)重用,每個(gè)實(shí)例又可以有自己的屬性
function SuperType(name) {this.name = namethis.colors = ['a','b'] } SuperType.prototype.sayName = function () {console.log(this.name) } function SubType(name,age) {SuperType.call(this,'nnn');this.age =age } SubType.prototype = new SuperType() SubType.prototype.sayAge = function() {console.log(this.age); } let i1 = new SubType('aaa',22) i1.colors.push('c') console.log(i1.colors) i1.sayAge() i1.sayName()let i2 = new SubType('ggg',222) console.log(i2.colors) i2.sayAge() i2.sayName()輸出 [ 'a', 'b', 'c' ] 22 nnn [ 'a', 'b' ] 222 nnn首先,由上往下看,subtype.prototype 是一個(gè)supertype的實(shí)例,然后他的上面有個(gè)sayAge方法。同時(shí)Subtype又用了盜用構(gòu)造函數(shù)這一模式。所以創(chuàng)建i1這個(gè)實(shí)例對(duì)象,會(huì)用構(gòu)造函數(shù)subtype,而構(gòu)造函數(shù)中先調(diào)用父類構(gòu)造函數(shù),為實(shí)例創(chuàng)建colors和name兩個(gè)屬性。同時(shí)supertype的原型對(duì)象上有一個(gè)sayName的方法,他會(huì)輸出實(shí)例對(duì)象(i1)上面的name屬性;然后完成supertype.call之后,會(huì)為實(shí)例對(duì)象
i1設(shè)置age屬性,同時(shí)他是subtype的實(shí)例對(duì)象,他的原型對(duì)象上有sayAge方法,輸出實(shí)例對(duì)象上的age。因?yàn)閟ubtype的原型對(duì)象是supertype的實(shí)例對(duì)象,所以順著原型鏈,subtype的原型對(duì)象內(nèi)部有一個(gè)指針,指向supertype的原型對(duì)象,這個(gè)對(duì)象上有sayName,所以i1這個(gè)實(shí)例對(duì)象全都有,他得到了一切
i2同理
組合繼承彌補(bǔ)了原型鏈和盜用構(gòu)造函數(shù)不足,是使用最多的繼承模式。也保留了instanceOf操作符和isPrototypeOf()方法識(shí)別合成對(duì)象的能力
原型式繼承
不自定義類型也可以通過原型實(shí)現(xiàn)對(duì)象之間的信息共享
function object(o) {function F() {}F.prototype = o;return new F(); } let person = {name: 'aaa',friends: ['zhangsan','lisi'] } let anotherPerson = object(person) anotherPerson.name = 'xiaoming' anotherPerson.friends.push('wangwu') console.log(person.friends) console.log(person.name)輸出 [ 'zhangsan', 'lisi', 'wangwu' ] aaa把一個(gè)對(duì)象o傳進(jìn)去,然后臨時(shí)創(chuàng)建一個(gè)構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)的原型對(duì)象賦值為o,然后返回這個(gè)構(gòu)造函數(shù)的實(shí)例對(duì)象,本質(zhì)上實(shí)現(xiàn)了對(duì)o的淺復(fù)制
在下面的實(shí)際例子中。anotherPeroson就是返回的實(shí)例對(duì)象,所以他的原型對(duì)象賦值為o,有name,和friends,因?yàn)闇\復(fù)制,所以改變他的friends就是改變person的friends
如此,實(shí)現(xiàn)了共享。這里實(shí)際上克隆了兩個(gè)person
Object.create()將上面的思想規(guī)范化了
這種方法適合不需要單獨(dú)創(chuàng)建構(gòu)造函數(shù),但仍然需要在對(duì)象間共享信息的場(chǎng)合。但要記住,屬性中包含的引用值始終會(huì)在相關(guān)對(duì)象間共享,跟使用原型模式是一樣的
寄生式繼承
類似于寄生構(gòu)造函數(shù)和工廠模式:創(chuàng)建一個(gè)實(shí)現(xiàn)繼承的函數(shù),以某種方式增強(qiáng)對(duì)象,然后返回這個(gè)對(duì)象
function createAnother(original) {let clone = object(original);clone.sayHi = function () {console.log('hi');}return clone; } function object(o) {function F() {}F.prototype = o;return new F(); }let person = {name:'aa',friends : ['a','b'] } let anotherPerson = createAnother(person) anotherPerson.sayHi()先說上面抽象的createAnother中把original傳入object,返回一個(gè)實(shí)例對(duì)象,賦值給clone,然后clone增強(qiáng)了sayHi方法,再返回clone這個(gè)對(duì)象
所以anotherPerson就是person的增強(qiáng)對(duì)象,具備sayHi方法
寄生式繼承同樣適合主要關(guān)注對(duì)象,不在乎類型和構(gòu)造函數(shù)的場(chǎng)景。object不是寄生式繼承必需的,任何返回新對(duì)象的函數(shù)都可以使用,上面的更多的是一種思想
寄生式組合繼承
組合繼承存在效率問題。主要的效率問題是父類構(gòu)造函數(shù)始終會(huì)被調(diào)用兩次:以此在創(chuàng)建子類原型是,另一次在子類構(gòu)造函數(shù)中調(diào)用。本質(zhì)上,子類原型對(duì)象是要包含超類對(duì)象的所有實(shí)例屬性。子類構(gòu)造函數(shù)只要在執(zhí)行時(shí)重寫自己的原型就行了。
不通過調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個(gè)副本。
通過object,返回一個(gè)原型對(duì)象是父類原型對(duì)象的實(shí)例對(duì)象,賦值給prototype;然后解決constructor丟失問題;最后,子類原型對(duì)象賦值為prototype,即子類原型對(duì)象就是一個(gè)實(shí)例,其指向父類原型對(duì)象
雖然很繞,但道理確實(shí)和之前實(shí)現(xiàn)的是一樣的
function inheritPrototype(subType,superType) {let prototype = object(superType.prototype);//創(chuàng)建對(duì)象prototype.constructor = subType;//增強(qiáng)對(duì)象,解決constructor丟失問題subType.prototype = prototype;//賦值對(duì)象 } function SuperType(name) {this.name = name;this.colors = ['a','b']; } SuperType.prototype.sayName = function () {console.log(this.name); } function SubType(name, age) {SuperType.call(this,name);this.age = age; } inheritPrototype(SubType,SuperType); SubType.prototype.sayAge = function () {console.log(this.age); }只用一次superType,提高了效率。算是引用類型繼承的最佳模式
類
class關(guān)鍵字具有正式定義類的能力,是基礎(chǔ)性語法糖結(jié)構(gòu)。實(shí)際上背后使用的仍然是原型和構(gòu)造函數(shù)的概念
class Person{}類聲明 const Animal = class {};類表達(dá)式 類定義不能提升類受塊作用域限制類包含:構(gòu)造函數(shù)方法 、 實(shí)例方法 、 獲取函數(shù)、 設(shè)置函數(shù) 、 靜態(tài)類方法,但都不是必需的
類構(gòu)造函數(shù)
constructor關(guān)鍵字用于在類定義塊內(nèi)部創(chuàng)建類的構(gòu)造函數(shù)。方法名constructor會(huì)告訴解釋器在使用new操作符創(chuàng)建類的新實(shí)例時(shí),應(yīng)該調(diào)用這個(gè)函數(shù)。構(gòu)造函數(shù)的定義不是必需的,不定義默認(rèn)為空函數(shù)
1.實(shí)例化
使用new 實(shí)例化Person等于使用new調(diào)用其構(gòu)造函數(shù)
使用new調(diào)用類的構(gòu)造函數(shù)會(huì)有如下操作:
在內(nèi)存中創(chuàng)建一個(gè)新對(duì)象。
這個(gè)新對(duì)象內(nèi)部[ [ prototype]]指針被賦值為構(gòu)造函數(shù)的prototype屬性。
構(gòu)造函數(shù)內(nèi)部的this被賦值為這個(gè)新對(duì)象(this指向新對(duì)象)
執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼
如果構(gòu)造函數(shù)返回非空對(duì)象則返回;否則,返回剛創(chuàng)建的對(duì)象
默認(rèn)分情況返回this對(duì)象;如果沒有什么引用新創(chuàng)建的this對(duì)象,那么這個(gè)對(duì)象被銷毀(我理解,就是返回來沒人接著,就把返回的銷毀了)。如果返回不是this對(duì)象,而是其他對(duì)象,那么這個(gè)對(duì)象不會(huì)通過instanceof操作符檢測(cè)出和類相關(guān)聯(lián)
class Person {constructor(override) {this.foo = 'foo';if(override) {return {bar: 'bar'}}} } let p1 = new Person() let p2 = new Person(true) console.log(p1) console.log(p1 instanceof Person) console.log(p2) console.log(p2 instanceof Person)輸出 Person { foo: 'foo' } true { bar: 'bar' } false2.類是特設(shè)函數(shù)
es并沒有正式的類這個(gè)類型。es類就是一種特殊函數(shù)
typeof返回function
類標(biāo)簽符有prototype屬性,而這個(gè)原型也有一個(gè)constructor屬性指向自身
class Person{} console.log(Person.prototype) console.log(Person === Person.prototype.constructor)類是JavaScript中的一等公民(在編程語言中,一等公民可以作為函數(shù)參數(shù),可以作為函數(shù)返回值,也可以賦值給變量。)。因此可以像其他對(duì)象或函數(shù)引用一樣把類作為參數(shù)傳遞
let classList = [class {constructor(id) {this.id_ = id;console.log(`instace ${this.id_}`);}} ]; function createInstace(classDefinition, id) {return new classDefinition(id); } let foo = createInstace(classList[0],314);立即實(shí)例化 let p = new class Foo {constructor(x) {console.log(x);} }('bar'); console.log(p)實(shí)例、原型和類成員
類的語法可以非常方便的定義應(yīng)該存在于實(shí)例上的成員、應(yīng)該存在于原型上的成員,以及應(yīng)該存在于類本身的成員
1.實(shí)例成員
每次通過new調(diào)用類標(biāo)識(shí)符時(shí),都會(huì)執(zhí)行類構(gòu)造函數(shù)。在這個(gè)函數(shù)內(nèi)部,可以為新創(chuàng)建的實(shí)例(this)添加自由屬性。至于添加什么樣的屬性,則沒有限制。另外,在構(gòu)造函數(shù)執(zhí)行完畢后,仍然可以給實(shí)例繼續(xù)添加新成員。
每個(gè)實(shí)例都對(duì)應(yīng)一個(gè)唯一的成員對(duì)象,這意味所有成員不會(huì)在原型上共享
2.原型方法與訪問器
為了在實(shí)例間共享方法,類定義語法把在類塊中定義的方法稱為原型方法
類定義也支持獲取和設(shè)置訪問器,與普通對(duì)象行為一樣
class Person {set name(newName) {this.name_ = newName}get name() {return this.name_} } let p = new Person() p.name = 'jake' console.log(p.name)3.靜態(tài)類方法
可以在類上定義靜態(tài)方法。這些方法通常用于執(zhí)行不特定用于實(shí)例的操作,也不要求存在類的實(shí)例。與原型成員類似,每個(gè)雷傷只能有一個(gè)靜態(tài)成員
靜態(tài)類成員在類定義中使用static關(guān)鍵字作為前綴。在靜態(tài)成員中,this引用類自身。其他同原型成員
大概是實(shí)例上的成員用constructor,
原型上的成員用原型方法
類本身上的成員用靜態(tài)方法
4.非函數(shù)原型和類成員
class Person {} Person.greeting = 'hello' Person.prototype.name = 'aaa' let p = new Person() 在類定義外部添加成員數(shù)據(jù)5.迭代器與生成器方法
支持在原型和類本身上定義生成器方法
因此,可以通過添加一個(gè)默認(rèn)的迭代器,把類實(shí)例變?yōu)榭傻鷮?duì)象
class Person {constructor() {this.nicknames = ['jek','jjj','hhhhhh'];}*[Symbol.iterator]() {yield *this.nicknames.entries();} } let p = new Person() for(let [idx, nickname] of p) {console.log(nickname) }繼承
類繼承機(jī)制。背后依舊是原型鏈
1.繼承基礎(chǔ)
extends關(guān)鍵字,就可以繼承任何擁有[ [ Construct]]和原型的對(duì)象。這意味不僅可以繼承一個(gè)類,也可以繼承普通的構(gòu)造函數(shù)(保持向后兼容)
2.構(gòu)造函數(shù)、HomeObject、super()
派生類的方法可以通過super關(guān)鍵字引用他們的原型。這個(gè)關(guān)鍵字只嗯呢該在派生類中使用,僅限于類構(gòu)造函數(shù)、實(shí)例方法、靜態(tài)方法內(nèi)部。在類構(gòu)造函數(shù)用super可以調(diào)用父類構(gòu)造函數(shù)
es6給類構(gòu)造函數(shù)和靜態(tài)方法添加了內(nèi)部特性[ [ HomeObject]],這個(gè)特性是一個(gè)指針,指向定義該方法的對(duì)象。這個(gè)指針是自動(dòng)復(fù)制的,而且只能在js引擎內(nèi)部訪問,super始終會(huì)定義為[ [ HomeObject]]的原型
super只在派生類構(gòu)造函數(shù)和靜態(tài)方法使用
不能單獨(dú)使用super,要么用它調(diào)用構(gòu)造函數(shù)或者靜態(tài)方法
super()會(huì)調(diào)用父類構(gòu)造函數(shù),并將返回的實(shí)例賦值給this
沒有定義類構(gòu)造函數(shù),在實(shí)例化派生類時(shí)會(huì)調(diào)用super()
3.抽象基類
供其他類繼承,但本身不被實(shí)例化。new.target可以實(shí)現(xiàn),其保存通過new關(guān)鍵字調(diào)用的類或函數(shù)。通過在實(shí)例化時(shí)檢測(cè)new.target是不是抽象基類,可以阻止對(duì)其實(shí)例化
也可以通過抽象基類要求派生類必須定義某個(gè)方法
4.繼承內(nèi)置類型
可以方便擴(kuò)展內(nèi)置類型
5.類混入
把不同類的行為集中到一個(gè)類是一種常見的js模式。雖然es6沒有顯示支持多類繼承,但通過現(xiàn)有特性可以輕松模擬這種行為。
混入模式可以通過在一個(gè)表達(dá)式中連綴多個(gè)混入元素實(shí)現(xiàn),這個(gè)表達(dá)式最終會(huì)解析為一個(gè)可以被繼承的類。
class Vehicle {} let FooM = (SuperC) => class extends SuperC {foo() {console.log('aaa')} } let BsrM = (SuperC) => class extends SuperC {boo() {console.log('bbb')} } let Caa = (SuperC) => class extends SuperC {coo() {console.log('ccc')} } class Bus extends FooM(BsrM(Caa(Vehicle))) {} let b = new Bus() b.foo() b.boo() b.coo() class Vehicle {} let FooM = (SuperC) => class extends SuperC {foo() {console.log('aaa')} } let BsrM = (SuperC) => class extends SuperC {boo() {console.log('bbb')} } let Caa = (SuperC) => class extends SuperC {coo() {console.log('ccc')} } function mix(BaseClass,...Mixins) {return Mixins.reduce((acc,cur) => cur(acc), BaseClass) } class Bus extends mix(Vehicle,FooM,BsrM,Caa) {} let b = new Bus() b.foo() b.boo()最后,composition over inheritance 復(fù)合勝過繼承,react中會(huì)有體現(xiàn),他拋棄混入模式,轉(zhuǎn)向了復(fù)合模式
總結(jié)
以上是生活随笔為你收集整理的红宝书读书笔记 第八章的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSU 1224 ACM小组的古怪象棋
- 下一篇: 通过url下载图片