ES6建立類的基本語法和繼承實現原理

ReneeTsang發表於2018-03-22

整理了在學習ES6關於類的筆記,從基礎使用語法到繼承的實現原理。

基本使用

ES5中建立類的例項,以及如何禁止使用者把類當作普通函式執行( new target

function Person(name,age){
    //console.log(new.target); //ES6增加的語法,如果是通過new執行的,返回的結果是當前建立的類;如果是當作普通函執行(Person()),返回的是undefined
    if(typeof new.target === 'undefined'){
        throw new SyntaxError('當前Person不能作為一個普通函式執行')
    }
        
    //new執行的時候,this是當前類的例項,this.xxx=xxx是給當前例項增加的私有屬性
    this.name=name;
    this.age=age;
}
​
//原型上存放的是公有的屬性和方法:給建立的例項使用
Person.prototype={
    constructor:Person,
    say:function(){
        console.log(`my name is ${this.name},i am ${this.age} years old`)
    }
}
​
//把Person當作一個普通的物件,給物件設定的私有屬性
Person.study=function(){
    console.log('good good study day day up')
}
var p1=new Person('renee',18)複製程式碼

ES6中建立類

class的內部是通過Object.definePropterty來定義的,把公共方法定義在原型鏈上。把靜態方法定義再類上

console.log(Person); //報錯 不存在變數提示
class Person{
    constructor(name='無名氏',age=18){
        //給例項設定的私有屬性
        this.name=name;
        this.age=age;
    }
    
    //直接在大括號中編寫的方法都設定在類的原型上,ES6預設把constructor的問題解決了,此時原型上的constructor指向的就是Person
    say(){
        console.log(`my name is ${this.name},i am ${this.age} years old`)
    }
    
    //把Person當作普通物件設定屬性和方法,只需要在設定的方法前加static即可
    static study(){
        console.log('good good study day day up')
    }
}
let p1=new Person('renee');
//Person(); //報錯 ES6中使用class建立的類,天生自帶new.target的驗證,不允許把建立的類當作普通函式執行複製程式碼

類的繼承

Object.create

function Parent(){
    this.name = 'parent'
}
Parent.prototype.smoking = function(){
    console.log('smoking')
}
function Child(){}
​
// 繼承公有屬性 Object.create是如何實現的
function create(Pproto){
    let Fn = function(){} // 建立一個空函式 沒有私有屬性和公用屬性
    Fn.prototype = Pproto; // 將父類的公有屬性放在這個函式上
    return new Fn(); // 產生的例項就只有公有屬性了
}
Child.prototype =Object.create(Parent.prototype,{constructor:{value:Child}});
let child = new Child();
console.log(child.__proto__=== Child.prototype);
console.log(Child.prototype.constructor == Child);複製程式碼

extends繼承

class Person{
    constructor(...arg){
        let [x=0,y=0]=arg;
        this.x=x;
        this.y=y;
    }
    sum(){
        return this.x+this.y;
    }
}
​
class Child extends Person{ 
    //建立了Child類,並且讓Child類繼承了Person類
    //1.把Person中的私有屬性繼承過來設定給了子類例項的私有屬性
    //2.讓子類例項的原型鏈上能夠找到Person父類的原型(這樣子類的例項就可以呼叫父類原型上的方法了)
    
    //constructor(){}//報錯
    //----------------
    //我們可以不寫constructor,瀏覽器會預設建立它,而且預設就把父類私有的屬性繼承過來了(而且把傳給子類的引數值也傳遞給父類了)
    //constructor(...arg){
        //arg:傳遞給子類的引數(陣列),[剩餘運算子]
        //super(...arg) //[展開運算子] 把arg中每一項值展開,分別傳遞給父類方法super(10,20,30) 
    //} 
    //----------------
    //很多時候我們不僅要繼承父類私有的,還需要給子類增加一些額外私有的,此時就必須寫constructor,但是一定要在constructor中第一行寫上super,否則會報錯
    constructor(...arg){
        super(...arg) //super must be first
        let [,,z]=arg;
        this.z=z
    } 
    
    //constructor(x,y,z){
    //    super() //Person.prototype.constructor.call(this)
    //    this.z=z;
    //} 
    
    fn(){
        
    }
}
let c=new Child(10,20,30)
​複製程式碼

ES6繼承的實現原理

// 負責將原型的方法和靜態方法定義在建構函式上的
function defineProperties(constructor,properties){
    for(let i = 0;i<properties.length;i++){
        let obj = {...properties[i],enumerable:true,writeable:true,configurable:true}
        Object.defineProperty(constructor,properties[i].key,obj);
    }
}
//  對不同的屬性做處理 如果是原型上的方法掛載Class.prototype 如果是靜態方法放在 Class上
function _createClass(con,protoProperty,staticProperty){
    if(protoProperty){
        defineProperties(con.prototype,protoProperty);
    }
    if(staticProperty){
        defineProperties(con,staticProperty);
    }
}
// 父類
var Parent = function(){
    function Parent(){
        // 類的呼叫檢測
        this.name = 'zfpx'
        _classCallCheck(this,Parent);
        return {a:100}
    }
    // 用來描述這個類的原型方法和靜態方法
    _createClass(Parent,[ // 第一個陣列表示的是公共方法的描述 descirptor
        {key:'getName',value:function(){
            return 1000
        }}
    ],[ //描述靜態的方法
        {key:'fn',value:function(){
            return 100;
        }}
    ])
   return Parent
}()
// 類的呼叫檢測
function _classCallCheck(instance,constructor){ //檢查當前類  有沒有new出來的,不是new出來的this屬於window
    if(!(instance instanceof constructor)) throw Error('without new')
}
// 繼承共有方法和靜態方法
function _inherits(subClass,superClass){
    // 子類繼承父類的公有方法
    subClass.prototype = Object.create(superClass.prototype,{constructor:{value:subClass}});
    // 也要讓子類繼承父類的靜態方法
    subClass.__proto__ = superClass;
}
var Child = function(Parent){ // 表示兒子繼承Parent類,要包多一層不然傳參會傳到Child上
    _inherits(Child,Parent); // 表示繼承 兒子繼承父親
    function Child(){ // 類的呼叫檢查
        // 在子類中應該呼叫父類的建構函式
        // Parent.call(this);
        _classCallCheck(this,Child);
        let that = this;
        let obj =  Object.getPrototypeOf(Child).call(this); // Child.__proto__ = Object.getPrototypeOf(Child) 繼承父類的私有方法,為了保險不用Parent.call(this),因為不一定繼承父類
        if(typeof obj === 'object'){ //如果是物件把obj作為例項
            that = obj;
        }
        return that;
    }
    return Child
}(Parent);
let child = new Child;
console.log(child);複製程式碼


相關文章