js 原型链

ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

构造函数、原型和实例 的关系:

  1.每一个构造函数都有一个原型对象 (prototype)

  2.原型对象都包含一个指向构造函数的指针(constructor)

  3.实例都包含一个指向原型 对象的内部指针(__proto__)

而原型链则是让一个原型对象等于另一个构造函数的实例。如

    function Animal(){}
    Animal.prototype.name = 'zs';
    Animal.prototype.sayName = function(){
        console.log(this.name);
    }
    function Dog(){}
    //继承了 Animal
    Dog.prototype = new Animal();
    var dog = new Dog();
    
    console.log(dog instanceof Animal);//true 
    console.log(Dog.prototype.__proto__ === Animal.prototype); //true
    console.log(dog.__proto__.__proto__ === Animal.prototype); //true
    dog.sayName();//zs

在 上面 dog 调用 sayName 时,会先搜索实例本身,是否存在该方法,没找到之后就去搜索原型中是否有该方法,然后返回保存在那里的方法。

上面经历了3个步骤:1.搜索 dog =>  2. 搜索 Dog.prototype  => 3. 搜索  Animal.prototype ,然后返回。

在找不到属性或方法的情况下,搜索过 程总是要一环一环地前行到原型链末端才会停下来。

1.默认的原型

  前面例子中展示的原型链还少一环,所有的引用类型都默认都继承了 Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是 Object 的实例,

因此默认原 型都会包含一个内部指针,指向 Object.prototype。如下

  

    function test(){}
    var t1 = new test();
    console.log(test instanceof Object);//true
    console.log(t1 instanceof Object);//true
   console.log(t1 instanceof test);//true
//所有函数的默认原型都是 Object 的实例 test.prototype = new Object();类似如此 console.log(test.prototype.__proto__ === Object.prototype); //true console.log(t1.__proto__.__proto__ === Object.prototype); //true

2.确定原型和实例的关系

  可以通过两种方式来确定原型和实例之间的关系。第一种方式是使用 instanceof 操作符,只要用 这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。

    console.log(test instanceof Object);//true
    console.log(t1 instanceof Object);//true
   console.log(t1 instanceof test);//true

由于原型链的关系,我们可以说 t1 是 Object、test 中任何一个类型 的实例。因此,测试这2个构造函数的结果都返回了 true。

3. 谨慎地定义方法

  子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎 样,给原型添加方法的代码一定要放在替换原型的语句之后。

  

    function Animal(){}
    Animal.prototype.test = function(name){
        console.log(name);
    }
    function Dog(){}
    Dog.prototype = new Animal();
    //添加新方法
    Dog.prototype.test1 = function(){
        //--
    }
    //重写超类型中的方法
    Dog.prototype.test = function(name){
        alert(name);
    }
    var dog = new Dog();
    dog.test('zs');

  以上代码加粗的部分,test1() 方法是在Dog 原型上新增的方法,test() 方法则是 在原型链上存在的方法,这里重写其实是屏蔽的原型链上的方法,而不是改写原型链上的方法,在 Dog 的实例调用 test () 方法时,

调用的是这个重写定义的方法,但是 当 Animal 的实例调用时,还会继续使用之前的那个方法。

    var animal = new Animal();
    animal.test('ls'); // 打印出来的,而不是弹出

还有就是 在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这 样做就会重写原型链。如下

    function Animal(){}
    Animal.prototype.test = function(name){
        console.log(name);
    }
    function Dog(){}
    Dog.prototype = new Animal();
    Dog.prototype = {
        test : '123'
    }
    
    var dog = new Dog();
    dog.test('zs'); //error

4. 原型链的问题

  在通过原型来实现继承时,原 型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。

  这个在 原型 中已经记录过了, 借用构造函数 解决

 5.原型继承时,添加属性

   在实现原型继承时候,添加属性

    function P(name){
        this.name = name;
    }
    P.prototype.sayName = function(){
        console.log(this.name)
    }
    //  A 继承 P,并且添加一个 age 属性,
    function A(name, age){
        P.call(this,name);// 类似es6 super 方法,改变this 指向
        this.age = age;
    }
    A.prototype = new P();
    A.prototype.constructor = A;
    A.prototype.sayAge = function(){
        console.log(this.age);
    }
    var a = new A('张三', 25);
    a.sayName();
    a.sayAge();
原文地址:https://www.cnblogs.com/bruce-gou/p/9662729.html