理解原型

原型是一个对象。其它对象能够通过它实现属性继承。

不论什么一个对象都能够成为继承,全部对象在默认的情况下都有一个原型。由于原型本身也是对象,所以每一个原型自身又有一个原型。

不论什么一个对象都有一个prototype的属性。记为:__proto__。

每当我们定义一个对象,其__proto__属性就指向了其prototype。示比例如以下:

 

var foo = { 
x: 10, 
y: 20 
};

即使我们不指定prototype,该属性也会预留。假设我们有明白指向的话。那么链表就连起来了。

须要注意的是,prototype自己也有指向,就是最高级的object.prototype。

示比例如以下:

var a = { 
x: 10, 
calculate: function (z) { 
return this.x + this.y + z 
} 
}; 
var b = { 
y: 20, 
__proto__: a 
}; 

var c = { 
y: 30, 
__proto__: a 
}; 

// call the inherited method 
b.calculate(30); // 60

使用原型

理解了原型的原理之后,怎样使用原型呢?或者说原型有什么作用呢?

一般的刚開始学习的人。在刚刚学习了主要的javascript语法后,都是通过面向函数来编程的。

例如以下代码:

var decimalDigits = 2,
	tax = 5;

function add(x, y) {
    return x + y;
}

function subtract(x, y) {
    return x - y;
}

//alert(add(1, 3));

通过运行各个函数来得到最后的结果。

可是利用原型,我们能够优化一些我们的代码,使用构造函数

首先。函数本体中仅仅存放变量:

var Calculator = function (decimalDigits, tax) {
    this.decimalDigits = decimalDigits;
    this.tax = tax;
};

其详细的方法通过prototype属性来设置:

Calculator.prototype = {
    add: function (x, y) {
        return x + y;
    },

    subtract: function (x, y) {
        return x - y;
    }
};
//alert((new Calculator()).add(1, 3));

这样就能够通过实例化对象后进行对应的函数操作。

这也是一般的js框架採用的方法。

原型另一个作用就是用来实现继承

首先。定义父对象:


var BaseCalculator = function() {
    this.decimalDigits = 2;
};

BaseCalculator.prototype = {
    add: function(x, y) {
        return x + y;
    },
    subtract: function(x, y) {
        return x - y;
    }
};

然后定义子对象。将子对象的原型指向父元素的实例化:

var Calculator = function () {
    //为每一个实例都声明一个税收数字
    this.tax = 5;
};
        
Calculator.prototype = new BaseCalculator();

我们能够看到Calculator的原型是指向到BaseCalculator的一个实例上,目的是让Calculator集成它的add(x,y)和subtract(x,y)这2个function,另一点要说的是,因为它的原型是BaseCalculator的一个实例,所以无论你创建多少个Calculator对象实例,他们的原型指向的都是同一个实例。

上面的代码。执行以后,我们能够看到由于Calculator的原型是指向BaseCalculator的实例上的,所以能够訪问他的decimalDigits属性值,那假设我不想让Calculator訪问BaseCalculator的构造函数里声明的属性值,那怎么办呢?仅仅须要将Calculator指向BaseCalculator的原型而不是实例即可了。代码例如以下:

var Calculator = function () {
    this.tax= 5;
};

Calculator.prototype = BaseCalculator.prototype;

在使用第三方库的时候。有时候他们定义的原型方法不能满足我们的须要,我们就能够自己加入一些方法。代码例如以下:


//覆盖前面Calculator的add() function 
Calculator.prototype.add = function (x, y) {
    return x + y + this.tax;
};

var calc = new Calculator();
alert(calc.add(1, 1));


原型链

对象的原型指向对象的父。而父的原型又指向父的父,这样的原型层层的关系。叫做原型链

在查找一个对象的属性时。javascript会向上遍历原型链,直到找到给定名称的属性为止。当查找到达原型链的顶部,也即是Object.prototype。仍然没有找到指定的属性。就会返回undefined。

示比例如以下:


function foo() {
    this.add = function (x, y) {
        return x + y;
    }
}

foo.prototype.add = function (x, y) {
    return x + y + 10;
}

Object.prototype.subtract = function (x, y) {
    return x - y;
}

var f = new foo();
alert(f.add(1, 2)); //结果是3。而不是13
alert(f.subtract(1, 2)); //结果是-1

我们能够发现,subtrace是依照向上找的原则,而add则出了意外。原因就是,属性在查找的时候是先查找自身的属性,假设没有再查找原型

 

说到Object.prototype,就不得不提它的一个方法。hasOwnProperty。

它能推断一个对象是否包括自己定义属性而不是原型链上的属性,它是javascript中唯一一个处理属性可是不查找原型链的函数。使用代码例如以下:

// 改动Object.prototype
Object.prototype.bar = 1; 
var foo = {goo: undefined};

foo.bar; // 1
'bar' in foo; // true

foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true

而为了推断prototype对象和某个实例之间的关系,又不得不介绍isPrototyleOf方法,演演示样例如以下:

alert(Cat.prototype.isPrototypeOf(cat2)); //true