《javascript语言精粹》读书笔记——第5章:继承

javascript是一门弱类型语言,从不需要类型转换。对象继承关系变得无关紧要。对于一个对象来说重要的是它能做什么,而不是它从哪里来。javascript是一门基于原型的语言,这意味着对象直接从其他对象继承。

1、伪类

javascript不直接让对象从其他对象继承,反而是插入了一个中间层:通过构造器函数产生对象。

当一个函数被创建时,Function 构造器产生的函数对象会运行类似这样的一些代码:

this.prototype = {constructor : this};
新创建的函数对象被赋予了一个prototype属性。对于普通函数,这个属性没有任何作用。因为javascript没有提供一种方法去确定那个函数是用来打算做构造器的,所以给每一个函数对象都赋予了这样一个prototype属性。对于作为构造器的函数对象来说,这个prototype属性是用来存放继承特征的地方。

 

函数作为构造器去构造新对象时,就是采用构造器调用模式去调用函数 ,也就是使用new 前缀去调用一个函数。此时的函数就相当于构造对象的一个类,当然这不是一个真正意义上的类,只是javascript为了刻意模拟面向对象系统而构造的一种语法。所以构造器函数是一个伪类。

现在我们来实现一个new 方法来替代 new 操作符,通过这个new 方法我们来看看到底new 操作符做了哪些操作:

 

Function.prototype.method = function(name, fun) {
 this.prototype[name]= fun;
};


Object.create = function(pro){
 var F = function(){};
 F.prototype = o;
 return new F();
};


Function.method('new', function(){
    var that = Object.create(this.prototype);
    var other = this.apply(that, arguments);
    return (other && typeof other === 'object') || that;
});

上面例子首先给Function的原型添加一个method方法,这个方法被所有的函数对象继承(Function自己也是函数对象),用于给自己的原型添加某个方法。这个用这个函数构造出来的新对象将会共享(继承)这个方法。

然后又给Object添加了一个create方法,这个方法用于创建一个新的对象,并以参数pro 作为构造函数的原型。这个新创建的对象将会共享(继承)pro的所有属性。

最后,我们给Function原型添加了一个new方法,这个方法模拟了new操作符在构建新对象时所作的操作。让我们看一个例子:

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

var p = Person.new("clopopo",12);

p.name // "clopopo"

p.age // 12

 2、对象说明符

编写构造器函数时,参数用一个对象说明符来代替一大串参数

//使用一大串参数来构造对象
var myObject = make(f, l, m, c, s);

//使用对象说明符来代替一大串参数
myObject = make({
 first  : f,
 middle : m,
 last   : l,
 state  : s,
 city   : c
});

相当于把参数组织成了类似json对象的东西。优点是不需要记住参数的顺序,另一个就是可以直接传入json对象,来生成真正的对象实例。

3、原型

基于原型的继承比基于类的继承模型在概念上要更简单:一个新对象可以继承一个旧对象的属性。利用上面Object的create方法,我们完全可以避免使用new这样令人迷惑的模拟类的继承方式(本质上还是基于原型)。

首先,利用对象字面量来定义一个有用的对象:

var myMammal = {
    name : 'Herb the Mammal',
    get_name : function() {
        return this.name;
    },
    says : function(){
        return this.saying || '';
    }
}

一旦有一个想要的对象,我们就可以利用Object的create方法(上文自己定义的)基于这个对象定制新的对象。

var myCat = Object.create(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.getName = function(){
    return this.says + ' ' + this.name + ' ' + this.says;
};

这是一种差异化继承,myCat查找属性时,如果自身存在就调用自身的属性,否则就去查找myCat连接的原型对象。

4、函数化

上文的几种继承方式有一个弱点就是无法保护隐私,就是我们无法得到私有函数和私有变量。有一种非常不可取的方法就是被称为“伪装私有”模式。就是给私有方法或变量去一个怪模怪样的名字,并希望使用代码的用户假装看不到这些变量,这是典型的自欺欺人啊。幸亏javascript的闭包特性给我们提供了一种叫做应用模块的模式。

var mammal = function(spec){
    var that = {};
    that.get_name = function(){
         return spec.name;
   };
   that.says = function(){
       return spec.saying || ' ';
   };
   return that;
}

var myMammal = mammal({name: 'Herb'});

再来看看原型继承

var cat = function(spec){
    spec.saying = spec.saying || '';
    var that = mammal(spec);
    that.get_name = function(){
        return that.says() + ' ' + spec.name; 
    }
    return that;
}
var myCat = cat('kitty');

函数化模式还给我们提供了一个处理父类方法的方法。用闭包封存了调用superior方法对象的this和当时name属性对应的方法。

Object.method('superior', function(name){
    var that   = this,
        method = that[name];
    return function(){
        return method.apply(that, arguments);
    }; 
});

先看看这个方法的用法:

var coolcat = function(spec){
    var that = cat(spec),
        super_get_name = that.superior('get_name');
    
    that.get_name = function(n){
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

super_get_name 得到的是cat对象创建时的get_name方法,根据superior方法定义知道,该方法永远都会以调用superior时的那个对象为上下文,上例中就是cat对象。所以调用super_get_name()时就是以cat为上下文,cat创建时的get_name方法为方法体。

转载于:https://my.oschina.net/clopopo/blog/120733

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值