定义一个对象的最简单的方法
var car = {
color:"red",
drive:function() {
alert(this.color + " car moved");
}
}
这个方法不是在非常有用,因为它创建了一个单独的对象,而这个对象和任何常见的数据结构没有任何联系,为了创建第二个car实例,必须重新定义它的结构.
通过构造函数创建一个新对象
function Car() {
this.color = "red";
this.drive = function(){
alert(this.color + " car moved");
}
}
var car = new Car();
car.drive();
JavaScript中的每个函数都有一个称为prototype的属性.如果某个函数被用作构造函数,则这个属性会被自动通过new调用创建对象的原型
function Car() {
this.color = "red";
}
Car.prototype.drive = function(){
alert(this.color + " car moved");
}
var car = new Car();
car.drive();
对prototype属性所做的任何更改能够应用于通过new Car()构造的每一个对象,不管它是在更改之前还是更改后创建.为Car.prototype 添加新函数.对于共享相同原型的每一个对象,该函数都可以立即使用,无论它是在更改之前或是之后构造,具体如下:
// 更新原型
function Car(color) {
this.color = color;
}
Car.prototype.drive = function(){
alert(this.color + " car is driving");
}
var car1 = new Car("red");
alert(car1.__proto__ === Car.prototype); // true
car1.drive();
// 为原型添加新的函数
Car.prototype.stop = function() {
alert(this.color + " car has stopped");
}
var car2 = new Car("blue");
// 共享同一对象原型的对象
alert(Object.getPrototypeOf(car1) === Object.getPrototypeOf(car2)); // true
// 这两个对象现在都能访问这个新的方法
car2.stop();
car1.stop();
面向对象的很重要一个特性就是继承了,这里只要介绍通过原型链继承
// 继承的基本设置
function Car(color) {
this.color = color;
}
Car.prototype.drive = function(){
alert(this.color + " car is driving");
}
Car.prototype.turn = function(direction) {
alert(this.color + " car turns " + direction);
}
Car.prototype.stop = function() {
alert(this.color + " car has stopped");
}
// 消防车
function FirTruck() {}
FirTruck.prototype.turnCannon = function(direction) {
alert("Cannon moves to the " + direction);
}
var truck = new FirTruck();
// 因为Car.prototype 并不在truck对象的原型链中
// 因此方法move()并不可用
truck.move();
truck.turnCannon("right");
JavaScript中有一个很特殊的Object.create(proto, properties)方法, 注:IE8及以下不支持
它可以用来创建一个新的空白对象并将其原型设置为proto
function Car(color) {
this.color = color;
}
Car.prototype.move = function(){
alert(this.color + " car is driving");
}
Car.prototype.turn = function(direction) {
alert(this.color + " car turns " + direction);
}
Car.prototype.stop = function() {
alert(this.color + " car has stopped");
}
if(!Object.create){
// 兼容性处理
FireTruck.prototype = Car.prototype;
}else{
FireTruck.prototype = Object.create(Car.prototype);
}
function FireTruck() {}
// 检查,以防万一
//alert(Object.getPrototypeOf(FireTruck.prototype) === Car.prototype);
// 如果为true
// 则Car.prototype被添加到链中
FireTruck.prototype.turnCannon = function(direction){
alert("Cannon moves to the " + direction);
}
var truck = new FireTruck();
// 现在可以工作了,因为Car.prototype已经在truck对象的原型链中
truck.move();
truck.turnCannon("right");
代码可以运行,但其输出还是存在一些问题
true
undefined car is driving
Cannon moves to the right
小汽车的color现在可以通过Car的构造函数进行设置.构造函数本身没有执行,所以其color显示为undefined.这个问题很好解决,修改FireTruck构造函数,在其中添加对Car构造函数的调用,这样Car就可以初始化它自己的对象变量
function FireTruck() {
Car.call(this, "red");
}
再次运行这段代码,欣赏一下正确的输出
即使该代码能够成功运行,但仍有一个小问题需要解决.当这个新函数创建时,它的prototype属性并不为空.它有一个称为constructor的属性将引用函数本身,当调用
FireTruck.prototype = Object.create(Car.prototype);
时,该属性丢失,因为这个新建的对象并不具有自己的属性.可以将丢失的属性作为第二个参数传递给create
FireTruck.prototype = Object.create(Car.prototype, {
constructor: {
value: FireTruck, // 和FireTruck.prototype.constructor一样
enumerable: false,
writable: true,
configurable: true
}
});
这里的constructor并不是通常的属性.当对象的键值进行循环时,它不会出现,但可以通过名称直接访问.通过给create()的第二个参数传递属性"描述符"可以取得相同的效果描述符具有如下所示的几个字段
value: 初始值
enumerable: 如果属性显示在对象属性的列表中, 在像for...in 这样的循环或Object.keys中
writable: 如果属性可以被指派一个不同的值
configurable: 如果描述符定义的规则可以被修改或其属性可以删除,则为true
这样就可以模仿真实的 constructor 的行为
最终版本的的继承示例
function extend(subConstructor, superConstructor) {
if(!Object.create){
subConstructor.prototype = superConstructor.prototype;
subConstructor.constructor = {
value: FireTruck, // 和FireTruck.prototype.constructor一样
enumerable: false,
writable: true,
configurable: true
}
}else{
subConstructor.prototype = Object.create(superConstructor.prototype, {
constructor: {
value: FireTruck, // 和FireTruck.prototype.constructor一样
enumerable: false,
writable: true,
configurable: true
}
});
}
}
function Car(color) {
this.color = color;
}
Car.prototype.move = function(){
alert(this.color + " car is driving");
}
Car.prototype.turn = function(direction) {
alert(this.color + " car turns " + direction);
}
Car.prototype.stop = function() {
alert(this.color + " car has stopped");
}
function FireTruck() {
Car.call(this, "red");
}
extend(FireTruck, Car);
FireTruck.prototype.turnCannon = function(direction){
alert("Cannon moves to the " + direction);
}
var truck = new FireTruck();
truck.move();
truck.turnCannon("right");