原型与原型链——JS

本文深入探讨了JavaScript中的原型和原型链概念,包括函数的prototype属性、原型对象的作用、显示原型与隐式原型的区别、原型链的查找机制以及实例对象的属性访问。此外,还介绍了原型继承和instanceof操作符的工作原理,并通过示例解析了原型继承的常见问题。最后,文章总结了原型链的关键点,强调了其在对象属性查找和继承中的重要性。
摘要由CSDN通过智能技术生成

原型对象

原型对象获取:函数的prototype属性

  • 每个函数都有一个prototype属性,它默认指向一个Object空对象,即称为:原型对象
    在这里插入图片描述

eg:

        function fun (){  
        }
        console.log(fun.prototype,typeof fun.prototype)

在这里插入图片描述
虽然prototype属性默认指向一个Object空对象,没有属性和方法,但是原型还有原型,那里面有共有的属性和方法,后面会讲解。

给原型对象添加属性(一般都是方法)

可以向prototype指向的Object空对象中添加方法和属性

        function fun (){
           
        }
        fun.prototype.test = function (){
           console.log('我是添加的函数')
        }
         console.log(fun.prototype,typeof fun.prototype)

在这里插入图片描述
eg:Date的原型

console.log(Date.prototype,typeof Date.prototype)

在这里插入图片描述

原型对象和函数对象的关系

原型对象中有一个属性constructor它指向 函数对象

       function fun (){  
        }
        console.log(fun.prototype.constructor === fun)
        console.log(Date.prototype.constructor === Date)

在这里插入图片描述
所以原型对象和函数对象可以通过自己的属性找到对方:
在这里插入图片描述
prototype属性 和 constructor 属性一一对应
函数对象.prototype = 原型对象
原型对象.constructor = 函数对象

如果没有prototype属性就不会有对应的constructor属性
eg:末尾的实例对象没有prototype 属性,自然也不会有对应的constructor指向它。

原型对象的作用

原型对象一般用于构造函数。
原因:构造函数可以创建实例,而所有实例对象自动拥有该构造函数原型中的属性和方法,这就相当于所有实例的公共区域,所以使用原型对象可以很方便地为所有实例添加共有的属性和方法。
eg:

        function Fun (){  
        }
        Fun.prototype.test = function (){
           console.log("我是共有的方法")
        }
        var fun =new Fun()
        fun.test()

输出:

我是共有的方法

显示原型与隐式原型

基本含义

  • 显式原型:每个(构造)函数function都有一个prototype,即显式原型(属性),默认指向一个空的0bject对象

  • 隐式原型:每个实例对象都有一个__proto__,可称为隐式原型(属性)

  • 显示原型与隐式原型的关系
    实例对象的隐式原型 = 构造函数的显式原型
    即: 实例对象.__proto__ = 构造函数.prototype
    __proto__是实例对象的属性,prototype是构造函数的属性。

  • 区分:
    函数的prototype属性:在定义函数时自动添加的,默认值是一个空object对象。
    对应的内部语句是:this.prototype = {}
    对象的_proto_属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
    对应的内部语句是:this.__proto__ = Fn.prototype

function Fn (){  
}
// 1.每个函数function都有一个prototype,即显式原型(属性),默认指向一个空的0bject对象
console.log(Fn.prototype)
// 2.每个实例对象都有一个__proto__,可称为隐式原型(属性)
var fn = new Fn()
console.log(fn.__proto__)
// 3.对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__)

输出:
在这里插入图片描述

内部结构

在这里插入图片描述

通过实例调用原型的方法

当我们使用一个对象的属性或方法时,会先在自身中寻找该属性或方法,自身中如果有,则直接使用,如果没有则去原型对象中寻找。
所以如果通过函数的prototype属性为原型对象添加的属性或方法,可以通过实例对象直接访问。
eg:

        function Fn (){  
        }
        var fn = new Fn()
        Fn.prototype.test  =function(){
            console.log("hello")
        }
        fn.test();

输出:

hello

注意: 程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)
所以我们经常使用 显式原型 添加属性 ,使用 隐式原型 访问属性

原型链

我们一直说函数的原型是一个Object空对象,从名字可以看出它是一个Object
构造函数的实例。
既然是实例就应该有隐式原型__proto__属性,也有对应的构造函数Object,同时该构造函数有对应的原型对象。

图解:

在这里插入图片描述
原型链的尽头就是Object的原型对象。
始终遵守实例对象的隐式原型 = 构造函数的显式原型

根据原型链查找对象属性

原型链的作用:查找对象的属性或方法
而查找对象的属性或方法的时候一般都是使用隐式原型链(__proto__)进行查找,所以原型链一般又叫做隐式是原型链。

对象属性的查找过程

  • 先在自身属性中查找,找到返回
  • 如果没有,再沿着_proto__这条链向上查找,找到返回
  • 知道查找到Object的原型对象,如果最终没找到,返回undefined

函数的原型链——带图解

前面我们以构造函数为原型链的开头,但是构造函数也是通过new得来的:
即:

       function fun(){
       }

相当于

       var fun = new function(){ 
       }

如果这样看的话 Function() 应该是fun的构造函数,所以fun函数也应该有原型函数。
所以原型链是如下:
在这里插入图片描述
按照上面说的,你是不是会想那 Function()还有没有构造函数呢,它的__proto__再指向它的构造函数的原型对象。答案是有的,Function还有构造函数,但是它的构造函数是他自己,即
Function = new Function()
再根据 实例对象的隐式原型 = 构造函数的显式原型 原则,那么 Function 的__proto__prototype指向同一个原型对象:

在这里插入图片描述
任何函数都是通过 new Function() 创建的,无论是内置的函数还是我们自己创建的函数,所以Object()函数也是由new Function() 创建的,它的__proto__属性指向Function原型函数。
在这里插入图片描述
Function原型对象又是 Object函数 的实例,验证:

    // A instanceof B:就是验证 A是不是B的实例
       console.log(Function.prototype instanceof Object)

输出:
在这里插入图片描述
所以Function原型函数的 __proto__属性 指向 Object的原型对象
在这里插入图片描述

也正式这样使得函数有许多从 Object圆形函数中继承来的方法、属性。所以通过函数名.属性/方法 就可以调用相应的属性/方法。

       function Fn (){  
       }
       console.log(Fn.toString())

输出:
在这里插入图片描述
所以说除去底端实例所有的函数都有两个属性,一个是 __proto__,一个是prototype,这是由于这个函数既是构造函数同时又是另一个构造函数的实例所致。
prototype指向自己的原型对象,__proto__指向构造函数的原型对象。

原型链小结

  • 所有函数的显示原型指向的对象默认是空object实例对象,包括Function。
    但Object不满足,Object的显示原型是自己的原型对象。

空object实例是Object的实例对象,所以除Object外的函数的显示原型对象的隐式原型对象才是Object原型对象。

       console.log(Fn.prototype instanceof Object)
       console.log(Function.prototype instanceof Object)
       console.log(Object.prototype instanceof Object)

输出:
在这里插入图片描述

  • 所有函数都是Function的实例(包含Function)

即:所有函数的构造函数是一样的,所以所有函数的隐式原型对象__proto__都是一样的,都指向Function的原型对象。

  • 并且Object原型对象是原型链的终点,因为Object的原型对象的 __proto__ == null。

原型继承

构造函数的实例对象自动拥有构造函数原型对象的属性(方法),这就是利用原型链实现的原型继承。

原型——属性问题

如果在书写的时候直接写对象.属性

  • 读取对象的属性值时:会自动到原型链中查找,从内向外逐步查找。
  • 设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接将该属性添加到该对象上。

eg:

       function Fn (){  
       }
       Fn.prototype.a='xxx'
       var fn1 = new Fn()
       var fn2 = new Fn()
       fn2.a = 'yyy'
       console.log(fn1.a)
       console.log(fn2.a)

输出:
在这里插入图片描述

  • 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
    因为不同对象的方法可能相同,但是属性一般不一致。
    eg:
       function Person (name,age){
          this.name = name
          this.age = age
       }
       var p1 = new Person("小李",21)
       var p2 = new Person("小张",21)
       Person.prototype.setName = function (name){
           this.name = name;
       }
       p1.setName("小王")
       p2.setName("小刘")
       console.log(p1.name,p2.name)
       console.log(p1)

在这里插入图片描述

instanceof 的原型理解

instanceof 的判断原理

表达式: A instanceof B
如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false。
这里的原型链指的是隐式原型链。

即判断的方法找到B的显示原型,然后找A的__proto__链上是否有该显示原型,有就返回true,否则返回false。

例1

  function Foo (){
    }
    var f1 = new Foo()
    console.log(f1 instanceof Foo)
    console.log(f1 instanceof Object)

在这里插入图片描述
为什么说 f1 是 Object 的实例呢,看图
在这里插入图片描述
Foo和Object的显式原型对象都在A的原型链上,所以都返回true。

例2

        console.log(Object instanceof Function)
        console.log(Object instanceof Object)
        console.log(Function instanceof Function)
        console.log(Function instanceof Object)

        function Fn(){}
        console.log(Object instanceof Fn)

输出:
在这里插入图片描述
就是根据原型链图进行查找,这里以Object instanceof Object为例:
在这里插入图片描述
Object的原型对象在Object的原型链上,所以返回true。

面试题

题1

        function A (){
           
        }
        A.prototype.n = 1

        var b =new A()
        A.prototype = {
            n:2,
            m:3
        }

        var c = new A()
        console.log(b.n,b.m,c.n,c.m)

输出:
在这里插入图片描述
分析:
在这里插入图片描述

题2

       var F = function (){
          
       }
       Object.prototype.a = function (){
          console.log('a()')
       }
       Function.prototype.b = function (){
          console.log('b()')
       }
       var f = new F()

       f.a()//a()
       f.b//undefine
       F.a()//a()
       F.b()//b()

F为例:
在这里插入图片描述
可以看到F函数既可以找到A,又可以找到B。

补充

两种报错方式

  • 查找变量,没有变量就报错 —— 作用域链
  • 查找对象,没有对象就返回undefined —— 原型链

继承

  • js是基于对象继承的
  • 而像Java一类的语言是基于类执行的
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值