zoukankan      html  css  js  c++  java
  • javascript原型链

    概念

    1 原型对象 Prototype

    我们创建的每个函数都有一个 prototype(原型)属性,指向一个对象,而这个对象的用途是包含所有实例共享的属性和方法。这个prototype指向的对象就是调用构造函数创建的实例的原型对象

    function F () {
        this.selfMethod = function (){
            console.log('selfMethod');
        }
    };
    F.prototype.publicMethod = function() {
        console.log('public method');
    }
    //构造函数内部定义的函数在不同实例上是不同的 
    let f1 = new F();
    let f2 = new F();
    f1.selfMethod === f2.selfMethod; //false
    //在原型上定义定义公共方法,被所有实例共享
    f1.publicMethod === f2.publicMethod; //true
    
    let f = new F(); //一个实例
    let prototype = Object.getPrototypeOf(f); //获取f的原型对象
    prototype === F.prototype; //true

     2 原型链

    每个实例对象(object )都有一个私有属性(称之为__proto__)指向它的原型对象(prototype),建议通过Object.getPrototypeOf()获取。该原型对象也有一个自己的原型对象(__proto__) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

    在调用一个对象的属性或者方法时,就是顺着这个原型链来找的,对象本身没有找到的话,就会在它的原型对象找,原型对象上没有,就寻找原型对象的原型对象,层层向上。

    几乎所有 JavaScript 中的对象都是位于原型链顶端的Object的实例。所以几乎所有对象都能调用Object.prototype.toString()/valueOf()。

    原型链越长,搜寻属性/方法的时间越长,会对性能产生影响。

    function F() {
        this.selfProp = 'self'
    }
    F.prototype.publicProp = 'pub';
    let f = new F();
    
    console.log(Object.getOwnPropertyNames(f));; //['selfProp'];
    console.log(Object.getOwnPropertyNames(F.prototype));; //['publicProp', 'constructor'];
    console.log(f.publicProp === 'pub'); //true

    console.log(typeof f.toString === 'function') //true
    Object.getPrototypeof(F.prototype) === Object.prototype //在Object.prototype 中找到了toString
    Object.getPrototypeof(Object.prototype) === null //true
    console.log(f.specialProp === 'undefined') // true 在整个原型链中找不到这个属性 经历了4次查询

     作用

    1 原型对象:在原型对象上定义的属性和方法能够被所有实例共享,减少占用内存的空间。需要注意的一点是,如果通过实例对公共属性赋值时,会在实例上增加这个属性,从而屏蔽掉原型对象上的属性,只有删除这个属性之后才会重新使用原型对象上的属性。此处还应注意公共属性是否是复合类型,如对象数组等。

    function F() {
        this.selfProp = 'self'
    }
    F.prototype.publicProp = 'pub';
    let f = new F();
    console.log(f.publicProp === 'pub');; //true
    console.log(Object.getOwnPropertyNames(f));; //['selfProp'];
    console.log(Object.getOwnPropertyNames(F.prototype));; //['publicProp', 'constructor'];
    f.publicProp = 'otherpub';
    console.log(Object.getOwnPropertyNames(f));; //['selfProp', 'publicProp']; 此处多了publicProp
    console.log(Object.getOwnPropertyNames(F.prototype));; //['publicProp', 'constructor'];
    delete f.publicProp 
    console.log(Object.getOwnPropertyNames(f));; //['selfProp']; 删除了publicProp,可以重新使用原型对象上的publicProp
    console.log(Object.getOwnPropertyNames(F.prototype));; //['publicProp', 'constructor'];

    2 原型链: 原型链是javascript实现继承的主要方式,将子类实例的原型对象,设置为父类实例,就实现了继承

    function Parent(firstName) {
        this.firstName = firstName;
    }
    Parent.prototype.getFirstName = function() {
        return this.firstName;
    }
    function Child(firstName, lastName) {
        Parent.call(this, firstName);
        this.lastName = lastName;
    }
    
    Child.prototype = new Parent(); //将子类的原型设置为父类的实例
      Child.prototype.constructor = Child; //将原型中的constructor 指回Child
    Child.prototype.getLastName = function() {
        return this.lastName;
    }
    
    let c = new Child('Michael', 'Jordan');
    console.log(c.getFirstName()); //Michael
    console.log(c.getLastName()); //Jordan
    
    console.log(c instanceof Parent); //true
    console.log(Object.getOwnPropertyNames(c)); //[ 'firstName', 'lastName' ]
    console.log(Object.getOwnPropertyNames(Child.prototype)); // [ 'firstName', 'constructor' ,'getLastName' ] 同样有firstName
      console.log(Object.getOwnPropertyNames(Parent.prototype)); // [ constructor', 'getFirstName' ]

    上面的代码中可以看到 c 是Parent的实例,能够调用 Parent.prototype 中的方法,因为实例c能访问Child.prototype, Child.prototypey又是Parent的实例,能够访问Parent.prototype,从而通过原型链实现了继承。但是这样的继承有个问题,就是Parent的构造函数执行了两次,第一次是在Child的构造函数中通过Parent.call(), 第二次是Child.prototype =  new Parent()的时候,这也是为什么Child.prototype中有firstName这个属性的原因。

    更好的实现

    function Parent(firstName) {
        this.firstName = firstName;
    }
    Parent.prototype.getFirstName = function() {
        return this.firstName;
    }
    function Child(firstName, lastName) {
        Parent.call(this, firstName);
        this.lastName = lastName;
    }
    
    Child.prototype = Object.create(Parent.prototype); //通过Object.create 创建原型对象 减少一次Parent构造函数的调用
    Child.prototype.constructor = Child; //将原型中的constructor 指回Child
    Child.prototype.getLastName = function() {
        return this.lastName;
    }
    
    let c = new Child('Michael', 'Jordan');
    console.log(c.getFirstName()); //Michael
    console.log(c.getLastName()); //Jordan
    
    console.log(c instanceof Parent); //true
    console.log(Object.getOwnPropertyNames(c)); //[ 'firstName', 'lastName' ]
    console.log(Object.getOwnPropertyNames(Child.prototype)); // [ constructor', 'getLastName' ] 没有了 firstName
     console.log(Object.getOwnPropertyNames(Parent.prototype)); // [ constructor', 'getFirstName' ]
     

    最好的实现方式 通过es6的class语法糖

    class Parent {
        constructor(firstName) {
            this.firstName = firstName;
        }
    
        getFristName() {
            return this.firstName;
        }
    }
    
    class Child extends Parent {
        constructor(firstName, lastName) {
            super(firstName);
            this.lastName = lastName;
        }
    
        getLastName() {
            return this.lastName;
        }
    }
    
    let c = new Child('Michael', 'Jordan');
    console.log(c instanceof Parent); //true
    
    console.log(c.getFristName()); //Michael
    console.log(c.getLastName()); //Jordan
    
    console.log(Object.getOwnPropertyNames(c)); //[ 'firstName', 'lastName' ]
    console.log(Object.getOwnPropertyNames(Child.prototype)); //[ 'constructor', 'getLastName' ]
    console.log(Object.getOwnPropertyNames(Parent.prototype)); // [ constructor', 'getFirstName' ] 

    class语法在不支持es6语法的环境中需要转码。

  • 相关阅读:
    扩展json序列化datatime类型数据
    用select实现socket的IO多路复用
    Python单例模式
    Django(信号相关)
    将字符串按固定长度分隔成子串
    Android Handler介绍
    Android activity生命周期
    Java 启动线程的方式
    java线程中的sleep和wait区别
    JAVA 统计字符串中中文,英文,数字,空格的个数
  • 原文地址:https://www.cnblogs.com/pandapeter/p/10560960.html
Copyright © 2011-2022 走看看