正如上篇所提到的,有些人認爲JavaScript並非真正的面嚮對象語言,在經典的面嚮對象語言中,您可能傾向於定義類對象,而後您能夠簡單地定義哪些類繼承哪些類,JavaScript使用了另外一套實現方式,繼承的對象函數並非經過複製而來,而是經過原型鏈繼承(一般被稱爲 原型式繼承 —— prototypal inheritance)。ios
一個函數,有三種角色。
當成普通函數,當成構造函數,當成對象es6
function Person (nickname) { var age = 15 //普通函數 標記1 this.nickname = nickname //構造函數 this.sayName = function() { console.log(this.nickname) } console.log(age) 標記2 } Person.nickname = '張三' //對象 var p1 = new Person('李四') //'15' console.log(Person.nickname) //'張三' p1.sayName() //'李四' 標記4
若是標記1處改爲 var nickname = 'fyy' 標記2處改爲 console.log(nickname)
則後面的輸出分別爲 'fyy' '張三' 'fyy'axios
實例屬性:又稱成員就是實例裏面的屬性babel
var p1 = new Person('fyy',11) p1// {age:11,name:'fyy'} //name和age就是實例屬性
原型屬性:就是類的原型上的屬性
Person.prototype.say = function(){}
ar p1 = new Person('fyy',11)
p1.say // say就是調用的Person的原型屬性app
私有屬性:就是隻能在類的內部訪問的方法和屬性mvvm
class IncreasingCounter { #count = 0; get value() { console.log('Getting the current value!'); return this.#count; } increment() { this.#count++; } } const counter = new IncreasingCounter(); counter.#count // 報錯 counter.#count = 42 // 報錯
靜態方法:類上的靜態方法就表示該方法不會被實例繼承,而是直接經過類來調用
Array的isArray就是靜態方法,只能經過實例調用由於掛在原型上沒有意義,必然是Array的實例才能調用
同理還有String的fromCharCode等等函數
Array.isArray([1,2,3]) // //定義 class Array { static isArray(){//...} constructor(){ //... } //... }
說到繼承,首先得明白繼承的是什麼東西。我的認爲繼承應該分爲成員屬性和原型屬性的繼承。
實例屬性是不能共用的屬性或者方法,好比身份證。
原型屬性是能共用的屬性或者方法,好比愛好屬性,吃飯方法。this
這個比較簡單,實例屬性用利用構造函數和call或者applyspa
const Person = function (name) { this.name = name } const Students = function (name) { Person.call(this,name) } const xm = new Students('小明') console.log(xm) //Students {name: "小明"}
這裏裏面坑有點多,你們聽我娓娓道來。
經過原型鏈實現原型屬性繼承確定沒錯,可是咱們設計的時候有個原則 子類須要有本身的原型,父類也必需要有本身的原型,子實例在本身的原型上找不到屬性的時候纔會到父原型上去找
子類.prototype = 父類.prototype 這樣確定不行,雖然能繼承父類原型的方法,可是子類的原型和父類的原型是同一個,給子類原型添加方法的時候,至關於給父類的原型也添加了一個方法。so咱們應該有一個緩衝
。prototype
子類實例---->子類原型------->中間對象------->父類原型 //沿着箭頭能訪問,表現上符合咱們的設計原則
最多見的是使用父類的實例當這個中間對象。
Children.prototype = new Parent()
可是了這麼作有個很差的地方。會實例化一次父類。若是父類特別複雜,好比axios,那麼會帶來不少額外的開銷。
咱們看一下中間對象有什麼做用,實際上只起了一個隔離和原型重定向的做用。徹底能夠用一個空對象實現這個功能
//實現中間對象 var fn = function() {} fn.prototype = Parent.prototype Children.prototype = new fn()
實際上,這個就是Oject.create()的實現
//Oject.create() Object.create = function(obj){ var fn = funcion(){} fn.prototype = obj reurturn new fn() }
如今既要繼承實例屬性,又要繼承原型屬性。
const Person = function (name) { this.name = name } Person.prototype.eat = function () { console.log('i am hungry,i want to eat!') } const Student = function (name) { Person.call(this,name) } Student.prototype = Object.create(Person.prototype) //注意這裏!原型重定向的後遺症 const xm = new Student ('小明') xm.name //'小明' xm.eat() //i am hungry,i want to eat! console.log(xm) //Student {name: "小明"} // name: "小明" // __proto__: Person // __proto__: //這一層是個空對象,只有一個__proto__屬性指向Person的原型 // eat: ƒ () // constructor: ƒ (name) // __proto__: Object
可是這裏 xm.constructor.name // Person
這裏由於原型重定向後沒有重置construtor,xm自己沒有construtor只能找咱們建立的空對象,空對象沒有construtor因此繼續往上找,找到了Person.prototype上的construtor爲 Person
因此解決思路很簡單,在改寫原型鏈的時候重置一下constructor就好了
... var temObj = Object.create(Person.prototype) temObj.constructor = Student Student.prototype =temObj ... xm.constructor.name // Student ok搞定
既然new在「類」的建立裏面必須使用,那麼咱們就說一下new到底幹了啥事情
1.建立一個對象o繼承構造函數
2.讓構造函數的this變爲o,並執行構造函數,將返回值設置爲k
3.若是k
//仿寫new function new1(func) { var o = Object.create(func.prototype) var k = func.apply(o,arguments[1]) return typeof k === 'object'? k: o } const x = new1(Student,['張三']) x.name //'張三' x.eat //'i am hungry,i want to eat!'
咱們回過頭再分析一下構造函數模式繼承
const Person = function (name) { this.name = name } const Students = function (name) { Person.call(this,name) //this是student實例 } const xm = new Students('小明') //分析這裏幹了什麼 console.log(xm) //Students {name: "小明"}
1.讓空對象o繼承Students(o能訪問Students的原型)
2.student執行,執行Person的代碼,this是o,而且傳入name, o.name='小明'返回的k是undefined
3.返回o,也就是返回{name:'小明'}
class Person { } class Student extends person{ }
在babel es2015-loose模式下編譯後的源碼以下
"use strict"; function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var Person = function Person() { }; var Student = /*#__PURE__*/ function (_person) { _inheritsLoose(Student, _person); function Student() { return _person.apply(this, arguments) || this; } return Student; }(person);
嚴格模式下,高級單例模式返回一個Student, 能夠看到Person的實例屬性用的Person的構造函數+apply繼承的
原型屬性用的_inheritsLoose這個方法繼承的
_inheritsLoose方法貌似就是咱們以前說的原型屬性繼承的終極解決方案吧?
未完待續....