JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型。
虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。
<script>
//【1】base
var BaseCalculator =function () {
//为每个实列都声明一个小数位数
this.decimalDigits=2;
}
//【2】 使用原型扩展两个对象方法
BaseCalculator.prototype.add=function (x, y) {
return x+y;
}
BaseCalculator.prototype.subtract=function (x, y) {
return x-y;
}
// 【3】定义一个类 (1)让Calculator集成它的add(x,y)和subtract(x,y)这2个function
Calculator=function () {
//为每个实列都声明一个整数数字
this.tax=5;
}
// (2)Calculator的原型是BaseCalculator它的实列,所以不管你创建多少个Calculator对象实例,他们的原型指向的都是同一个实例
Calculator.prototype=new BaseCalculator();
var cal=new Calculator();
console.log(cal.add(1,1))
console.log(cal.subtract(2,1))
console.log(cal.tax)
console.log(cal.decimalDigits)
</script>
(3)通过将BaseCalculator的原型赋给Calculator的原型,这样你在Calculator的实例上就访问不到那个decimalDigits值了,如果你访问如下代码,那将会提升出错。
不想让Calculator访问BaseCalculator的构造函数里声明的属性值
var Calculator = function () {
this.tax= 5;
};
Calculator.prototype = BaseCalculator.prototype;
重写原型:
在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过继续声明的同样的add代码的形式来达到覆盖重写前面的add功能,代码如下:
// 重写原型:
Calculator.prototype.add=function (x, y) {
return x+y+this.tax;
}
var calc=new Calculator();
console.log(calc.add(1,1))
这样,我们计算得出的结果就比原来多出了一个tax的值,但是有一点需要注意:那就是重写的代码需要放在最后,这样才能覆盖前面的代码。
原型链
function Foo() {
this.value = 24;
}
Foo.prototype = {
method: function () {
console.log('Foo method')
}
}
function Bar() {
}
Bar.prototype = new Foo();//【1】bar原型为Foo
Bar.prototype.foo = 'hello world';
//修正bar.prototype.constructor为bar本身
Bar.prototype.constructor = Bar;
var test = new Bar();
//原型链
test.method();
console.log(test.value += 6);
console.log(test.value);
console.log(test.foo)
console.log('------------------------test for in------------------')
for (var i in test) {
console.log(test[i])
}
上面的例子中,test 对象从 Bar.prototype 和 Foo.prototype 继承下来;因此,它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性
属性查找:
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined
//属性查找
console.log('-------------------------------属性查找---------------------')
function foo1() {
this.add=function (x, y) {
return x+y;
}
}
foo1.prototype.add=function (x, y) {
return x+y+10;
}
Object.prototype.subtract=function (x, y) {
return x-y;
}
var fo1=new foo1();
var a=fo1.add(1,2)
console.log(a)
通过代码运行,我们发现subtract是安装我们所说的向上查找来得到结果的,但是add方式有点小不同,这也是我想强调的,就是属性在查找的时候是先查找自身的属性,如果没有再查找原型,再没有,再往上走,一直插到Object的原型上,所以在某种层面上说,用 for in语句遍历属性的时候,效率也是个问题
还有一点我们需要注意的是,我们可以赋值任何类型的对象到原型上,但是不能赋值原子类型的值,比如如下代码是无效的:
hasOwnProperty函数:
hasOwnProperty是Object.prototype的一个方法
他能判断一个对象是否包含 自定义属性 而不是原型链上的属性
JavaScript 中唯一一个处理属性但是不查找原型链的函数。
Object.prototype.bar = 11;
var foo = {goo: undefined};
console.log(foo.bar)//11
console.log('bar' in foo)//true
console.log(foo.hasOwnProperty('bar'))////false
console.log(foo.hasOwnProperty('goo'))//true
只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。
但有个恶心的地方是:JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果。
console.log('------------------不被保护的 hasOwnProperty----------------')
var foo = {
hasOwnProperty: function () {
return false;
}, bar: 'Here be dragons'
};
var bl=foo.hasOwnProperty('bar');
console.log(bl);
console.log('------------------解决方法----------------');
// 使用{}对象的 hasOwnProperty,并将其上下为设置为foo
var bl1= ( {}).hasOwnProperty.call(foo, 'bar'); // true
console.log(bl1)
当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子: