• JS中原型与原型链


    一. 普通对象与函数对象

    JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function等 是 JS 自带的函数对象。下面举例说明。

    var o1 = {}; 
    var o2 =new Object();
    var o3 = new f1();
    
    function f1(){}; 
    var f2 = function(){};
    var f3 = new Function('str','console.log(str)');
    
    console.log(typeof Object); //function 
    console.log(typeof Function); //function  
    
    console.log(typeof f1); //function 
    console.log(typeof f2); //function 
    console.log(typeof f3); //function   
    
    console.log(typeof o1); //object 
    console.log(typeof o2); //object 
    console.log(typeof o3); //object
    在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象(Function),其他的都是普通对象(Object)。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object也都是通过 New Function()创建的

    二. 原型对象

    function Person(){}
    Person.prototype.name = 'test';
    Person.prototype.age  = 28;
    Person.prototype.job  = 'Software Engineer';
    Person.prototype.sayName = function() {
      alert(this.name);  
    }
    var person = new Person();

    在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。而原型对象他本身就是一个普通对象(没有通过 new Function() 创建的对象都是普通对象),即原型对象就是Person.prototype,如果你还是害怕它,那就把它想想成一个字母 A:var A = Person.prototype。这里要强调一点,只有函数对象才会拥有prototype属性,但是每个对象都拥有__proto__属性(null除外)

    在上面我们给A添加了四个属性:name、age、job、sayName。其实它还有一个默认的属性:constructor。在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype属性所在的函数(Person)。即:Person.prototype.constructor == Person。当我们创建对象var person = new Person()时,person可以继承原型对象Person.prototype的constructor属性,因此person.constructor == Person,注意person这个实例本身是没有constructor,实例的constructor是通过原型链(__proto__)获取原型对象上边的constructor。

    person.constructor == Person
    Person.prototype.constructor == Person

    从这一角度,我们可以将Person.prototype理解为Person的一个实例。(但其实原型对象(Person.prototype)并不是构造函数(Person)的实例,而是构造函数的属性,而且是预定义添加的)。

    var A = new Person();
    Person.prototype = A;

    但是有一个非常特别的原型对象:Function.prototype,它并不是普通对象,而是函数对象,而这个函数对象却没有prototype属性(前面所说的“每个函数对象都有一个prototype属性,这个属性指向函数的原型对象”,对Function.prototype并不适用)。

    function Person(){};
    console.log(typeof Person.prototype) //Object
    console.log(typeof Function.prototype) // Function,这个特殊
    console.log(typeof Object.prototype) // Object
    console.log(typeof Function.prototype.prototype) //undefined

    Function.prototype为什么是函数对象呢?

     var A = new Function();
     Function.prototype = A;

    上文提到过凡是通过凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。因为 A 是函数对象,所以Function.prototype是函数对象。

    . __proto__

    JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的构造函数的原型对象。对象 person有一个__proto__属性,创建它的构造函数是Person,构造函数的原型对象是Person.prototype ,所以:person.__proto__ == Person.prototype

    Person.prototype.constructor == Person;
    person.__proto__ == Person.prototype;
    person.constructor == Person;

    类似的,Person.__proto__ == Function.prototype;Person.prototype.__proto__ == Object.prototype;Object.__proto__ == Function.prototype; Object.prototype.__proto__ == null,按照上述理解 Object.prototype是普通对象,而普通对象的构造函数是Object,那么Object.prototype.__proto__ == Object.prototype,从而在原型链上形成死循环无法终止,因此定义Object.prototype.__proto__ == null,null是原型链的顶端。

    不过,要明确的真正重要的一点就是,这个连接存在于实例(person)与构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于实例(person)与构造函数(Person)之间

    var animal = function(){};
    var dog = function(){};
    
    animal.price = 2000;
    dog.prototype = animal;
    var tidy = new dog();
    console.log(dog.price) //undefined
    console.log(tidy.price) // 2000

    实例(tidy)和 原型对象(dog.prototype)存在一个连接。这个连接存在于实例(tidy)与构造函数的原型对象(dog.prototype)之间,而不是存在于实例(tidy)与构造函数(dog)之间。

    四. 函数对象

    所有函数对象的__proto__都是指向Function.prototype,它是一个空函数。

    Number.__proto__ === Function.prototype  // true
    Number.constructor == Function //true
    
    Boolean.__proto__ === Function.prototype // true
    Boolean.constructor == Function //true
    
    String.__proto__ === Function.prototype  // true
    String.constructor == Function //true
    
    Object.__proto__ === Function.prototype  // true
    Object.constructor == Function // true
    
    // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
    Function.__proto__ === Function.prototype // true
    Function.constructor == Function //true
    
    Array.__proto__ === Function.prototype   // true
    Array.constructor == Function //true
    
    RegExp.__proto__ === Function.prototype  // true
    RegExp.constructor == Function //true
    
    Error.__proto__ === Function.prototype   // true
    Error.constructor == Function //true
    
    Date.__proto__ === Function.prototype    // true
    Date.constructor == Function //true

    所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。Function.__proto__ == Function.prototype,而前面说过,Function.prototype它不是普通对象,而是函数对象,那么Function.prototype.__proto__ == ?,按照上述,Function.prototype.__proto__ == Function.prototype,但又出现了原型链上的死循环,JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向 Object.prototype,Object.prototype.__proto__ == null,保证原型链能够正常结束。

    Function.prototype.__proto__ ===  Object.prototype // true

    特别的,Math,JSON是以普通对象形式存在的。


    Math.__proto__ === Object.prototype // true Math.construrctor == Object // true JSON.__proto__ === Object.prototype // true JSON.construrctor == Object //true

    四. 总结

    原型和原型链是JS实现继承的一种模型。

    原型链的形成是真正是靠__proto__而非prototype。

    参考资料:https://www.jianshu.com/p/dee9f8b14771

  • 相关阅读:
    php iconv函数转换出错问题
    linux 上配置swoole
    Linux中查看某 个软件的安装路径
    mysql 5.0存储过程学习总结
    maven--私服的搭建(Nexus的使用)
    Linux的chattr与lsattr命令详解
    [转]Delphi 中 image 控件加载bmp、JPG、GIF、PNG等图片的办法
    [转]Delphi 中动态链接库(dll)的建立和使用
    Delphi PChar与String互转
    [转]Delphi 快捷键 让你更像高手!!
  • 原文地址:https://www.cnblogs.com/RainyBear/p/8612560.html
Copyright © 2020-2023  润新知