深入理解JavaScript系列(5):强大的原型和原型链

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 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子:
这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/u014749668/article/details/82692777