zoukankan      html  css  js  c++  java
  • 复习面向对象 -- 原型与原型链

      面向对象中原型以及原型链非常重要,复习面向对象,学习一下原型和原型链,创建对象部分可以看前一个文章,传送门,继承部分可以看后一个文章,传送门

    什么是原型:

      原型有两种形式:prototype__proto__;对应的呈现方式不同。
      prototype:是函数的一个属性,这个属性的值是一个对象。所以一切的函数都有原型,这个原型就是prototype。
      __proto__:是对象的一个属性,同样的属性值也是一个对象。(但__proto__不是一个规范属性,只是部分浏览器实现了此属性,对应的标准属性是[[prototype]])所以一切对象都有标准属性。

      ps:函数没有__proto__属性,同样的,对象也没有prototype属性。

    var obj = {name:'peter'};
    console.log(obj.__proto__) // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
    
    var fn = function(){this.name = 'peter'};
    console.log(fn.prototype) // {constructor: ƒ}

      所有的函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype,所以所有的自定义类型都含有toString(),valueOf()等默认方法的根本原因。

    理解原型对象:

      在高程中,有句原话:无论什么时候,只要创建了一个函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
      简单来说:只要创建了一个函数,都会有一个prototype属性,属性值就是一个对象,这个对象里有一个属性constructor,这个值就是该函数。

      constructor是一个构造器。一般指向的是其构造函数。

    var fn = function(){this.name = 'peter'};
    console.log(fn.prototype) // {constructor: ƒ}   
    console.log(fn.prototype.constructor == fn)  // true

      对于__proto__,只要是对象,都有constructor属性。在创建的方式中,constructor所指向的构造函数不一样。

      1 字面量创建对象:

      字面量创建对象,其中__proto__的constructor属性指向的构造函数是原始对象函数Object()。

    var obj = {name:'peter'}
    console.log(obj.__proto__.constructor) // ƒ Object() { [native code] }
    
    console.log(obj.__proto__.constructor == Object);  // true
    console.log(obj.constructor == Object )  // true
    console.log(obj.__proto__.constructor == obj.constructor) // true

      2 构造函数创建对象:

      所谓的构造函数:就是先创建一个函数,new实例化一个对象,函数里的this指向这个对象
      构造函数是个函数,有prototpe属性,值是个对象,对象里有个constructor属性,指向这个函数Preson
      实例化的对象peter是一个对象,有__proto__属性,值是个对象,对象里constructor属性,指向这个构造函数Person。
      两个constructor所对应的构造函数是相等的,因此实例化的__proto__ 和 构造函数的prototype是相等的。

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.sayHi = function (){
            console.log(this.name);
        }
    }
    var peter = new Person('peter','male');
    console.log(Person.prototype.constructor == peter.__proto__.constructor)  // true
    console.log(Person.prototype == peter.__proto__) // true

      3 Object.create() 创建对象

      Object.create()是es5新增的一个方法,创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

      ps!这个创建的对象的__proto__和原对象的__proto__不一样,这说明了新创建的对象只是复制愿对象的原型,但是是一个独立的原型。

    var obj = {};
    var male = Object.create(obj);
    console.log(male.__proto__.constructor) // ƒ Object() { [native code] }
    male.name = 'tom';
    console.log(male); //  {name:'tom'}
    console.log(obj); //  {name:'peter'}
    console.log(male.__proto__ == obj.__proto__);  // false

    原型链:

      其基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。
      当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找,如果该__proto__上没有这个属性,就去__proto__的属性上去找(__proto__.__proto__),依次往下找,找到就使用,找不到就继续往下找,到最上层都没有找到就返回null。
      这样的形式就叫原型链。 总结如下:由于__proto__是任何对象都有的属性,所以会形成一条__proto__连起来的链条,递归访问__proto__,到最后若没有找到,则返回一个null。
      例子如下:(为了强调原型链,所以采用了混合模式创建对象)

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.sayHi = function(){
        console.log(this.name);
    }
    var peter = new Person('peter','male');
    peter.sayName = function(){
        console.log('我叫17号')
    }
    peter.sayName();
    peter.sayHi();
    peter.toString();
    peter.toGo();

      这个例子要调用四个方法。先描述怎么拿到该方法:

      1.peter.sayName(); // 我叫17号
      对象本身写的方法,是挂载了对象的属性上,所以不用通过原型链的方式直接就能拿到该方法。

      2.peter.sayHi(); // peter
      该对象上没有sayHi的方法,=> 该对象的__proto__去找 => peter.__proto__(由于peter.__proto__ = Person.prototype) => 该prototype上有sayHi属性,调用,查找结束。

      3.peter.toString(); // "[object Object]"
      该对象上没有toString的方法,=> 该对象的__proto__去找 => peter.__proto__ => 该__proto__上没有该属性 => peter.__proto__.__proto__ => 在该原型上找到toString(),调用,结束。

      4.peter.toGo();
      该对象上没有toGo的方法,=> 该对象的__proto__去找 => peter.__proto__ => 该__proto__上没有该属性 => peter.__proto__.__proto__ => 在该原型上没有该属性 => peter.__proto__.__proto__.__proto__ => 返回null => 没有该属性,直接报错。

      从上面的例子就可以得知,原型链就是通过__proto__连起来的链子,可以依次查找,找到就调用,找不到直接undefined或报错。这就是原型链。

     
    后记:
      在复习面向对象中,由于篇幅过长,将知识点分成了三部分,面向对象--创建对象,面向对象--原型与原型链,面向对象--继承,对应的知识点我放在了github里,有需要的可以去clone学习,觉得好的话,给个star。
      文章有不对或者不理解的地方,请私信或者评论,一起讨论进步。

     
    参考资料:
      javascript高级程序设计(第三版)
  • 相关阅读:
    Algorithm Gossip (37) 快速排序法 ( 一 )
    Algorithm Gossip (36) Heap排序法( 堆排序 )
    Algorithm Gossip (35) Shaker法
    Algorithm Gossip (34) 希尔排序
    AlgorithmGossip (33) 选择、插入、气泡排序
    Algorithm Gossip (32) 得分排行
    Algorithm Gossip (31) 数字拆解(dp问题)
    Algorithm Gossip (30) m元素集合的 n 个元素子集
    Algorithm Gossip (29) 产生可能的集合
    Algorithm Gossip (27) 排列组合
  • 原文地址:https://www.cnblogs.com/sqh17/p/9664878.html
Copyright © 2011-2022 走看看