【面试】网易:所有的对象最终都会继承自Object.prototype 吗?搞懂原型原来这么简单!!

前言

【干货】网易:所有的对象最终都会继承自Object.prototype 吗?

你知道什么是原型(Prototype)吗?

你知道显式原型和隐式原型吗?

你知道如何对原型进行增删改查吗?

你知道原型链吗?

正文

今天,我们就来搞清楚原型(Prototype),如何对原型进行增删改查

以及学习什么是显式原型,隐式原型和原型链

什么是原型(Prototype)?

在JavaScript中,原型(Prototype)是一种实现面向对象编程(OOP)的技术。它是JavaScript中对象(Object)的一个重要特性。每个JavaScript对象都有一个原型对象,这个原型对象定义了这个对象的属性和方法。

当我们在JavaScript中创建一个对象时,这个对象会从其原型对象继承属性和方法。这意味着,如果我们修改了对象的原型,那么这个对象就会继承这个修改。同样地,如果我们添加新的属性和方法到对象的原型,那么所有使用这个原型的对象都会拥有这个新的属性和方法。

原型链是JavaScript中对象继承特性的实现方式。当试图访问一个对象的属性时,如果这个对象自身没有这个属性,那么JavaScript就会在对象的原型(也就是它的[[Prototype]]属性)中寻找这个属性,如果原型中也没有,那么就会继续在原型的原型中寻找,以此类推,直到找到这个属性或者达到原型链的尽头。

我们来看这样一个案例吧

js 复制代码
Person.prototype.say=function(){return '你好'}
function Person()
{
    this.name = '肖总'
    this.age =18
}
let p = new Person()
let p2 = new Person()
console.log(p2.say==p.say)
js 复制代码
输出:true

为什么这里是输出这样的结果勒?

在这里我们就得学习一下New的执行原理,这一节我在:[干货]你真的了解JavaScript中的封装类吗?你知道New的执行原理吗?你知道原始数据的隐式包装类吗? - 掘金 (juejin.cn)有学习过!

今天我们就在补充一遍:

NEW的运行机制

在JavaScript中,new关键字用于创建一个用户定义的对象类型的实例或具有构造函数的内建对象的实例。当在JavaScript中使用new关键字创建一个对象时,会执行以下的步骤:

  1. 创建一个新的空对象:首先,new操作符会在内存中创建一个新的空对象。这个对象是使用JavaScript中的对象字面量语法创建的,其属性均为undefined
  2. 设置原型链:新创建的对象的[[Prototype]](即原型)会被设置为构造函数的prototype对象。这意味着新对象可以访问构造函数原型上的属性和方法。
  3. 构造函数执行:之后,构造函数会使用这个新创建的对象作为其上下文被执行。换句话说,this关键字在构造函数内部指向这个新创建的对象。
  4. 返回新对象:如果构造函数没有显式返回一个对象,那么会自动返回新创建的对象。如果构造函数返回了一个非原始类型(即对象或者函数),那么这个返回值会被作为整个new表达式的返回值。如果返回的是原始类型(例如:Number、String、Boolean、Null、Undefined),则忽略这个返回值,依然返回新创建的对象。

所以在上述的案例当中,p1p2访问的都是构造函数的原型prototypesay属性,所以返回结果就是true

接下来,我们再看这个案例:

js 复制代码
function Car(owner,color){
    this.owner = owner
    this.color = color
}
Car.prototype={
    name:'春风',
    lang:'4900',
    height:'1400'
}//这样设置的对象是后天你生成的,与先天的还是有点区别
var car1 = new Car('浪鸽','green')
var car2 = new Car('犄角','pink')
car1.name = '宝马'
console.log(car2.name)
console.log(car1)

我们来看看结果:

js 复制代码
输出:
春风
{ owner: '浪鸽', color: 'green', name: '宝马' }

分析为什么输出这样的结果。

重点重点

car1car2Car的两个实例对象,因此我们能通过这两个实例对象去访问Car原型中的属性,并且,我们通过实例对象是修改不了原型中数据的 。我们在代码中尝试通过car1.name = '宝马'去修改原型中的name属性,输出car2.name发现原型中的属性没有被修改。我输出car1对象时,发现其对象体中添加了一个新属性name: '宝马',所以,我们可以知道:实例对象没有修改原型数据的权限,只有查看数据的权限!

实例对象与原型的关系---对象原型(隐式原型)

在JavaScript中,每个对象都有一个原型对象,并从该原型对象继承属性和方法,这个原型对象定义了这个对象的基类。换句话说,它定义了该对象继承的方法和属性。这个原型对象定义了该对象的基类,如果对象自身没有某个属性或方法,那么它就会去其原型对象中寻找。这种机制构成了JavaScript中对象继承的基础。

每个函数在创建时都会自动获得一个prototype属性,这个属性是一个空对象,是所有实例共享的。当我们创建一个新的实例时,这个实例的prototype属性(在ES6中被称为__proto__)就会被链接到这个函数的prototype对象。

实例对象和原型的这种关系在JavaScript中非常核心,它使得JavaScript的对象系统成为一个非常灵活和动态的系统。这种动态性主要体现在以下两个方面:

  1. 动态属性:你可以随时给一个对象添加新的属性和方法,不论这个对象是实例对象还是它的原型。
  2. 动态继承:如果一个对象的原型对象发生了改变(比如添加了新的属性和方法),那么这个改变会立即反映到所有的实例对象中。

实例对象和原型的这种紧密关系也让我们能够通过修改原型对象来优化性能。当我们有一个被频繁引用的对象实例时,如果我们修改了它的原型对象(比如添加了一个新的方法),那么这个修改对所有的实例对象都是可见的,这意味着我们只需要在内存中保存一份这个方法,就可以节省内存。

函数的原型也被我们称为:显式原型!

我们再来看一个案例:

js 复制代码
function Bus(){

}
Car.prototype = {
    constructor:Bus
}
function Car(){

}
var car = new Car();
//constructor一定会在隐式继承中具有,在浏览器中隐式属性是颜色更淡,显式继承颜色更深
//constructor记录对象是由谁创建的,原型是可以修改的,constructor也是可以修改的
//(构造师)constructor给对象打个标记,表示谁是创建者
console.log(car.constructor)
js 复制代码
输出:[Function: Bus]

首先,JavaScript中的prototype是一个对象,可以通过它来给对象添加属性和方法。当你创建一个新的对象时,这个对象的__proto__属性或者它的原型对象(如果没有__proto__属性)会被设置为它的构造函数的一个属性prototype

然后,当你创建一个新的对象时,这个对象的constructor属性会被自动设置为它的构造函数。

在上述代码中,我们将Car原型prototypeconstructor指向了Bus构造函数,而car会隐式继承Carprototype因此我们输出

car.constructor时,会输出[Function: Bus]

我们再来看一个案例:

html 复制代码
<script>
    Person.prototype.name = 'John';
    function Person(){
    }
    var p = new Person();
    //函数的原型,简称原型
    //对象的原型,不能简称[prototype]
    console.log(p)//__proto__对象的原型,谷歌浏览器这样的,其他浏览器我们为了区分就要__proto__
    //函数实例化没有
    //对象一定有对象原型

</script>

结果:

我们可以看到p实例对象的__proto__指向的Personprototype,所以说我们实例化的对象会继承到其构造函数的原型!!

原型链

原型链是JavaScript中的一个重要概念,它指的是通过原型来实现对象之间的继承关系。每个JavaScript对象都有一个原型对象,用于定义该对象的属性和方法。如果在一个对象上访问某个属性或方法时,该对象本身并没有该属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法为止。

具体来说,每个JavaScript对象都有一个内置的[[proto]]属性,该属性指向创建它的函数对象的prototype。当访问一个对象的属性或方法时,如果该对象自身没有这个属性或方法,JavaScript引擎就会沿着原型链向上查找,直到找到这个属性或方法或者达到原型链的顶端(null)。

原型链的存在主要是为了实现对象的继承。在JavaScript中,每个函数都有一个prototype属性,这个属性是一个预定义的属性,称之为原型对象。当定义一个函数对象的时候,会包含一个预定义的属性叫prototype,这就是原型对象。因此,当创建新的对象实例时,它们将自动获得构造函数的prototype对象中的属性和方法。如果对象实例需要一个在其原型对象中不存在的属性或方法,那么可以通过其构造函数来添加。

接下来,我们来分析一张图,就能将包括原型链在内今天学习原型知识点掌握啦!

来到这一张图,

  1. 我们从f1,f2这两个实例对象出发,他们是构造函数Foo的实例对象,所以他们的隐式原型是Foo函数的显式原型
  2. Foo.prototype是函数Foo的原型,而Foo.prototypeconstructor(创造者)便是函数Foo
  3. Foo.prototype显式原型也是一个对象 !!它的隐式原型__proto__是万物之顶Object函数的显式原型
  4. 作为万物之顶的Object.prototype也是一个对象,但是它的隐式原型__proto__却是null
  5. Object.prototype是函数Object的原型,它的constructor也是函数Object
  6. 同理o1,o2Object的实例对象,他们的隐式原型__proto__就是Object.prototype

我们来到function Foo

  1. Foo函数名也是一个对象,而它的隐式原型便是Function prototype是函数Function的原型。
  2. Function prototype也是对象,它是函数Function的原型,它的constructor是函数Function,同时它的隐式原型__proto__也是Object.prototype
  3. 而函数名Function和Object都是对象,他们的隐式原型__proto__都是Function prototype都是函数Function的显式原型

到这里我们关于这张图片的关系学习就All in啦!!

最后

我们来回答一道增加网易的面试题

网易:所有的对象最终都会继承自Object.prototype ?

大家觉得是这样的吗?

所有的对象最终都会继承自Object.prototype,这是JavaScript中对象继承机制的核心原理。

然而,有一个特殊的例子,即null。在JavaScript中,null是一个特殊的值,它不具有任何属性或方法,并且也不继承自Object.prototype。它是一个没有原型对象的特殊值。

此外,还有一些内置对象,如undefined,也不是从Object.prototype继承的。

所以这个答案是不对的

好了!!到这里啦,大家对原型掌握得怎么样啦?

如果你有任何想法和指正,欢迎大家在评论区留言哦~

点个小小的赞鼓励支持一下吧!🌹🌹🌹

相关推荐
hbrown10 分钟前
Flask+LayUI开发手记(十一):选项集合的数据库扩展类
前端·数据库·python·layui
猫头虎13 分钟前
什么是 npm、Yarn、pnpm? 有什么区别? 分别适应什么场景?
前端·python·scrapy·arcgis·npm·beautifulsoup·pip
迷曳22 分钟前
27、鸿蒙Harmony Next开发:ArkTS并发(Promise和async/await和多线程并发TaskPool和Worker的使用)
前端·华为·多线程·harmonyos
安心不心安1 小时前
React hooks——useReducer
前端·javascript·react.js
像风一样自由20201 小时前
原生前端JavaScript/CSS与现代框架(Vue、React)的联系与区别(详细版)
前端·javascript·css
啃火龙果的兔子1 小时前
react19+nextjs+antd切换主题颜色
前端·javascript·react.js
_pengliang1 小时前
小程序按住说话
开发语言·javascript·小程序
布兰妮甜1 小时前
创建游戏或互动体验:从概念到实现的完整指南
javascript·游戏开发·游戏ai·互动体验·用户输入处理
paid槮1 小时前
HTML5如何创建容器
前端·html·html5
小飞悟2 小时前
一打开文章就弹登录框?我忍不了了!
前端·设计模式