亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript基礎(chǔ): 類與繼承

liuchengxu / 1598人閱讀

摘要:類的方法相當(dāng)于之前我們定義在構(gòu)造函數(shù)的原型上。的構(gòu)造函數(shù)中調(diào)用其目的就是調(diào)用父類的構(gòu)造函數(shù)。是先創(chuàng)建子類的實(shí)例,然后在子類實(shí)例的基礎(chǔ)上創(chuàng)建父類的屬性。

前言

  首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫(xiě)東西沒(méi)法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。
  
  許久已經(jīng)沒(méi)有寫(xiě)東西了,因?yàn)殡s七雜八的原因最近一直沒(méi)有抽出時(shí)間來(lái)把寫(xiě)作堅(jiān)持下來(lái),感覺(jué)和跑步一樣,一旦松懈下來(lái)就很難再次撿起來(lái)。最近一直想重新靜下心來(lái)寫(xiě)點(diǎn)什么,選題又成為一個(gè)讓我頭疼的問(wèn)題,最近工作中偶爾會(huì)對(duì)JavaScript繼承的問(wèn)題有時(shí)候會(huì)感覺(jué)恍惚,意識(shí)到很多知識(shí)即使是很基礎(chǔ),也需要經(jīng)常的回顧和練習(xí),否則即使再熟悉的東西也會(huì)經(jīng)常讓你感到陌生,所以就選擇這么一篇非常基礎(chǔ)的文章作為今年的開(kāi)始吧。
  

  JavaScript不像Java語(yǔ)言本身就具有類的概念,JavaScript作為一門基于原型(ProtoType)的語(yǔ)言,(推薦我之前寫(xiě)的我所認(rèn)識(shí)的JavaScript作用域鏈和原型鏈),時(shí)至今日,仍然有很多人不建議在JavaScript中大量使用面對(duì)對(duì)象的特性。但就目前而言,很多前端框架,例如React都有基于類的概念。首先明確一點(diǎn),類存在的目的就是為了生成對(duì)象,而在JavaScript生成對(duì)象的過(guò)程并不不像其他語(yǔ)言那么繁瑣,我們可以通過(guò)對(duì)象字面量語(yǔ)法輕松的創(chuàng)建一個(gè)對(duì)象:

var person = {
    name: "MrErHu", 
    sayName: function(){
        alert(this.name);
    }
};

  一切看起來(lái)是這樣的完美,但是當(dāng)我們希望創(chuàng)建無(wú)數(shù)個(gè)相似的對(duì)象時(shí),我們就會(huì)發(fā)現(xiàn)對(duì)象字面量的方法就不能滿足了,當(dāng)然聰明的你肯定會(huì)想到采用工廠模式去創(chuàng)建一系列的對(duì)象:
  

function createObject(name){
    return {
        "name": name,
        "sayName": function(){
            alert(this.name);
        }
    }
}

  但是這樣方式有一個(gè)顯著的問(wèn)題,我們通過(guò)工廠模式生成的各個(gè)對(duì)象之間并沒(méi)有聯(lián)系,沒(méi)法識(shí)別對(duì)象的類型,這時(shí)候就出現(xiàn)了構(gòu)造函數(shù)。在JavaScript中構(gòu)造函數(shù)和普通的函數(shù)沒(méi)有任何的區(qū)別,僅僅是構(gòu)造函數(shù)是通過(guò)new操作符調(diào)用的。
  

function Person(name, age, job){
    this.name = name;
    this.sayName = function(){
        alert(this.name);
    };    
}

var obj = new Person();
obj.sayName();

  我們知道new操作符會(huì)做以下四個(gè)步驟的操作:
  

創(chuàng)建一個(gè)全新的對(duì)象

新對(duì)象內(nèi)部屬性[[Prototype]](非正式屬性__proto__)連接到構(gòu)造函數(shù)的原型

構(gòu)造函數(shù)的this會(huì)綁定新的對(duì)象

如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象

  這樣我們通過(guò)構(gòu)造函數(shù)的方式生成的對(duì)象就可以進(jìn)行類型判斷。但是單純的構(gòu)造函數(shù)模式會(huì)存在一個(gè)問(wèn)題,就是每個(gè)對(duì)象的方法都是相互獨(dú)立的,而函數(shù)本質(zhì)上就是一種對(duì)象,因此就會(huì)造成大量的內(nèi)存浪費(fèi)?;仡?b>new操作符的第三個(gè)步驟,我們新生成對(duì)象的內(nèi)部屬性[[Prototype]]會(huì)連接到構(gòu)造函數(shù)的原型上,因此利用這個(gè)特性,我們可以混合構(gòu)造函數(shù)模式原型模式,解決上面的問(wèn)題。

function Person(name, age, job){
    this.name = name;
}

Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}

var obj = new Person();
obj.sayName();

  我們通過(guò)將sayName函數(shù)放到構(gòu)造函數(shù)的原型中,這樣生成的對(duì)象在使用sayName函數(shù)通過(guò)查找原型鏈就可以找到對(duì)應(yīng)的方法,所有對(duì)象共用一個(gè)方法就解決了上述問(wèn)題,即使你可能認(rèn)為原型鏈查找可能會(huì)耽誤一點(diǎn)時(shí)間,實(shí)際上對(duì)于現(xiàn)在的JavaScript引擎這種問(wèn)題可以忽略。對(duì)于構(gòu)造函數(shù)的原型修改,處理上述的方式,可能還存在:
  

Person.prototype.sayName = function(){
    alert(this.name);
}

  我們知道函數(shù)的原型中的constructor屬性是執(zhí)行函數(shù)本身,如果你是將原來(lái)的原型替換成新的對(duì)象并且constructor對(duì)你又比較重要記得手動(dòng)添加,因此第一種并不準(zhǔn)確,因?yàn)?b>constructor是不可枚舉的,因此更準(zhǔn)確的寫(xiě)法應(yīng)該是:

Object.defineProperty(Person, "constructor", {
    configurable: false,
    enumerable: false,
    writable: true,
    value: Person
});

  到現(xiàn)在為止,我們會(huì)覺(jué)得在JavaScript中創(chuàng)建個(gè)類也太麻煩了,其實(shí)遠(yuǎn)遠(yuǎn)不止如此,比如我們創(chuàng)建的類可能會(huì)被直接調(diào)用,造成全局環(huán)境的污染,比如:
  

Person("MrErHu");
console.log(window.name); //MrErHu

  不過(guò)我們迎來(lái)了ES6的時(shí)代,事情正在其變化,ES6為我們?cè)贘avaScript中實(shí)現(xiàn)了類的概念,上面的的代碼都可以用簡(jiǎn)介的類(class)實(shí)現(xiàn)。
  

class Person {
    constructor(name){
        this.name = name;
    }
    
    sayName(){
        alert(this.name);
    }
}

  通過(guò)上面我們就定義了一個(gè)類,使用的時(shí)候同之前一樣:
  

let person = new Person("MrErHu");
person.sayName(); //MrErHu

  我們可以看到,類中的constructor函數(shù)負(fù)擔(dān)起了之前的構(gòu)造函數(shù)的功能,類中的實(shí)例屬性都可以在這里初始化。類的方法sayName相當(dāng)于之前我們定義在構(gòu)造函數(shù)的原型上。其實(shí)在ES6中類僅僅只是函數(shù)的語(yǔ)法糖:
  

typeof Person  //"function"

  相比于上面自己創(chuàng)建的類方式,ES6中的類有幾個(gè)方面是與我們自定義的類不相同的。首先類是不存在變量提升的,因此不能先使用后定義:
  

let person = new Person("MrErHu")
class Person { //...... } 

  上面的使用方式是錯(cuò)誤的。因此類更像一個(gè)函數(shù)表達(dá)式。
  
  其次,類聲明中的所有代碼都是自動(dòng)運(yùn)行在嚴(yán)格模式下,并且不能讓類脫離嚴(yán)格模式。相當(dāng)于類聲明中的所有代碼都運(yùn)行在"use strict"中。
  
  再者,類中的所有方法都是都是不可枚舉的。
  
  最后,類是不能直接調(diào)用的,必須通過(guò)new操作符調(diào)用。其實(shí)對(duì)于函數(shù)有內(nèi)部屬性[[Constructor]][[Call]],當(dāng)然這兩個(gè)方法我們?cè)谕獠渴菦](méi)法訪問(wèn)到的,僅存在于JavaScript引擎。當(dāng)我們直接調(diào)用函數(shù)時(shí),其實(shí)就是調(diào)用了內(nèi)部屬性[[Call]],所做的就是直接執(zhí)行了函數(shù)體。當(dāng)我們通過(guò)new操作符調(diào)用時(shí),其實(shí)就是調(diào)用了內(nèi)部屬性[[Constructor]],所做的就是創(chuàng)建新的實(shí)例對(duì)象,并在實(shí)例對(duì)象上執(zhí)行函數(shù)(綁定this),最后返回新的實(shí)例對(duì)象。因?yàn)轭愔胁缓袃?nèi)部屬性[[Call]],因此是沒(méi)法直接調(diào)用的。順便可以提一句ES6中的元屬性 new.target
  
  所謂的元屬性指的就是非對(duì)象的屬性,可以提供給我們一些補(bǔ)充信息。new.target就是其中一個(gè)元屬性,當(dāng)調(diào)用的是[[Constructor]]屬性時(shí),new.target就是new操作符的目標(biāo),如果調(diào)用的是[[Call]]屬性,new.target就是undefined。其實(shí)這個(gè)屬性是非常有用的,比如我們可以定義一個(gè)僅可以通過(guò)new操作符調(diào)用的函數(shù):

function Person(){
    if(new.target === undefined){
        throw("該函數(shù)必須通過(guò)new操作符調(diào)用");
    }
}

  或者我們可以用JavaScript創(chuàng)建一個(gè)類似于C++中的虛函數(shù)的函數(shù):

class Person {
  constructor() {
    if (new.target === Person) {
      throw new Error("本類不能實(shí)例化");
    }
  }
}

  

繼承

  在沒(méi)有ES6的時(shí)代,想要實(shí)現(xiàn)繼承是一個(gè)不小的工作。一方面我們要在派生類中創(chuàng)建父類的屬性,另一方面我們需要繼承父類的方法,例如下面的實(shí)現(xiàn)方法:
  

function Rectangle(width, height){
  this.width = width;
  this.height = height;
}

Rectangle.prototype.getArea = function(){
  return this.width * this.height;
}

function Square(length){
  Rectangle.call(this, length, length);
}

Square.prototype = Object.create(Rectangle.prototype, {
  constructor: {
    value: Square,
    enumerable: false,
    writable: false,
    configurable: false
  }
});

var square = new Square(3);

console.log(square.getArea());
console.log(square instanceof Square);
console.log(square instanceof Rectangle);

  首先子類Square為了創(chuàng)建父類Rectangle的屬性,我們?cè)?b>Square函數(shù)中以Rectangle.call(this, length, length)的方式進(jìn)行了調(diào)用,其目的就是在子類中創(chuàng)建父類的屬性,為了繼承父類的方法,我們給Square賦值了新的原型。除了通過(guò)Object.create方式,你應(yīng)該也見(jiàn)過(guò)以下方式:
  

Square.prototype = new Rectangle();
Object.defineProperty(Square.prototype, "constructor", {
    value: Square,
    enumerable: false,
    writable: false,
    configurable: false
});

  Object.create是ES5新增的方法,用于創(chuàng)建一個(gè)新對(duì)象。被創(chuàng)建的對(duì)象會(huì)繼承另一個(gè)對(duì)象的原型,在創(chuàng)建新對(duì)象時(shí)還可以指定一些屬性。Object.create指定屬性的方式與Object.defineProperty相同,都是采用屬性描述符的方式。因此可以看出,通過(guò)Object.createnew方式實(shí)現(xiàn)的繼承其本質(zhì)上并沒(méi)有什么區(qū)別。
  
  但是ES6可以大大簡(jiǎn)化繼承的步驟:

class Rectangle{
    constructor(width, height){
        this.width = width;
        this.height = height;
    }
    
    getArea(){
        return this.width * this.height;
    }
}

class Square extends Rectangle{
    construct(length){
        super(length, length);
    }
}

  我們可以看到通過(guò)ES6的方式實(shí)現(xiàn)類的繼承是非常容易的。Square的構(gòu)造函數(shù)中調(diào)用super其目的就是調(diào)用父類的構(gòu)造函數(shù)。當(dāng)然調(diào)用super函數(shù)并不是必須的,如果你默認(rèn)缺省了構(gòu)造函數(shù),則會(huì)自動(dòng)調(diào)用super函數(shù),并傳入所有的參數(shù)。
  
  不僅如此,ES6的類繼承賦予了更多新的特性,首先extends可以繼承任何類型的表達(dá)式,只要該表達(dá)式最終返回的是一個(gè)可繼承的函數(shù)(也就是講extends可以繼承具有[[Constructor]]的內(nèi)部屬性的函數(shù),比如null和生成器函數(shù)、箭頭函數(shù)都不具有該屬性,因此不可以被繼承)。比如:

class A{}
class B{}

function getParentClass(type){
    if(//...){
        return A;
    }
    if(//...){
        return B;
    }
}

class C extends getParentClass(//...){
}

  可以看到我們通過(guò)上面的代碼實(shí)現(xiàn)了動(dòng)態(tài)繼承,可以根據(jù)不同的判斷條件繼承不同的類。
  
  ES6的繼承與ES5實(shí)現(xiàn)的類繼承,還有一點(diǎn)不同。ES5是先創(chuàng)建子類的實(shí)例,然后在子類實(shí)例的基礎(chǔ)上創(chuàng)建父類的屬性。而ES6正好是相反的,是先創(chuàng)建父類的實(shí)例,然后在父類實(shí)例的基礎(chǔ)上擴(kuò)展子類屬性。利用這個(gè)屬性我們可以做到一些ES5無(wú)法實(shí)現(xiàn)的功能:繼承原生對(duì)象。

function MyArray() {
  Array.apply(this, arguments);
}

MyArray.prototype = Object.create(Array.prototype, {
  constructor: {
    value: MyArray,
    writable: true,
    configurable: true,
    enumerable: true
  }
});

var colors = new MyArray();
colors[0] = "red";
colors.length  // 0

colors.length = 0;
colors[0]  // "red"

  可以看到,繼承自原生對(duì)象ArrayMyArray的實(shí)例中的length并不能如同原生Array類的實(shí)例
一樣可以動(dòng)態(tài)反應(yīng)數(shù)組中元素?cái)?shù)量或者通過(guò)改變length屬性從而改變數(shù)組中的數(shù)據(jù)。究其原因就是因?yàn)閭鹘y(tǒng)方式實(shí)現(xiàn)的數(shù)組繼承是先創(chuàng)建子類,然后在子類基礎(chǔ)上擴(kuò)展父類的屬性和方法,所以并沒(méi)有繼承的相關(guān)方法,但ES6卻可以輕松實(shí)現(xiàn)這一點(diǎn):

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}

var arr = new MyArray();
arr[0] = 12;
arr.length // 1

arr.length = 0;
arr[0] // undefined

  我們可以看見(jiàn)通過(guò)extends實(shí)現(xiàn)的MyArray類創(chuàng)建的數(shù)組就可以同原生數(shù)組一樣,使用length屬性反應(yīng)數(shù)組變化和改變數(shù)組元素。不僅如此,在ES6中,我們可以使用Symbol.species屬性使得當(dāng)我們繼承原生對(duì)象時(shí),改變繼承自原生對(duì)象的方法的返回實(shí)例類型。例如,Array.prototype.slice本來(lái)返回的是Array類型的實(shí)例,通過(guò)設(shè)置Symbol.species屬性,我們可以讓其返回自定義的對(duì)象類型:
  

class MyArray extends Array {
  static get [Symbol.species](){
    return MyArray;
  }
    
  constructor(...args) {
    super(...args);
  }
}

let items = new MyArray(1,2,3,4);
subitems = items.slice(1,3);

subitems instanceof MyArray; // true

  最后需要注意的一點(diǎn),extends實(shí)現(xiàn)的繼承方式可以繼承父類的靜態(tài)成員函數(shù),例如:
  

class Rectangle{
    // ......
    static create(width, height){
        return new Rectangle(width, height);
    }
}

class Square extends Rectangle{
    //......
}

let rect = Square.create(3,4);
rect instanceof Square; // true

  
  

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/93677.html

相關(guān)文章

  • javascript類與繼承

    摘要:的類與繼承的類與一般的面向?qū)ο笳Z(yǔ)言有很大的不同,類的標(biāo)識(shí)是它的構(gòu)造函數(shù),下面先定義一個(gè)類顯然我們可以看出這兩個(gè)函數(shù)是不同的,雖然它們實(shí)現(xiàn)了相同的功能。利用構(gòu)造函數(shù)來(lái)繼承上面的方法子類顯然無(wú)法繼承父類的原型函數(shù),這樣不符合我們使用繼承的目的。 javascript的類與繼承 javascript的類與一般的面向?qū)ο笳Z(yǔ)言有很大的不同,類的標(biāo)識(shí)是它的構(gòu)造函數(shù),下面先定義一個(gè)類 var ...

    impig33 評(píng)論0 收藏0
  • JavaScript實(shí)現(xiàn)類與繼承的方法(全面整理)

    摘要:定義類的種方法工廠方法構(gòu)造函數(shù)方法原型方法大家可以看到這種方法有缺陷,類里屬性的值都是在原型里給定的。組合使用構(gòu)造函數(shù)和原型方法使用最廣將構(gòu)造函數(shù)方法和原型方法結(jié)合使用是目前最常用的定義類的方法。 JavaScript定義類的4種方法 工廠方法 function creatPerson(name, age) { var obj = new Object...

    Near_Li 評(píng)論0 收藏0
  • javascript---類與函數(shù)化

    摘要:函數(shù)用于指定對(duì)象的行為。關(guān)于屬性只在構(gòu)造器函數(shù)的原型上才有的屬性并指向該構(gòu)造器,改寫(xiě)了的原型對(duì)象默認(rèn)是沒(méi)有屬性的。函數(shù)化工廠模式在偽類模式里,構(gòu)造器函數(shù)不得不重復(fù)構(gòu)造器已經(jīng)完成的工作。 1.對(duì)象適合于收集和管理數(shù)據(jù),容易形成樹(shù)型結(jié)構(gòu)。Javascript包括一個(gè)原型鏈特性,允許對(duì)象繼承另一對(duì)象的屬性。正確的使用它能減少對(duì)象的初始化時(shí)間和內(nèi)存消耗。2.函數(shù)它們是javascript的基礎(chǔ)...

    waruqi 評(píng)論0 收藏0
  • TS 類與接口

    摘要:二正文中與的區(qū)別接口只聲明成員方法,不做實(shí)現(xiàn)。在中,我們可以采用全新的基于類繼承的模式設(shè)計(jì)更優(yōu)雅的語(yǔ)義化接口,這是因?yàn)橹械目梢岳^承動(dòng)態(tài)構(gòu)造的類,這一點(diǎn)和其他的靜態(tài)聲明類的編程語(yǔ)言不同。 一 前言 1.在typescript上遇到過(guò)實(shí)例化對(duì)象的兩種寫(xiě)法:implement和extends。extends很明顯就是ES6里面的類繼承,那么implement又是做什么的呢?它和extends...

    garfileo 評(píng)論0 收藏0
  • Python方法(二) - 類與繼承

    摘要:在類內(nèi)部的方法中使用時(shí)。類的私有方法兩個(gè)下劃線開(kāi)頭,聲明該方法為私有方法,不能在類地外部調(diào)用。先在本類中查找調(diào)用的方法,找不到才去基類中找。如果在繼承元組中列了一個(gè)以上的類,那么它就被稱作多重繼承。 類定義 類對(duì)象:創(chuàng)建一個(gè)類之后,可以通過(guò)類名訪問(wèn)、改變其屬性、方法 實(shí)例對(duì)象:類實(shí)例化后,可以使用其屬性,可以動(dòng)態(tài)的為實(shí)例對(duì)象添加屬性(類似javascript)而不影響類對(duì)象。 類...

    DevWiki 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<