91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript 常見的繼承方式匯總

發布時間:2020-10-08 13:13:07 來源:腳本之家 閱讀:330 作者:guoqi 欄目:開發技術

原型鏈機制:

  在ECMAscript中描述了原型鏈的概念,并將原型鏈作為實現繼承的主要方法,其基本思想就是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。

構造函數和原型還有實例之間的關系:

  每個構造函數都有一個原型對象(prototype),原型對象都包含一個指向構造函數的指針(constructor),而實例都包含一個指向原型對象的內部指針 ( __propto__ ) 。關系圖如下圖所示:

JavaScript 常見的繼承方式匯總

  每一個Function都是Object基類的一個實例,所以每一個Function上都有一個__proto__指向了Object.prototype

  當查找一個實例的屬性時,會先從這個實例的自定義屬性上找,如果沒有的話通過__proto__去實例所屬類的原型上去找,如果還沒有的話再通過原型(原型也是對象,只要是對象就有__proto__屬性)的__proto__到Object的原型上去找,一級一級的找,如果沒有就undefined。

  所以引用類型之間的繼承就是通過原型鏈機制實現的。

一.原型繼承

  原型繼承:把父類的私有+公有的屬性和方法,都作為子類公有的屬性。

  核心:不是把父類私有+公有的屬性克隆一份一模一樣的給子類的公有。他是通過__proto__建立和子類之間的原型鏈,當子類的實例需要使用父類的屬性和方法的時候,可以通過__proto__一級級找上去使用。 

function Parent(){
  this.x = 199;
  this.y = 299;
}
Parent.prototype.say = function(){
  console.log('say')
}
function Child(){
  this.g = 90;
}
Child.prototype = new Parent();
var p = new Parent();
var c = new Child();
console.dir(c)

   實現的本質是重寫了原型對象 ,通過將子類的原型指向了父類的實例,所以子類的實例就可以通過__proto__訪問到 Child.prototype 也就是 Parent的實例,這樣就可以訪問到父類的私有方法。然后再通過__proto__指向父類的prototype就可以獲得到父類原型上的方法。

  這樣就做到了將父類的私有、公有方法和屬性都當做子類的公有屬性。這樣就通過原型鏈實現了繼承。

  但是別忘了默認的原型,因為所有引用類型都是繼承了Object的,所有說子類也可以訪問到Object上的方法如toString() 、valueOf() 等。

  結果如下圖所示:

JavaScript 常見的繼承方式匯總

  有的時候我們需要在子類中添加新的方法或者是重寫父類的方法時候,切記一定要放到替換原型的語句之后

function Parent(){
  this.x = 199;
  this.y = 299;
}
Parent.prototype.say = function(){
  console.log('say')
}
function Child(){
  this.g = 90;
}
/*Child.prototype.Bs = function(){
  console.log('Bs')
}*/在這里寫子類的原型方法和屬性是沒用的因為會改變原型的指向,所以應該放到重新指定之后
Child.prototype = new Parent();
Child.prototype.constructor=Child//由于重新修改了Child的原型導致默認原型上的constructor丟失,我們需要自己添加上,其實沒啥用,加不加都一樣
Child.prototype.Bs = function(){
  console.log('Bs')
}
Child.prototype.say = function(){
  console.log('之后改的')
}
var p = new Parent();
var c = new Child();
console.dir(c)
c.Bs() //Bs
c.say()  // 之后改的
p.say() //say 不影響父類實例訪問父類的方法

  存在的問題:

  1. 子類繼承父類的屬性和方法是將父類的私有屬性和公有方法都作為自己的公有屬性和方法,我們要清楚一件事情就是我們操作基本數據類型的時候操作的是值,在操作應用數據類型的時候操作的是地址,如果說父類的私有屬性中引用類型的屬性,那他被子類繼承的時候會作為公有屬性,這樣子類一操作這個屬性的時候,會影響到子類二。

  2. 在創建子類的實例時,不能向父類型的構造函數中傳遞參數。應該說是沒有辦法在不影響所有對象實例的情況下,給父類的構造函數傳遞參數。

  所以在實際中很少單獨使用原型繼承

二.call繼承

  改變方法的this指向,同時執行方法。 在子類構造函數中父類.call(this) 可以將父類的私有變成子類的私有。

function Parent() {
  this.x = 100;
  this.y = 199;
}
Parent.prototype.fn = function() {}
 
function Child() {
  this.d = 100;
  Parent.call(this); //構造函數中的this就是當前實例
}
var p = new Parent();
var c = new Child();
console.log(p) //Parent {x: 100, y: 199}
console.log(c) //Child {d: 100, x: 100, y: 199}

  在子類的構造函數中,改變父類的this指向,改變為子類的實例,同時運行父類方法,這樣父類中的this.x就變成了子類的實例.x ,通過這種方法就可以繼承了父類的私有屬性,且只能繼承父類的私有屬性和方法。

三.冒充對象繼承

  冒充對象繼承的原理是循環遍歷父類實例,然后父類實例的私有方法全部拿過來添加給子類實例。

function Parent(){
  this.x = 100;
}
Parent.prototype.getX = function(){
  console.log('getX')
}
function Child(){
  var p = new Parent();
  for(var attr in p){//for in 可以遍歷到原型上的公有自定義屬性
    this[attr] = p[attr]
  }
  //以下代碼是只獲得到私有方法和屬性,如果不加這個的話就可以遍歷到所有方法和屬性
  /*if(e.hasOwnProperty(attr)){
    this[attr] = e[attr]
  }
  e.propertyIsEnumerable()*///可枚舉屬性==> 可以拿出來一一列舉的屬性
}
var p = new Parent();
var c = new Child();
console.dir(c)

  for in 可以遍歷到原型上的公有自定義屬性 ,所以他可以拿到私有和公有的屬性和方法,這個你可以遍歷私有和公有的,需要你加限制條件。但是如果不做hasOwnProperty判斷那么就是把父類的公有的和私有的都拿過來當私有的。

四.混合繼承

  就是將call繼承和原型繼承集合在一起,無論是私有的還是公有的都拿過來了。但是有個問題就是子類的原型上的多了一套父類私有屬性,但是不會產生問題。因為子類的私有屬性也有一套相同的通過call繼承拿過來的。

function Parent(){
  this.x=100;
}
Parent.prototype.getX = function(){}
function Child(){
  Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var p = new Parent();
var c = new Child();
console.log(c)//Child {x: 100}

  存在的問題:

  無論在什么情況下,都會調用兩次構造函數:一次是在創建子類型原型的時候,另一次是在子類型構造函數的內部,沒錯,子類型最終會包含父類型對象的全部實例屬性,但我們不得不在調用子類構造函數時重寫這些屬性。

  還有一種就是call+拷貝繼承

//混合繼承:call繼承+拷貝繼承
  function extend(newEle,oldEle){
    for(var attr in oldEle){
      newEle[attr]=oldEle[attr];
    }
  }
  function F(){
    this.x=100;
    this.showX=function(){}
  }
  F.prototype.getX=function(){};
  F.prototype.getX1=function(){};
  var f1=new F;
  console.dir(f1)
  function S(){
    F.call(this)//call繼承
  }
  extend(S.prototype, F.prototype);//拷貝繼承
  S.prototype.cc=function(){ }
  var p1=new S;
  console.dir(p1);

  這種方式使用call繼承將父類的私有方法繼承過來,使用for in 拷貝將父類的公有屬性和方法繼承過來,比較實用。

五.中間件繼承

  中間件繼承就是通過原型鏈的機制,子類的prototype.__proto__本來應該是直接指向Object.prototype。

  從父類的原型上的__proto__也可以到Object.prototype,在父類.prototype上停留了下,父類.prototype就是一個中間件,所以子類可以繼承到父類的公有方法當做自己的公有方法。

function Parent(){
  this.x = 100;
}
Parent.prototype.getX = function(){}
function Child(){
  
}
Child.prototype.__proto__ = Parent.prototype;
var p = new Parent();
var c = new Child()
console.log(c)

六.寄生組合式繼承

   寄生式組合: call繼承+Object.create();

   所謂寄生組合式繼承就是通過借用構造函數來繼承屬性,通過原型鏈的混合形式來繼承方法

   基本思路是不必為了指定子類的原型而調用父類的構造函數,我們所需要的就是父類型原型的一個副本。

   本質上,就是使用寄生式繼承父類的原型,然后再將結果指定給子類的原型。

function F(){
  this.x=100;
}
F.prototype.showX=function(){};
function S(){
  this.y = 200
  F.call(this)//只繼承了私有的;
}
function inheritPrototype(subType,superType){
  var prototype = Object.create(superType.prototype);//創建對象
  prototype.constructor = subType;//增強對象
  subType.prototype = prototype;//指定對象
}
inheritPrototype(S,F)
var p1=new S;
console.dir(p1)

  1、第一步是創建父類型原型的一個副本。

  2、第二步是為創建的副本增加constructor屬性,從而彌補了因為重寫原型而失去的默認的constructor屬性。

  3、第三步是將創建的對象賦值給子類型的原型。

  這個例子的高效率體現在他只調用了一次SuperType 構造函數,并且因此避免了在SubType.prototype上面創建不必要的、多余的屬性。與此同時原型鏈還能保持不變,所以可以正常使用instanceof 和 isPrototypeOf() ,所以寄生組合繼承是引用類型最理想的繼承方法。

七.class繼承

  class 可以通過extends關鍵字實現繼承,這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多。

class Father{
  constructor(x, y) {
     this.x = x;
     this.y = y;
  }

  toString() {
     return '(' + this.x + ', ' + this.y + ')';
  }
}
class Son extends Father{
  constructor(x,y,color){
     super(x,y); // 調用父類的constructor(x, y)
     this.color = color;
  }
  toString() {
        console.log( super.toString()+this.color); // 調用父類的toString()
  }
}
let son = new Son(3,4,'red');
son.toString();//結果為(3,4)red

   上面代碼定義了一個Son類,該類通過extends關鍵字,繼承了Father類的所有屬性和方法。

  上面代碼中,constructor方法和toString方法之中,都出現了super關鍵字,它在這里表示父類的構造函數,用來新建父類的this對象

  子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類自己的this對象,必須先通過父類的構造函數完成塑造,得到與父類同樣的實例屬性和方法,然后再對其進行加工,加上子類自己的實例屬性和方法。如果不調用super方法,子類就得不到this對象。

以上就是JavaScript 常見的繼承方式匯總的詳細內容,更多關于JavaScript 繼承方式的資料請關注億速云其它相關文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平度市| 沂水县| 荥经县| 得荣县| 新平| 永年县| 北宁市| 兴隆县| 桐乡市| 临潭县| 全椒县| 塘沽区| 遵义县| 榆社县| 项城市| 南木林县| 黔江区| 彰武县| 绍兴县| 越西县| 潞西市| 麦盖提县| 阿鲁科尔沁旗| 当涂县| 竹溪县| 安岳县| 道孚县| 贺兰县| 沾益县| 千阳县| 天水市| 广灵县| 兴仁县| 青岛市| 汾阳市| 甘洛县| 武义县| 吉隆县| 汝阳县| 宁夏| 司法|