JavaScript继承

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

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

伪类

当我们构造一个函数对象时, 会默认生成一个constructor属性, 其值就是自身
var Person = function(name){    this.name = name;}Person.prototype.whoAmI = function(){    console.info(this.name);}    
js-inherit0.jpg构造一个伪类, 继承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 @nameclass 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;