JavaScript继承
2015 年 02 月 10 日
web

    一直对JS的继承概念有点模糊, 大体知道是怎么回事, 但也没有去深究过, 今天翻阅【JavaScript语言精髓】一书的第五章比较深刻进行认知, 特此记录一番。

    JavaScript是一门基于原型的语言, 其对象是直接从其他对象继承的

  • 伪类

  • 当我们构造一个函数对象时, 会默认生成一个constructor属性, 其值就是自身
  • var Person = function(name){
        this.name = name;
    }
    Person.prototype.whoAmI = function(){
        console.info(this.name);
    }
        
  • 构造一个伪类, 继承Person
  • var Student = function(grade){
        this.grade = grade;
    }
    Student.prototype = new Person('haolin');
        
  • 但这些伪类操作会让人觉得怪异, 我们需要隐藏这些prototype, 这是书中使用的通用代码片段
  • Function.prototype.method = function(name, func){
        this.prototype[name] = func;
        return this;
    }
        

    上述代码片段, 为Function.prototype增加一个method方法, 这样我们就可以通过String.method("newMethod", function(){...}) (而不是String.prototype.newMethod = function(){...})来为String扩展新方法了。

  • 于是我们也可以扩展一个方法, 用于继承:
  • Function.method("extends", function(Parent){
        this.prototype = new Parent;
        return this;
    });
        
  • 那么可以通过这样的来进行继承
  • var Student = function(grade){
        this.grade = grade;
    }.extends(Person);
        

    现在我们基本达到了继承的目的, 但是也存在一些问题, 比如父类属性能公开访问, 在继承时需要加上new关键字等。

  • 函数化构造器

  • 我们将不使用new关键字, 进行对象构造, 基本分为四个步骤
  • var constructor = function(spec, my){
        var that, 其他私有实例变量
        my = my || {};
    
        把共享的变量和函数添加到my中
    
        that = 一个新对象
    
        添加给that访问私有属性的方法
    
        return that;
    }
        
  • 按照这个原则, 我们试着重新构造之前的继承关系:
  • var person = function(context){
        var that = {};
    
        var whoAmI = function(){
            return context.name;
        };
        that.whoAmI = whoAmI;
    
        var howOld = function(){
            return context.age || 0;
        };
        that.howOld = howOld;
    
        return that;
    }
    
    var p2 = person({"name" : "haolin", "age" : 24});
        
  • 之前通过伪类进行继承, 我们需要重复构造器Person已经完成的工作, 而通过函数化构造则不用关心父类构造, 只需调用父类构造即可
  • var student = function(context){
    
        var number = context.number || "";
        // 调用父类构造方法, 并能访问父类暴露的接口whoAmI, howOld
        var that = person(context);
    
        // 子类扩展方法
        var getNumber = function(){
            return number;
        }
        that.getNumber = getNumber;
    
        var selfIntroduce = function(){
            return "I am " + that.whoAmI() + ", I'm " + that.howOld() + " years old, My number is " + number;
        }
        that.selfIntroduce = selfIntroduce;
    
        return that;
    }
    
    var s2 = student({"name" : "haolin", "age" : 24, "number" : "123456"});
        
  • 现在, 我们就能直接调用父类方法, 并且防止了父类私有属性被直接访问。
  • CoffeeScript中的类继承

  • 现在的前端项目都使用CoffeeScript, 其支持类和继承, 想看看它是怎么理解JS的类和继承的。

    class Person
        constructor: (@name, @age) ->
    
    getName: ->
        console.info @name
    
    class Student extends Person
        constructor: (@number) ->
    
    getNumber: ->
        console.info @number
    
        
  • 编译后的JS为
  • var Person, Student, __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) {   //扩展函数
        for (var key in parent) {
        if (__hasProp.call(parent, key))    //赋值父类属性
            child[key] = parent[key];
        }
        function ctor() {                   //子类构造函数
            this.constructor = child;
        }
        ctor.prototype = parent.prototype;
        child.prototype = new ctor();
        child.__super__ = parent.prototype; //父类标识
        return child;
    };
    
    Person = (function() {                  //使用闭包, 保护属性
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
    
        Person.prototype.getName = function() {
            return console.info(this.name);
        };
    
        return Person;
    
    })();   //发起一次调用
    
    Student = (function(_super) {
        __extends(Student, _super);
    
        function Student(number) {
            this.number = number;
        }
    
        Student.prototype.getNumber = function() {
            return console.info(this.number);
        };
    
        return Student;
    
    })(Person);
    
        
好人,一生平安。