前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >说说JS中的原型对象和原型链

说说JS中的原型对象和原型链

作者头像
用户6256742
发布2024-07-11 10:02:39
800
发布2024-07-11 10:02:39
举报
文章被收录于专栏:网络日志

理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,

函数对象

俗话说,JS中万物皆对象。函数也是一个对象,只不过函数是在特定环境中执行代码的对象。

什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被当作一个对象来维护,这就是函数对象。JS中声明函数的方式有:

代码语言:javascript
复制
function fn1(){}
var fn2 = function(){}
var fn3 = new Function()

所以可以理解为fn1、fn2、fn3都是函数对象。JS中还包括一些系统内置的函数对象,比如:

代码语言:javascript
复制
Function  Object  Array  String  Number  RegExp

函数对象之外的对象都是普通对象。函数对象能创建普通的对象,反之则不行。

理解原型对象(其实就一普通对象)

1、只有函数对象才拥有原型对象

也即无论什么时候以什么方式创建一个函数(函数对象),都会根据特定的规则为该函数创建一个prototype属性(原型对象的地址的引用),这个属性就是指向该函数的原型对象。比如:

代码语言:javascript
复制
function Person () {};
console.log(Person.prototype) // Person.prototype就是Person的原型对象,实际是原型对象的内存地址的引用
 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

看到没有,原型对象并不神秘,就是一个普通的对象,只不过其默认有了constructor__proto__(下一节会讲)属性而已(其中__proto__不建议在实际中应用,因为在有些浏览器可能并没有实现该属性)。

由上图看出,函数Person的原型对象(Person.prototype)默认拥有一个属性constructor,此属性就是用来重新指向函数Person

代码语言:javascript
复制
function Person () {};
Person.prototype.constructor === Person // true
 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

2、普通对象与原型对象的关系

一般我们定义一个构造函数(构造函数其实就是普通的函数,只不过目的是创建对象),然后通过new操作符来创建一个普通对象。

代码语言:javascript
复制
function Person (name) {
    this.name = name;
    this.age = 18;
}
var xiaoming = new Person('小明'); // {name: '小明', age: 18}
var xiaohong = new Person('小红'); // {name: '小红', age: 18}

在上述代码中,变量xiaomingxiaohong是构造函数Person的实例。我们通过上一节知道了Person与其原型对象的关系,但实例与构造函数的原型对象有什么关系呢?

每当调用构造函数创建一个实例即普通对象后,该实例将包含一个内部的指针[[Prototype]],这个指针指向的就是构造函数的原型对象。

目前ECMAScript的标准中并没有实现标准的访问该指针的方式,但像Firefox、Chrome和Safari等浏览器实现了__proto__属性,此属性就是用来访问指针[[Prototype]],所以可以借用__proto__属性展示实例和原型对象的关系。

代码语言:javascript
复制
xiaoming.__proto__ === Person.prototype // true
xiaohong.__proto__ === Person.prototype // true

3、总结上述两小节

每创建一个函数,就会为相应的函数创建一个prototype的属性,这个属性指向了函数的原型对象,这个函数的原型对象会默认拥有一个constructor属性,此属性指向了对应的函数。而使用new操作符调用函数创建出来的实例,会拥有一个内部的指针[[Prototype]],此指针指向函数的原型对象。

千言万语不如一幅图:

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

原型链

由上节我们可以知道,原型对象上的属性和方法被所有实例所共享的。每当访问一个对象的属性或者方法时,会首先搜索对象自身,如果找到了此属性或者方法,则直接返回,否则向对应的原型对象上面搜索,如果找到则直接返回,否则继续向原型对象的原型对象上查找,直到搜索到null,抛出错误或返回undefined

代码语言:javascript
复制
function Person (name) {
    this.name = name;
    this.age = 18;
}
Person.prototype.sayName () { // 在Person的原型对象上添加的方法,被所有实例共享
    console.log(this.name);
}
var xiaoming = new Person('小明'); // {name: '小明', age: 18}
xiaoming.sayName(); // 小明

上面代码中,实例xiaoming本身并没有sayName方法,但却成功调用了。 其实就是通过实例内部的[[Prototype]]指针去原型对象Person.prototype 上找对应的方法,然后调用。

如果我调用一个实例本身和原型对象都没有的方法,其过程是怎么样的呢?

代码语言:javascript
复制
xiaoming.sayAge() // 实例本身和原型对象都不存在的方法

(1)首先搜索xiaoming这个对象,并没有sayAge方法,

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

(2)继续向原型对象搜索(通过内部的[[Prototype]]指针)。没有找到sayAge方法

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

(3)继续向原型对象的原型对象上搜索,即xiaoming.__proto__.__proto__。也没有找到sayAge方法。

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

(4)继续向原型对象的原型对象的原型对象上搜索,即xiaoming.__proto__.__proto__.__proto__,但发现xiaoming.__proto__.__proto__.__proto__null,停止搜索,抛出错误或返回undefined

如果原型对象和实例上具有同名的属性或方法,则搜索时取最近的。

如上述的原型链的搜索机制,你通过阅读本文知道xiaoming.__proto__Person.prototype,但xiaoming.__proto__.__proto__呢? 不说话看图:

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

由此,可得到下面的关系图:

 说说JS中的原型对象和原型链
说说JS中的原型对象和原型链

思考

原型链中的关系图其实还缺少一环,就是内置函数FunctionFunction比较特殊,有兴趣的可以去研究下FunctionObject的关系。

本文是笔者对原型对象和原型链的理解,如有错误或不足的地方,欢迎指正。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数对象
  • 理解原型对象(其实就一普通对象)
    • 1、只有函数对象才拥有原型对象
      • 2、普通对象与原型对象的关系
        • 3、总结上述两小节
        • 原型链
        • 思考
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档