每一個JS函數中都有一個prototype(原型)屬性,指向這個函數的原型對象,通過這個函數產生的實例對象都有一個__proto__(隱式原型)屬性,這個屬性也是指向同一個原型對象,所有的實例對象的屬性都會繼承這個原型對象的屬性,原型對象上也有一個__proto__屬性,指向的objec原型對象,所有的對象屬性都會繼承objec原型對象屬性。而object原型對象的__proto__指向的是null。當我們訪問對象的某個屬性時,就會從實例對象,原型對象,object原型對象上層層尋找,由此形成原型鏈。而原型就是原型對象上的屬性。
應用
需要給每個對象都添加一個方法時,顯然不可能new出一個對象就添加一個方法,而是在原型對象上添加方法。
在觀察者模式、發布訂閱模式中,也是通過在原型對象上添加方法和屬性實現。
手寫原型鏈繼承
所謂繼承,就是一個對象使用另外一個對象的方法和屬性
- 普通繼承方式
這種情況下,父類型的對象原型上不能有任何屬性和方法,因為父類型的prototype指向已經不是對象原型了
//設定一個Parents構造函數 function Parents(){ this.job = 'makeMoney' } //設定一個Child構造函數 function Child(){ this.study = 'college' } //讓Parents的原型指向Child的實例對象 Parents.prototype = new Child() const Chen = new Parents console.log(Chen.study,Chen.job);
- 對象原型指向實例對象的繼承
//設定一個Parents構造函數 function Parents(){ this.job = 'makeMoney' this.Sub = function (){ console.log('Parents的方法'); } } //把方法寫在Parents對象原型上 Parents.prototype.Sud = function (){ console.log('Parents對象原型上的方法'); } //設定一個Child構造函數 function Child(){ this.study = 'college' this.Sup = function (){ console.log('Child的方法'); } } //把方法寫在Child對象原型上 Child.prototype.SuC = function () { console.log('Child對象原型上的方法'); } //讓Parents的原型指向Child的實例對象 Parents.prototype.__proto__ = new Child() const Chen = new Parents console.log(Chen.Sub,Chen.Sud,Chen.SuC,Chen.Sup);
這樣的話,所有的方法都可以使用
- 另外一種繼承方式
//屬性繼承 function Person (name, age) { this.name = name this.age = age } // 方法定義在構造函數的原型上 Person.prototype.getName = function () { console.log(this.name)} function Teacher (name, age, subject) { Person.call(this, name, age) this.subject = subject } //方法繼承 Teacher.prototype = Object.create(Person.prototype) Teacher.prototype.constructor = Teacher
這種方式的方法繼承不是引用關系,而是拷貝關系。兩者互補影響