JavaScript原型面向对象构造

1 封装
先使用构造函数声明一个类,在构造函数中给this添加本地属性,并实例化一个对象,这种方式可以为对象声明一个公共的本地属性:
function Animal(name) {
     this.name = name;
     this.sleep = function() {
          alert(this.name + ' sleep');
     };
}
var a1 = new Animal('不高兴');
a1.sleep();
Note:类名为Animal,使用大写字母开头,是编程的一种命名约定。
使用prototype也可以实现:
function Animal(name) {
     this.name = name;
}
Animal.prototype.sleep = function() {
     alert(this.name + ' sleep');
};
var a1 = new Animal("不高兴");
a1.sleep();
但是,两种声明公共属性/方法的方式是有区别的,使用hasOwnProperty()方法可以用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性,后面详细说明本地属性与prototype属性的关系。
在执行构造函数和a1对象方法调用时,this变量会绑定到a1对象,在这里就不具体说明this了。
私有属性有一种命名约定以下划线(_)作为开头,一般在看到这种命名约定时,就应当想到,这是对象的一个私有属性,不应该随意修改,如:
function Animal(name) {
     this._isMammal = true;
     this.name = name;
}
Animal.prototype._frenzy = function() {
};
3.2继承
继承分为接口继承与实现继承,因为JavaScript没有接口(interface)的概念,所以无法实现接口继承。
看下面一段代码:
function Aniaml() {
     alert('Animal init');
}
Animal.prototype.sleep = function() {
     alert('Animal sleep');
};
var a1 = new Animal();  //alert Animal init
a1.sleep();  //alert Animal sleep
上面这段代码声明了一个Animal类,我现在想声明一个Cat类并继承它,该如何做呢?
function Cat() {
     alert('Cat init');
}
Cat.prototype = Animal.prototype;
Cat.prototype.sleep = function() {
     alert('Cat sleep');
};
var c2 = new Cat();  //alert Cat init
c2.sleep();  //alert Cat sleep
a1.sleep();  //alert Cat sleep,这时候a1也输出了Cat sleep
上面这么写很显然是有问题的,这么写会使Cat.prototype与Animal.prototype引用相同对象,修改Cat.prototype的属性值会影响到Animal.prototype。那么,换一种写法:
function Cat() {
     alert('Cat init');
}
Cat.prototype = new Animal();
似乎也有问题,在给Cat.prototype属性赋值时,会实例化Animal。我们只是希望继承Animal.prototype,并不希望执行Animal的构造函数。这时候,我们可以利用一个空对象作为中介,实现对Animal的原型继承:
function Cat() {
     alert('Cat init');
}
function Empty() {}
Empty.prototype = Animal.prototype;
Cat.prototype = new Empty();
Cat.prototype.constructor = Cat;
Cat.prototype.sleep = function() {
     alert('Cat sleep');
};
var c2 = new Cat(); // alert Cat init
c2.sleep();  //alert Cat sleep
a1.sleep();  //alert Animal sleep
总算一切正常,现在我们来重新组织下Animal与Cat的原型链:
将继承的方法封装成一个公共函数:
var TemplateClass = function() {};
function chain(object) {
     TemplateClass.prototype = object;
     var result = new TemplateClass();
     TemplateClass.prototype = null;
     return result;
}
写一个完整的原型继承例子,这个例子将会描述如何调用父类(super class)的方式
function Animal(name, color) {
     this.name = name;
     this.color = color;
}
Animal.prototype.sleep = function() {
     alert(this.name + ' sleep');
};
var a1 = new Animal('倒霉熊', 'white');
a1.sleep();  //倒霉熊 sleep
function Cat() {
      //通过调用父类的构造函数实现初始化
     Animal.apply(this, arguments);
}
Cat.prototype = chain(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.greenEye = true;
Cat.prototype.mew = function() {
     alert(this.name + ' mew');
};
var c2 = new Cat('没头脑', 'red');
c2.mew();  //没头脑 mew
c2.sleep();  //没头脑 sleep
alert(c2.greenEye); //true
function PersianCat() {
     Cat.apply(this, arguments);
}
PersianCat.prototype = chain(Cat.prototype);
PersianCat.prototype.constructor = PersianCat;
PersianCat.prototype.name = 'persian cat'; //在原型中声明name属性
PersianCat.prototype.blueEye = true;
PersianCat.prototype.mew = function() {
     //重写方法并不一定要完全覆写,也可以调用父类方法,执行父类细节之后实现更多细节
     Cat.prototype.mew.call(this);
     alert(this.name + ' miaow');
};
var p3 = new PersianCat('不高兴', 'yellow');
p3.mew(); // 不高兴mew, 不高兴miaow
p3.sleep();  //不高兴 sleep
alert(p3.greenEye);  //true
alert(p3.blueEye);  //true
alert(p3.__proto__.name);  //输出persian cat, 本地name属性赋值之后,并不会覆盖prototype中name属性的值

下面通过一个更详细的原型链图,来描述这个例子中本地属性与prototype属性之间的关系:
通过这个图,大家应该也看明白,a1、c2、p3中的是本地属性,其他的都是prototype属性,从例子的运行结果可以知道,对本地属性赋值,并不会覆盖prototype属性。在使用this访问对象的属性或方法时,是先从本地属性中查找,如果未到,那么它会向上遍历原型链,直到找到给定名称的属性为止,当到达原型链的顶部(也就是Object.prototype)仍然没有找到指定的属性,就会返回undefined。
chain()函数也可以使用Object.create()函数替代,可以简单的理解成Object.create()完成的工作与chain()一样。这样可以对上面例子的代码再优化,将类继承封装成一个独立函数:
var TemplateClass = function() {},
     chain = Object.create || function(object) {
          TemplateClass.prototype = object;
          var result = new TemplateClass;
          TemplateClass.prototype = null;
          return result;
     };
function extend(SubClass, SuperClass, overrides) {
     var subProto, name;
     SuperClass = SuperClass || Object;
     SubClass.prototype = chain(SuperClass.prototype);
     SubProto = SubClass.prototype;
     SubProto.constructor = SubClass;
     if (overrides) {
          for (name in overrides) {
               if (overrides.hasOwnProperty(name)) {
                    SubProto[name] = overrides[name];
               }
          }
     }
}
对原型例子代码重构:
function Animal(name, color) {
     this.name = name;
     this.color = color;
}
extend(Animal, Object, {
     sleep: function() {
          alert(this.name + ' sleep');
     }
});
var a1 = new Animal('倒霉熊', 'white');
a1.sleep();  //倒霉熊sleep
function Cat() {
     Animal.apply(this, arguments);
}
extend(Cat, Animal, {
     greenEye: true,
     mew: function() {
          alert(this.name + ' mew');
     }
});
var c2 = new Cat('没头脑', 'red');
c2.mew();  //没头脑 mew
c2.sleep();  //没头脑 sleep
alert(c2.greenEye); //true
function PersianCat() {
     Cat.apply(this, arguments);
}
extend(PersianCat, Cat, {
     name: 'persian cat',
     blueEye: true,
     mew: function() {
          Cat.prototype.mew.call(this);
          alert(this.name + ' miaow');
     }
});
var p3 = new PersianCat('不高兴', 'yellow');
p3.mew();  //不高兴 mew, 不高兴 miaow
p3.sleep();  //不高兴 sleep
alert(p3.greenEye);  //true
alert(p3.blueEye);  //true

以上是关于JavaScript基于原型的面向对象编程的全部。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值