亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

面向對象的三大特性:封裝、繼承和多態。上一篇我們簡單的了解了封裝的過程,也就是把對象的屬性和方法封裝到一個函數中,這一篇講一下JAVAScript中繼承的實現,繼承是面向對象中非常重要的特性,它可以幫助我們提高代碼的復用性。繼承主要的思想就是將重復的代碼邏輯抽取到分類中,子類只需要通過繼承分類,就可以使用分類中的方法,但是在實現JavaScript繼承之前,需要先了解一個重要的知識點“原型鏈”。

1.JavaScript中的原型鏈

在上一篇JavaScript面向對象—對象的創建和操作中已經簡單的了解過了JavaScript中對象的原型和函數的原型,當我們從一個對象上獲取屬性時,如果在當前對象自身沒有找到該屬性的話,就會去它原型上面獲取,如果原型中也沒有找到就會去它原型的原型上找,沿著這么一條線進行查找,那么這條線就是我們所說的原型鏈了。

示例代碼:

const obj = {
  name: 'curry',
  age: 30
}

obj.__proto__ = {}
obj.__proto__.__proto__ = {}
obj.__proto__.__proto__.__proto__ = { height: 1.83 }

console.log(obj.height) // 1.83

對應的內存中的查找過程:

JavaScript面向對象—繼承的實現

 

當通過原型鏈查找某個屬性時,一直找不到的話會一直查找下去么?肯定是不會的,JavaScript的原型鏈也是有盡頭的,這個盡頭就是Object的原型。

2.Object的原型

事實上,不管是對象還是函數,它們原型鏈的盡頭都是Object的原型,也稱之為頂層原型,我們可以打印看看這個頂層原型長什么樣。

(1)打印Object的原型

console.log(Object.prototype)
  • 在node環境中:
JavaScript面向對象—繼承的實現

 

  • 在瀏覽器中:
JavaScript面向對象—繼承的實現

 

(2)Object原型的特殊之處

  • 如果我們再次打印Object.prototype的原型,這個原型屬性已經指向了null
  • console.log(Object.prototype.__proto__) // null
  • 并且在Object.prototype上有很多默認的屬性和方法,像toString、hasOwnProperty等;

(3)上一篇中講到當使用new操作符調用構造函數時,其對象的[[prototype]]會指向該構造函數的原型prototype,其實Object也是一個構造函數,因為我們可以使用new操作符來調用它,創建一個空對象。

  • 示例代碼:
  • const obj = new Object() obj.name = 'curry' obj.age = 30 console.log(obj.__proto__ === Object.prototype) // true console.log(obj.__proto__) // [Object: null prototype] {} console.log(obj.__proto__.__proto__) // null
  • 內存表現:
  •  

(4)總結

  • 從Object的原型可以得出一個結論“原型鏈最頂層的原型對象就是Object的原型對象”,這也就是為什么所有的對象都可以調用toString方法了;
  • 從繼承的角度來講就是“Object是所有類的父類”;

3.JavaScript繼承的實現方案

3.1.方案一:通過原型鏈實現繼承

如果需要實現繼承,那么就可以利用原型鏈來實現了。

  • 定義一個父類Person和子類Student;
  • 父類中存放公共的屬性和方法供子類使用;
  • 核心:將父類的實例化對象賦值給子類的原型;
// 定義Person父類公共的屬性
function Person() {
  this.name = 'curry'
  this.age = 30
}
// 定義Person父類的公共方法
Person.prototype.say = function() {
  console.log('I am ' + this.name)
}

// 定義Student子類特有的屬性
function Student() {
  this.sno = 101111
}

// 實現繼承的核心:將父類的實例化對象賦值給子類的原型
Student.prototype = new Person()

// 定義Student子類特有的方法
Student.prototype.studying = function() {
  console.log(this.name + ' studying')
}

// 實例化Student
const stu = new Student()
console.log(stu.name) // curry
console.log(stu.age) // 30
console.log(stu.sno) // 101111
stu.say() // I am curry
stu.studying() // curry studying

內存表現:

JavaScript面向對象—繼承的實現

 

缺點

  • 從內存表現圖中就可以看出,當打印stu對象時,name和age屬性是看不到的,因為不會打印原型上的東西;
  • 當父類中的屬性為引用類型時,子類的多個實例對象會共用這個引用類型,如果進行修改,會相互影響;
  • 在使用該方案實現繼承時,屬性都是寫死的,不支持動態傳入參數來定制化屬性值;

3.2.方案二:借用構造函數實現繼承

針對方案一的缺點,可以借用構造函數來進行優化。

  • 在子類中通過call調用父類,這樣在實例化子類時,每個實例就可以創建自己單獨屬性了;
// 定義Person父類公共的屬性
function Person(name, age) {
  this.name = name
  this.age = age
}
// 定義Person父類的公共方法
Person.prototype.say = function() {
  console.log('I am ' + this.name)
}

// 定義Student子類特有的屬性
function Student(name, age, sno) {
  // 通過call調用Person父類,創建自己的name和age屬性
  Person.call(this, name, age)
  this.sno = sno
}

// 實現繼承的核心:將父類的實例化對象賦值給子類的原型
Student.prototype = new Person()

// 定義Student子類特有的方法
Student.prototype.studying = function() {
  console.log(this.name + ' studying')
}

// 實例化Student
const stu1 = new Student('curry', 30, 101111)
const stu2 = new Student('kobe', 24, 101112)
console.log(stu1) // Person { name: 'curry', age: 30, sno: 101111 }
console.log(stu2) // Person { name: 'kobe', age: 24, sno: 101112 }

內存表現:

JavaScript面向對象—繼承的實現

 

缺點:

  • 在實現繼承的過程中,Person構造函數被調用了兩次,一次在new Person(),一次在Person.call();
  • 在Person的實例化對象上,也就是stu1和stu2的原型上,多出來了沒有使用的屬性name和age;

3.3.方案三:寄生組合式繼承

通過上面兩種方案,我們想實現繼承的目的是重復利用另外一個對象的屬性和方法,如果想解決方案二中的缺點,那么就要減少Person的調用次數,避免去執行new Person(),而解決的辦法就是可以新增一個對象,讓該對象的原型指向Person的原型即可。

(1)對象的原型式繼承

將對象的原型指向構造函數的原型的過程就叫做對象的原型式繼承,主要可以通過以下三種方式實現:

  • 封裝一個函數,將傳入的對象賦值給構造函數的原型,最后將構造函數的實例化對象返回;
  • function createObj(o) { // 定義一個Fn構造函數 function Fn() {} // 將傳入的對象賦值給Fn的原型 Fn.prototype = o // 返回Fn的實例化對象 return new Fn() } const protoObj = { name: 'curry', age: 30 } const obj = createObj(protoObj) // 得到的obj對象的原型已經指向了protoObj console.log(obj.name) // curry console.log(obj.age) // 30 console.log(obj.__proto__ === protoObj) // true
  • 改變上面方法中的函數體實現,使用Object.setPrototypeOf()方法來實現,該方法設置一個指定的對象的原型到另一個對象或null;
  • function createObj(o) { // 定義一個空對象 const newObj = {} // 將傳入的對象賦值給該空對象的原型 Object.setPrototypeOf(newObj, o) // 返回該空對象 return newObj }
  • 直接使用Object.create()方法,該方法可以創建一個新對象,使用現有的對象來提供新創建的對象的__proto__;
  • const protoObj = { name: 'curry', age: 30 } const obj = Object.create(protoObj) console.log(obj.name) // curry console.log(obj.age) // 30 console.log(obj.__proto__ === protoObj) // true

(2)寄生組合式繼承的實現

寄生式繼承就是將對象的原型式繼承和工廠模式進行結合,即封裝一個函數來實現繼承的過程。而這樣結合起來實現的繼承,又可以稱之為寄生組合式繼承。下面就看看具體的實現過程吧。

  • 創建一個原型指向Person父類的對象,將其賦值給Student子類的原型;
  • 在上面的實現方案中,Student子類的實例對象的類型都是Person,可以通過重新定義constructor來優化;
// 定義Person父類公共的屬性
function Person(name, age) {
  this.name = name
  this.age = age
}
// 定義Person父類的公共方法
Person.prototype.say = function() {
  console.log('I am ' + this.name)
}

// 定義Student子類特有的屬性
function Student(name, age, sno) {
  // 通過call調用Person父類,創建自己的name和age屬性
  Person.call(this, name, age)
  this.sno = sno
}

// 調用Object.create方法生成一個原型指向Person原型的對象,并將這個對象賦值給Student的原型
Student.prototype = Object.create(Person.prototype)
// 定義Student原型上constructor的值為Student
Object.defineProperty(Student.prototype, 'constructor', {
  configurable: true,
  enumerable: false,
  writable: true,
  value: Student
})

// 定義Student子類特有的方法
Student.prototype.studying = function() {
  console.log(this.name + ' studying')
}

// 實例化Student
const stu1 = new Student('curry', 30, 101111)
const stu2 = new Student('kobe', 24, 101112)
console.log(stu1) // Student { name: 'curry', age: 30, sno: 101111 }
console.log(stu2) // Student { name: 'kobe', age: 24, sno: 101112 }

內存表現:

JavaScript面向對象—繼承的實現

 

總結:

  • 多個地方用到了繼承,可以將上面的核心代碼賦值在一個函數里面,如果不想用Object.create(),也可以使用上面封裝的createObj函數;
  • function createObj(o) { function Fn() {} Fn.prototype = o return new Fn() } /** * @param {function} SubClass * @param {function} SuperClass */ function inherit(SubClass, SuperClass) { SubClass.prototype = createObj(SuperClass.prototype) Object.defineProperty(SubClass.prototype, 'constructor', { configurable: true, enumerable: false, writable: true, value: SubClass }) }
  • 寄生組合式實現繼承的原理其實就是創建一個空對象用于存放子類原型上的方法,并且這個對象的原型指向父類的原型,在ES6中推出的class的實現原理就在這;

 

原文地址:
https://www.cnblogs.com/MomentYY/p/15999285.html

分享到:
標簽:JavaScript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定