美文网首页
原型与原型链相关

原型与原型链相关

作者: 你喜欢吃青椒吗_c744 | 来源:发表于2019-08-02 11:51 被阅读0次

    前言

    最近在整理原生JS的相关知识,发现关于原型和原型链涉及的知识对理解JS有很大的帮助。遂分享一下。

    构造函数

    所谓构造函数,就是提供了一个生成对象的模板并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。总的来说,构造函数就是对象的模板,对象就是构造函数的实例。

    • 构造函数的首字母必须大写,用来区分于普通函数
    • 内部使用的this对象,来指向即将要生成的实例对象
    • 使用New来生成实例对象

    构造函数的缺点

    所有的实例对象都可以继承构造函数中的属性和方法。但是,同一个对象实例之间,无法共享属性

    function Person(name,height){
     this.name=name;
     this.height=height;
     this.hobby=function(){
     return 'watching movies';
    }
     }
    var boy=new Person('keith',180);
     var girl=new Person('rascal',153);
     console.log(boy.name); //'keith'
     console.log(girl.name); //'rascal'
     console.log(boy.hobby===girl.hobby); //false
    

    上面代码中,一个构造函数Person生成了两个对象实例boy和girl,并且有两个属性和一个方法。但是,它们的hobby方法是不一样的。也就是说,每当你使用new来调用构造函数放回一个对象实例的时候,都会创建一个hobby方法。这既没有必要,又浪费资源,完全可以被两个对象实例共享。
    所以,构造函数的缺点就是:同一个构造函数的对象实例之间无法共享属性或方法

    prototype解决这个问题

    为了解决构造函数的对象实例之间无法共享属性的缺点,js提供了prototype属性。

    js中每个数据类型都是对象(除了null和undefined),而每个对象都继承自另外一个对象,后者称为“原型”(prototype)对象,只有null除外,它没有自己的原型对象。

    原型对象上的所有属性和方法,都会被对象实例所共享。

    function Person(name,height){
    this.name=name;
    this.height=height;
    }
    Person.prototype.hobby=function(){
    return 'watching movies';
    }
    var boy=new Person('keith',180);
    var girl=new Person('rascal',153);
    console.log(boy.name); //'keith'
    console.log(girl.name); //'rascal'
    console.log(boy.hobby===girl.hobby); //true
    //将hobby方法放在原型对象上,那么两个实例对象都共享着同一个方法。
    

    上面代码中,当修改了原型对象的hobby方法之后,两个对象实例都发生了变化。这是因为对象实例其实是没有hobby方法,都是读取原型对象的hobby方法。也就是说,当某个对象实例没有该属性和方法时,就会到原型对象上去查找。如果实例对象自身有某个属性或方法,就不会去原型对象上查找

    boy.hobby=function(){
     return 'play basketball';
     }
     console.log(boy.hobby()); //'play basketball'
     console.log(girl.hobby()); //'swimming'
    //上面代码中,boy对象实例的hobby方法修改时,就不会在继承原型对象上的hobby方法了。不过girl仍然会继承原型对象的方法。
    

    总结:

    • 原型对象的作用,就是定义所有对象实例所共享的属性和方法
    • prototype,对于构造函数来说,它是一个属性;对于对象实例来说,它是一个原型对象。

    原型链

    对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型对象本身对于对象实例来说也是对象,它也有自己的原型,所以形成了一条原型链(prototype chain)。

    比如,a对象是b对象的原型,b对象是c对象的原型,
    以此类推。所有一切的对象的原型顶端,都是Object.prototype,
    即Object构造函数的prototype属性指向的那个对象。
    

    当然,Object.prototype对象也有自己的原型对象,那就是没有任何属性和方法的null对象,而null对象没有自己的原型。即Object.prototype = null

    • 读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
    • 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。
    • 一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

    constructor

    prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

    由于constructor属性是定义在原型(prototype)对象上面,意味着可以被所有实例对象继承。

    function A(){};
    var a=new A();
    console.log(a.constructor); //A()
    console.log(a.constructor===A.prototype.constructor);//true
    //之所以可以a.constructor是因为A的原型对象有这个属性;
    //而a会继承原型对象的属性和方法,
    //所以,a才可以调用constructor属性。
    

    constructor属性的作用

    • 分辨原型对象到底属于哪个构造函数
    function A(){};
    var a=new A();
    console.log(a.constructor===A) //true
    console.log(a.constructor===Array) //false
    

    instanceof运算符

    instanceof运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。

    因为instanceof对整个原型链上的对象都有效,所以同一个实例对象,可能会对多个构造函数都返回true。

    注意,instanceof对象只能用于复杂数据类型(数组,对象等),不能用于简单数据类型(布尔值,数字,字符串等)。此外,null和undefined都不是对象,所以instanceof 总是返回false。

    _ proto _

    JS的万物都有__proto__属性。用于指向创建它的构造函数的原型对象。

    为什么不用prototype指向原型?
    因为prototype属性只有函数对象才拥有。

    image.png
    //创建一个构造函数
    function People (name) {
      this.name = name;
    }
    
    //实例化一个构造函数
    let g1 = new People('蜡笔小新')
    

    那么,则会有:

    People.prototype === g1.__proto__
    
    《JavaScript 高级程序设计》的图 6-1

    [[Prototype]] 其实就是__proto__。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox,Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本是完全不可见的。

    Object和Function

    Object对象体系下:
    一切对象的原型链最终都是.... → Object.prototype → null。例如定义一个num变量var num = 1,则num的原型链为x → Number.prototype → Object.prototype → null; 定义一个函数对象fnfunction fn() {},则fn的原型链为fn → Function.prototype → Object.prototype → null;等等...

    原型链

    Function体系下:
    不考虑null的情况下(其中Object.prototype.__proto__ === null),Object.prototype为原型链的顶端,Function.prototype继承Object.prototype而产生,最后,FunctionObject和其它构造函数继承Function.prototype而产生。

    原型链
    注意:Function.prototype === Function.__proto__true

    JS万物诞生记

    https://img.haomeiwen.com/i17237086/1333d91c5d466cea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

    总结

    • 同一个对象实例之间无法共享属性。即实例1和实例2的属性不能共享。
    • 构造函数的prototype属性叫做原型对象。(只有函数对象才有 prototype 属性)
    • 原型对象上的所有属性和方法,都会被对象实例所共享。
    • 原型链:读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
    • 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。
    • 一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。
    • prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
    • 由于constructor属性是定义在原型(prototype)对象上面,意味着可以被所有实例对象继承。
    • instanceof对象只能用于判断复杂数据类型(数组,对象等),不能用于简单数据类型(布尔值,数字,字符串等)
    • JS的万物都有proto属性。用于指向创建它的构造函数的原型对象。

    参考文章

    [详解Javascript中prototype属性(推荐)]

    最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)
    最详尽的 JS 原型与原型链终极详解,没有「可能是」。(二)
    最详尽的 JS 原型与原型链终极详解,没有「可能是」。(三)
    彻底搞懂Object和Function的关系

    由一段代码引发的关于Object和Function的鸡和蛋问题的思考

    JavaScript 世界万物诞生记

    相关文章

      网友评论

          本文标题:原型与原型链相关

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