美文网首页
对象、原型与原型链原理

对象、原型与原型链原理

作者: 卡拉咖啦 | 来源:发表于2019-06-19 16:27 被阅读0次

    在前面讨论 string , number 的文章中这些基本数据类型的时候,很少提到他们自身的方法,比如 toString ,实际上,因为除了对象的其它六种基本数据类型并没有携带这些方法,它们本身就是一个对应自身特征的 hash,虽然它们可以是对象,但是他们毕竟不是对象,与真正的对象有着根本上的差异。

    想要理解这一层,关键在于搞明白对象储存数据的方式。
    JavaScript 界有句流行的话,“一切都是对象”,其实不太准确,更准确地表达是“一切都可以是对象”,但可见对象在 JavaScript 语言中的重要性。

    对象的存储原理

    对象不同于其它基本数据类型的根本在于,它的值是不直接存在 stack 上的,它在 stack 上只存了一个指向真正存储数据的 Heap 上的地址

    存储.png

    于是,但我们直接使用一个对象的 toString 方法时,得到的是“奇怪”的东西:

    let a = {a:1};
    a.toString() // `[object object]`
    

    之所以这么做的原因就是因为,对象内部的数据状态是不稳定、可变的,如果在 stack 上实现的话,因为 stack 是讲究顺序的,你插入一个东西就要改动很多其它内容的位置,特别不方便。在 Heap 上实现的话,就没有这种问题。

    原型与原型链

    对象的这种数据存储方式,还有一个极大的好处,它允许了公用属性的存在,也就是说,我们在 Heap 上存的一个地址,可以别一个或者多个变量引用。

    多个引用.png

    所以,我们可以想象,在对象的使用过程中,每个对象,需要各种各样类似的属性方法,我们只需要把它们都放到一个 heap 中一个共用的对象,然后大家都在那里引用就可以了。JavaScript 确实就是这么做的。

    不过,具体,JS 是如何实现的呢?让我们看看 JS 官方文档中的第一张图:

    object.png

    图中的 CF 是一个“构造函数”,它确实是个函数(函数也属于对象),它有一个 prototype 属性(这个就是我们所说的“原型”),对应着的就是一个 包含各种用于被继承属性的对象地址,图中的 cf1,cf2,cf3,cf4,cf5 通过 proto 属性指向这个对象,这样就实现了继承,可以使用原型中的属性方法了。

    即,每个对象都有一个 proto 属性,指向 构造函数的 prototype 属性;

    即,

    cf1.__proto__ == CF.prototype // true;
    //对象(或“下一层的构造函数”).__proto__ === 函数.prototype
    //cf1 是通过 new CF 产生的。
    

    于是,所谓的“原型”(prototype),就是构造函数的 prototype 属性指向的一个可以被“通过这个构造函数所产生的对象”引用的对象;

    proto 与 prototype 的区别:
    __proto 是对象属性, prototype 是函数属性,不过,我们可以看出,它们存的地址是一样。
    proto 与 prototype 是可以存在于同一个对象的,因为构造函数也属于对象。不过,这个时候,他们所对应的对象地址是不一样的,因为它的 proto 属性指向的是上一层的 prototype;

    所谓的“原型链”就是这么回事:每个对象的 proto 属性都指向它们的上一层,一层接一层,最后到,Function object,再到 null,结束。

    其它数据类型与对象的关系

    按照上面的理论基础,我们就可以理解,一个值,它本身自带的各种属性方法(toString,valueOf)是怎么来的,从一个公用的原型继承来的。
    那么,不同类型属性又有自己独特属性方法,这些是如何来的呢?
    比如,

    let a = "aaa", b = 111;
    a.toString == b.toString // false
    b.toUpperCase // undefined
    

    这是因为, Object 原型在被各种数据继承之前,中间还有一层不同类型数据的原型 String 原型, Number 原型;

    原型链.png

    不过,可能与你想的不太一样,JS 除了对象意外的数据类型并不是直接继承了原型的属性;

    首先,这两个是不一样的东西

    1 // → 数字 1
    new Number(1) // → Number {1} , 这是一个对象
    

    实际上,除了对象以外的这些基本数据类型,它们本身并没有 proto 属性,
    于是,JS 多做了一层工作,但我们想要访问一个数据类型比如 string 的原型属性的时候,JS 会自动帮我们生成一个相应的 new String(),然后把里面的原型拿来用,用完之后就把这个对象消除了。

    所以:

    let a = "abc";
    a.b = "ok"; // 这个b属性会放到自动生成的 new String() 中
    a.b // undefined; 因为上面生成的对象用完清除了,这里是新的,新的不存在 b 属性。
    

    暂时考虑到的就这么多~

    相关文章

      网友评论

          本文标题:对象、原型与原型链原理

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