原型链浅析

88 阅读3分钟

前言: 关于原型对象的概念我在上一篇文章里已经提到了,所以,原型对象的有关知识我就不再过多的赘述了。首先来看看什么是原型链,即原型链的概念是什么

原型链介绍

  • 每一个对象都有原型,原型本身又是对象,所以原型又有原型,以此类推形成一个链式结构,称为原型链。原型链的终点是null
  • 那么对象是怎么访问原型链中的成员的呢?对象访问原型链中的成员遵循的是就近原则。对象先访问自己的,自己没有就找原型的,原型没有就找原型的原型,一直到原型链终点null,如果还找不到。属性则获取undefined,方法则会报错:xxx is not a function

image.png

 //1.构造函数
       function Person(name,age){
           this.name = name
           this.age = age
        //    this.girlFriend = ''//如果自己有girlFriend,优先访问自己的
       }
       
  //2.原型对象 : 存储具有共同特征的数据
  Person.prototype.type = '哺乳动物'
  Person.prototype.country = '中国'
  Person.prototype.playGame = function(){
           console.log(this.name + '玩游戏')
       }
      // 实例对象
       let p1 = new Person('小熊'18)
       let p2 = new Person('快跑'18)
       console.log(p1)

       /* 小测试 */
       console.log( p1.name )//小熊  p1自己有name属性
       console.log( p1.age )//18   p1自己有age
       console.log( p1.type )//哺乳动物  p1自己没有type,但是p1的原型有
       console.log( p1.girlFrined )//undefined  p1自己没有girlFrined, p1的原型也没有girlFrined   
       
            /* 思考:  p1自己没有toString, p1的原型也没有toString, 但是为什么不报错呢?
      原因:  p1的原型的原型有toString
      */ 
       p1.toString()
       
       
       //查看p1的原型
       console.log(p1.__proto__.constractor)//Person
       console.log(Person.prototype === p1.__proto__)//true
       //查看p1原型的原型
       console.log(p1.__proto__.__proto__.constractor)//object
       console.log(object.prototype === p1.__proto__.__proto__)//true
       //查看p1的原型的原型的原型
       console.log(p1.__proto__.__proto__.__proto__)//null
       

内置对象的原型链

  • 构造函数的原型本身是一个对象,只要是对象就有原型
  • 所有的内置对象的原型链都是一样的,最终指向Object
  • 只有对象才有原型,这里有一个地方容易搞混淆,那就是基本数据类型string、number、boolean、和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
Array的原型链
// 数组对象  
        //实例化对象
        let arr = [10,20,30]//这里相当于 new Array(10,20,30)
        console.log( arr )
        //1.1 查看arr的原型
        console.log( arr.__proto__.constructor )//Array
        console.log( arr.__proto__ === Array.prototype )//true
        //1.2 查看arr的原型的原型
        console.log( arr.__proto__.__proto__.constructor )//Object
        console.log( arr.__proto__.__proto__ === Object.prototype )//true

image.png

  • 那么来思考这样一个问题,为什么arr.toString()方法和对象的toString方法得到的结果不同?

    数组的toString()方法是把数组每个元素以,分割并以字符串的形式返回;对象的toString()方法返回的是特定类型[object 类型]。原因是实例对象arr的原型对象Array.prototype中有默认的toString()方法,找到原型对象Array.prototype中默认的toString()方法后就不会往上找了;而其他的实例对象的原型中没有默认的toString()方法,就会往上找,找到原型的原型,object.prototype中有toString()方法,就把这个object.prototype中toString()方法拿来用了,这两个方法虽然名字一样,但是并不一样。

Date的原型链
// 日期对象
        let date = new Date()
        /* js有几个特殊的对象 无法使用 log来打印的,需要用dir来打印: function date dom对象 */
        console.dir(date)
        //3.1 查看date的原型
        console.log(date.__proto__.constructor) //Date
        console.log(date.__proto__ === Date.prototype) //true
        //3.2 查看date的原型的原型
        console.log(date.__proto__.__proto__.constructor) //Object
        console.log(date.__proto__.__proto__ === Object.prototype) //true

image.png

String对象原型链
let str = new String('abc')
        console.log(str)
        //2.1 查看str的原型
        console.log(str.__proto__.constructor) //String
        console.log(str.__proto__ === String.prototype) //true
        //2.2 查看arr的原型的原型
        console.log(str.__proto__.__proto__.constructor) //Object
        console.log(str.__proto__.__proto__ === Object.prototype) //true

image.png

补充一个关于原型链的运算符

instanceof运算符

  • 语法:对象 instanceof 构造函数
  • 作用:检测构造函数的原型prototype在不在这个对象的原型链上 举例:
let arr = [10,20,30]
        // arr-> Array.prototype -> Object.prototype -> null
        console.log( arr instanceof Array )//true
        console.log( arr instanceof Object )//true
        console.log( arr instanceof String )//false
  • 应用场景:某些函数为了限制你的数据类型,在内部需要instanceof进行判断是否正确的数据类型
        function fn(arr){
            if( arr instanceof Array){
                console.log( arr.reverse() )
            }else{
                console.log('数据类型错误')
            }
        }

        fn( [10,20,30] )
        fn( 'abc' )