一文带你搞懂JavaScript中的原型和原型链

 更新时间:2023年08月28日 08:25:00   作者:LBruse  
JavaScript是基于原型继承的语言,每个对象都有一个原型(prototype),本文则是重点对prototype相关知识点做拆解和梳理,感兴趣的可以了解下

原型和原型链

JavaScriptJava这种面向对象的语言不太一样,JavaScript基于原型继承的语言。虽然在ES6及之后,classextend语法也渐渐取代了之前修改prototype实现继承的方式,但本质上还是通过修改prototype来实现继承的。本文则是重点对prototype相关知识点做拆解和梳理

通过class声明并实例化对象

java中声明并实例化对象是这样的

package geek.springboot.application.entity;
public class WechatUser {
    private String name;
    private String openId;
    private String avatar;
    public WechatUser(String name, String openId, String avatar) {
        this.name = name;
        this.openId = openId;
        this.avatar = avatar;
    }
    // 打印输出当前微信用户信息
    public void print() {
        System.out.println("name: " + this.name + " openId: " + this.openId + " avatar: " + this.avatar);
    }
    // java程序启动入口
    public static void main(String[] args) {
        WechatUser user = new WechatUser("Bruse", "opwrogfajadfoa113", "avatar-1.png");
        user.print();
    }
}

JavaScript实例化对象是这样的

class WechatUser {
    constructor(name, openId, avatar) {
        this.name = name
        this.openId = openId
        this.avatar = avatar
    }
    print(){
        console.log(`name: ${this.name} openId:${this.openId} avatar: ${this.avatar}`)
    }
}
const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg')
user.print()

输出name: Bruse openId:sfoqioiooa1 avatar: avatar-1.jpg

从语法上看两者差别并不大,class定义对象模板,定义了对象该有的属性和方法,然后通过new关键字将对象进行实例化

extend继承

通过extend便可让不同的class实现继承关系,达到代码复用的效果

class MiniProgramUser extends WechatUser {
    constructor(name, openId, avatar, appId) {
        // 调用父类的构造函数
        super(name, openId, avatar);
        this.appId = appId
    }
    // 重写父类方法
    print() {
        console.log(`name: ${this.name} openId:${this.openId} avatar: ${this.avatar} appId: ${this.appId}`)
    }
}

输出name: Bruse openId:sfoqioiooa1 avatar: avatar-1.jpg appId: appId13322

原型

以上示例演示了如何用class进行声明和实例化对象,但其实class只不过是所谓的语法糖,本质上JavaScript并不会像Java那样基于类实现面向对象。

class WechatUser{}

实际上也还是个函数,class只是个语法糖,它等同于

function WechatUser() {}

隐式原型和显式原型

隐式原型

const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg')
console.log('user.__proto__ ', user.__proto__)

以上边的代码为例,其实在创建出来的user对象中,有一个__protocol__属性,这个即每个实例都有的隐式原型,打印输出如下

显式原型

console.log('WechatUser.prototype ',WechatUser.prototype)

输出WechatUserprototype属性,prototype原型的意思,每个class(function)都有显式原型,结果如下

隐式原型和显式原型的关系

可以看到无论是user.__proto__还是WechatUser.prototype,都有print方法,constructor都是WechatUser,那么是否也就意味着user.__proto__[实例的隐式原型]===WechatUser.prototype[class的显式原型]

console.log('equals ', user.__proto__ === WechatUser.prototype)

输出为equals true,证明user.__proto__的确等于WechatUser.prototype,引用地址是同一个。

这里的关系可以用下图表示

  • 每个class都有显式原型prototype
  • 每个实例都有隐式原型__proto__
  • 实例的__proto__指向其所对应的class的prototype

基于原型的属性/方法查找

基于上边的内容,其实可以总结出:获取实例属性或执行方法时,会先在实例自身进行寻找有没有相关的属性或方法,有的话就获取或调用,没有的话,会顺着实例的__proto__往上找到实例对应的class的prototype,并对prototype进行变量查找或方法调用。这也就是所谓的基于原型的继承方式

原型链

搞明白了基于原型是怎么回事后,那接下来就是多个原型之间的关联形成原型链

const miniUser = new MiniProgramUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg', "appId13322")

这里声明一个miniUser,它是基于MiniProgramUser实例化的,所以miniUser.__proto__相等于MiniProgramUser.prototype

但其实MiniProgramUser.prototype也有一个__proto__属性,输出如下

miniUser的隐式原型等于MiniProgramUser的显式原型,可MiniProgramUser的显式原型的隐式原型(是有点绕)又等于谁呢?

因为定义MiniProgramUser这个class的时候,使用了extend关键词,表示其继承于WechatUser,而且WechatUser也有自己的prototype,输出如下

那么尝试将MiniProgramUser.prototype.__proto__WechatUser.prototype比较,结果如下

console.log('equals',  MiniProgramUser.prototype.__proto__ === WechatUser.prototype)

输出equals true,证明MiniProgramUser显式原型隐式原型等于WechatUser的显示原型,隐约间形成了一条原型链

原型链的尽头

那么在这里其实也可以做一个举一反三,既然每个classprototype都会有一个__proto__,既然WechatUser这个class并没有在代码中显式指定继承于哪个class,那么WechatUser.prototype.__proto__应该就等同于Object.prototype,输出验证如下

console.log(WechatUser.prototype.__proto__ === Object.prototype)

结果为true

这里也有一个知识点,因为ObjectJavaScript所有对象中是最顶级的存在了,所以虽然Objectprototype也有__proto__,但它实际上不指向任何对象,仅为null

console.log('Object.prototype.__proto__ ', Object.prototype.__proto__)

输出 Object.prototype.__proto__ null

原型链总结

这里可以用一张图清楚表示形成的原型链是怎么样的

typeof vs instanceof

typeof

typeof是用来判断当前变量是何种类型?基本类型?引用类型?也就是说它能

  • 识别所有值类型
  • 识别函数
  • 判断是否引用类型,但只能判断出为object,没法再细分

判断值类型

const name = 'Bruse'   typeof name // 输出'string'
const sym = Symbol('sym')   typeof sym // 输出'symbol'
const done = false  typeof done // 输出'boolean'

识别函数

typeof console.log   // 'function'
function print () { console.log(1+1) }
typeof print    // 'function'

引用类型则非常笼统地识别为object

typeof null  // 'object'
typeof [1,2,3] // 'object'
typeof {name: 'Bruse', age: 16}  // 'object'

instanceof

instanceof也是用作类型判断的,只不过比typeof更精准了,它可以判断出当前变量是否该class构建出来的。

[]  instanceof Array  // true
[]  instanceof Object // true
{}  instanceof Object // true
miniUser instanceof MiniProgramUser // true
miniUser instanceof WechatUser // true
miniUser instanceof Object // true

结合出上边原型链的知识,其实可以搞清楚instanceof的原理,其实就是根据instanceof左边变量miniUser的原型链一层层往上找,判断prototype__proto__是否等于instanceof右边的class WechatUserprototype

到此这篇关于一文带你搞懂JavaScript中的原型和原型链的文章就介绍到这了,更多相关JavaScript原型和原型链内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JS 去除Array中的null值示例代码

    JS 去除Array中的null值示例代码

    去除Array中的null值,反复测试个不错的方法可以完美去除,在此与大家分享下,需要的朋友不要错过
    2013-11-11
  • 深入理解JavaScript中的预解析

    深入理解JavaScript中的预解析

    JavaScript有“预解析”行为。理解这一特性是很重要的,不然在实际开发中你可能会遇到很多无从解析的问题,甚至导致程序bug的存在。下面这篇文章就给大家详细介绍了JavaScript中的预解析,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2017-01-01
  • 使用canvas及js简单生成验证码方法

    使用canvas及js简单生成验证码方法

    在很多时候都需要用到验证码,前端验证码需要知道Html5中的canvas知识点。验证码生成步骤是:1.生成一张画布canvas 2.生成随机数验证码 3.在画布中生成干扰线 4.把验证码文本填充到画布中 5.点击画布更换验证码
    2017-04-04
  • 小程序二次贝塞尔曲线实现购物车商品曲线飞入效果

    小程序二次贝塞尔曲线实现购物车商品曲线飞入效果

    这篇文章主要介绍了小程序二次贝塞尔曲线实现购物车商品曲线飞入效果,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • 解决layer.open后laydate失效的问题

    解决layer.open后laydate失效的问题

    今天小编就为大家分享一篇解决layer.open后laydate失效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • uniapp小程序使用高德地图api实现路线规划的示例代码

    uniapp小程序使用高德地图api实现路线规划的示例代码

    路线规划常用于出行路线的提前预览,我们提供4种类型的路线规划,分别为:驾车、步行、公交和骑行,满足各种的出行场景,这篇文章主要介绍了uniapp小程序使用高德地图api实现路线规划,需要的朋友可以参考下
    2023-01-01
  • 完美实现js焦点轮播效果(一)

    完美实现js焦点轮播效果(一)

    这篇文章主要为大家详细介绍了完美实现js焦点轮播效果的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03
  • javascript判断网页是关闭还是刷新

    javascript判断网页是关闭还是刷新

    本篇文章给大家介绍js判断网页是关闭还是刷新,实现原理就是通过离开页面行为时间onunload触发时间去检测此时的浏览器的窗口大小,根据大小由此判断用户是刷新,跳转或是关闭行为程序,需要的朋友可以参考下本文
    2015-09-09
  • 详解JS中常用的Fetch API

    详解JS中常用的Fetch API

    Fetch API是一种用于进行网络请求的现代JavaScript API,提供了更简洁、强大和灵活的方式来处理异步数据交互,本文主要为大家介绍了js中js中基本用法,感兴趣的同学可以参考下
    2023-07-07
  • JS中的事件委托实例浅析

    JS中的事件委托实例浅析

    这篇文章主要介绍了JS中的事件委托,结合实例形式简单分析了javascript事件委托的概念、功能、使用方法及相关注意事项,需要的朋友可以参考下
    2018-03-03

最新评论