美文网首页
原型和原型链

原型和原型链

作者: 肥羊猪 | 来源:发表于2021-03-02 09:18 被阅读0次

_ proto_,隐式原型 对象特有(constructor
prototype 显式原型 函数特有

prototype原型最大的作用,共享属性和方法
js的原型是为了实现对象间的联系,解决构造函数无法数据共享而引入的一个属性。原型链是实现对象间联系(继承)的主要方法

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.sayHello=function(){
        console.log(this.name + "say hello");
    }
给函数Person的原型中声明了sayHello方法,当我们的构造实例对象去访问的时候访问的方法是同一个
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(girl.name);  //bella
    console.log(boy.name);   //alex
    console.log(girl.sayHello === boy.sayHello);  //true 

所有的引用类型,都具有对象特性,即可自由扩展属性(除了“null”以外)
所有的引用类型,都有一个隐式原型__proto__属性,属性值是一个普通的对象
所有的引用类型,隐式原型__proto__属性值指向它的构造函数的显式原型“prototype”属性值
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__(也就是它的构造函数的显式原型prototype)中寻找。

instanceof是通过原型去进行比较对象是否属于当前比较的构造函数

原型和原型链.png

继承是通过原型链来体现的

function Person(){  }
  var p1=new Person();
  Person.prototype.name='老王';
  Person.prototype.age='99';
  console.log(p1.name);//'老王'
p1是Person实例化出来的函数,我并没有给p1定义name这个属性,那p1.name是怎么来的--是从Person.prototype来的,
因为p1._ proto_指向Person.prototype,当访问对象的某个属性时,现在这个对象本身去找,
如果找不到那就顺着_ proto_往上找,直到找到或者Object.prototype为止。

继承参考https://www.cnblogs.com/ranyonsue/p/11201730.html
组合继承(常用)
组合 原型链继承 和 借用构造函数继承
背后的思路是:使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。

Student.prototype = new Person()
 Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的


function Parent(name){
            this.name = name;
            this.colors = ['red', 'blue', 'green'];
        }

        Parent.prototype.getName = function(){
            console.log(this.name);
        }

        function Child(name,age){
            Parent.call(this,name);// 第二次调用 Parent()
            this.age = age;
        }

        Child.prototype = new Parent(); // 第一次调用 Parent()

        var child1 = new Child('xiaopao',18);
        var child2 = new Child('lulu',19);
        child1.getName(); // xiaopao
        child2.getName(); // lulu
        console.log(child1.age); // 18
        console.log(child2.age); // 19
        child1.colors.push('yellow');
        console.log(child1.colors);  // ["red", "blue", "green", "yellow"]
        console.log(child2.colors); // ["red", "blue", "green"]
        console.log(child1 instanceof Child); // true
        console.log(child1 instanceof Parent); // true
优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
缺点:调用了两次父类构造函数

寄生组合式继承
子类构造函数复制父类的自身属性和方法,子类原型只接收父类的原型属性和方法

// 寄生组合式继承
function Parent(name){
            this.name = name;
            this.colors = ['red', 'blue', 'green'];
        }

        Parent.prototype.sayName = function(){
            console.log(this.name);
        }

        function Child(name,age){
            Parent.call(this,name); 
            this.age = age;
        }

        function CreateObj(o){
            function F(){};
            F.prototype = o;
            return new F();
        }

        // Child.prototype = new Parent(); // 这里换成下面
        function prototype(child,parent){
            var prototype = CreateObj(parent.prototype);
            prototype.constructor = child;
            child.prototype = prototype;
        }
        prototype(Child,Parent);

        var child1 = new Child('xiaopao', 18);
        console.log(child1); 
普遍认为寄生组合式继承是引用类型最理想的继承方式

原型链继承

Student.prototype = new Person() // 子类型的原型为父类型的一个实例对象
子类的实例就可以通过__proto__访问到 Student.prototype 也就是Person的实例,
这样就可以访问到父类的私有方法,然后再通过__proto__指向父类的prototype就可以获得到父类原型上的方法
特点:
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现
缺点:
无法实现多继承
来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中

借用构造函数继承

Person.call(this, name, age)  // 相当于: this.Person(name, age)
特点:
解决了原型链继承中子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性和方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

组合继承优化

借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
      function Person(name, age) {
           this.name = name,
           this.age = age
       }
       Person.prototype.setAge = function () {
           console.log("111")
       }
       function Student(name, age, price) {
           Person.call(this, name, age)
           this.price = price
           this.setScore = function () {}
       }
       Student.prototype = Object.create(Person.prototype)//核心代码
       Student.prototype.constructor = Student//核心代码
       var s1 = new Student('Tom', 20, 15000)
       console.log(s1 instanceof Student, s1 instanceof Person) // true true
       console.log(s1.constructor) //Student
       console.log(s1)

ES6中class 的继承
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。

       class Person {
            //调用类的构造方法
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            //定义一般的方法
            showName() {
                console.log("调用父类的方法")
                console.log(this.name, this.age);
            }
        }
        let p1 = new  Person('kobe', 39)
        console.log(p1)
        //定义一个子类
        class Student extends Person {
            constructor(name, age, salary) {
                super(name, age)//通过super调用父类的构造方法
                this.salary = salary
            }
            showName() {//在子类自身定义方法
                console.log("调用子类的方法")
                console.log(this.name, this.age, this.salary);
            }
        }
        let s1 = new Student('wade', 38, 1000000000)
        console.log(s1)
        s1.showName()

并不是所有的浏览器都支持class关键字

相关文章

网友评论

      本文标题:原型和原型链

      本文链接:https://www.haomeiwen.com/subject/ngoofltx.html