原型就好似套娃,看完这篇基础讲解,让你原型这关顺利通过!

前言

JavaScript 是一门基于原型的语言,这意味着每个 JavaScript 对象天生都有一个原型对象,原型对象包含了一些属性和方法,这些属性和方法可以被该对象共享和继承。在本文中,我们将深入探讨 JavaScript 中原型的概念、作用和使用。

在学习今天的主要目标------------原型前,我们先来看一段代码:

js 复制代码
function Car(){
    this.name='奔驰'
    this.long=4900
    this.height=3000
}
let car = new Car()
let car2 = new Car()   //使用new构造了两个函数

console.log(car)       
console.log(car2)      

现在我们来看一下输出car以及car2的结果:

此时我们可以发现,car以及car2的输出结果一样,那是不是意味着两个对象是同一个呢?然而并不是的,我们可以验证一下,输入console.log(car == car2),若结果为true 说明两者是同一个函数,若结果为false,则说明两者不是同一个函数。

结果显示为false,两者不是同一个函数,因为我们知道new每调用一次Car()都会创建一个新的对象,所以调用两次Car()时就会创建两次对象,两个对象中的属性是相同的,但是两者作为引用对象,其在堆中的存储地址是不同的,自然两者也不会是同一个对象。

原型

原型是函数具有的属性 (prototype), 它定义了构造函数制造出来的对象的公共祖先 ,通过构造函数创建的对象,可以隐式的继承函数原型上的属性和方法。

我们还是利用上面那段代码来看:

js 复制代码
function Car(){
    this.name='奔驰'
    this.long=4900
    this.height=3000
}
Car.call =function(){
    console.log('beautiful')
}
let car = new Car()
let car2 = new Car()
console.log(car.call)

此时我们添加了Car的属性(Car.call),我们此时输出car.call 或者car2.call,看看是否能够调用到Car新添加的属性call。
结果显示undefined,说明并不能继承到Car在函数体外新添加的属性call。如果我们要输出beautiful,只能是调用原函数Car.call,将console.log除去。

但是如果我们在Car的原型上添加属性,结果会怎么样呢?原型的英文就是prototype。

js 复制代码
function Car(){
    this.name='奔驰'
    this.long=4900
    this.height=3000
}
Car.prototype.call =function(){
    console.log('beautiful')        //在原型上添加属性call
}
let car = new Car()
let car2 = new Car()
console.log(car)         //输出car或者car2

此时输出car或者car2并查看结果

此时我们可以看到,car或者car2属性中并没有添加call属性,但是我们利用car.call可以将其调用出来

js 复制代码
function Car(){
    this.name='奔驰'
    this.long=4900
    this.height=3000
}
Car.prototype.call =function(){
    console.log('beautiful')
}
let car = new Car()
let car2 = new Car()
car.call()

这也验证了我们上文说的,通过构造函数创建的对象,可以隐式的继承函数原型上的属性和方法。 ,即使新创建的对象中并没有call属性,但是新对象car可以调用出call()。而this.name这些属性自然就是显式继承,可以输出来的。

并且car和car2两者隐式继承的call是相同的,因为call是他们共同的原型上的属性。

实例运用

那么我们在实际的写代码的过程中应该如何运用原型呢?我们还是拿上面那段car的代码来说吧。

现在我们在Car中加入两个参数owner以及color

js 复制代码
function Car(owner,color){
    this.name='奔驰'
    this.long=4900
    this.height=3000
    this.owner=owner
    this.color=color
}

let car = new Car('李总','red')
let car2 = new Car('王总','blue')
console.log(car)

加入成功,但是我们可以想一想,既然两辆车name、long以及height都是相同的,我们这样操作的话,会不会浪费资源,如果我们把这三个固定的属性定为原型上的属性是不是更方便呢? 100辆车的话100辆车都可以继承下来,不用再放到函数体内循环一百遍。

看如下操作:(利用原型属性)

js 复制代码
Car.prototype.name='奔驰'      //原型属性
Car.prototype.long=4900        //原型属性
Car.prototype. height=3000     //原型属性

function Car(owner,color){
    this.owner=owner
    this.color=color
}
let car = new Car('李总','red')
let car2 = new Car('王总','blue')
console.log(car.name)
console.log(car2.long)

此时我们输出第一辆车的name以及第二辆车的long,看看是否可以成功获得数据

如上结果所示,继承成功!这便是原型的用法,共同不变的属性放在原型中声明,简化我们的工作量,也使得我们更好的理解!

但是我们输出car和car2的信息的话,是看不到原型属性的,只有函数体内定义的owner以及color,因为他们是隐式继承,存在而看不见。

对象原型(隐式原型)

我们在之前讲new的作用时讲到过实例对象,new会创建一个新的对象,例如 let car = new Car,我们将这个新的对象称之为实例对象。

现在我们来介绍一下实例对象的隐式原型,看如下代码:

js 复制代码
function Car(){
    this.name='奔驰'
    this.long=4900
    this.height=3000
}
Car.prototype.call =function(){
    console.log('beautiful')
}
let car = new Car()
let car2 = new Car()
console.log(car2.__proto__ === Car.prototype)

car作为实例对象是显示继承Car函数体内的属性(因为当你console.log(car)时,显示出来的属性只有name、long以及height)但是实例对象是隐式继承Car.prototype,而构造函数是显式继承Car.prototype,说明实例对象的隐式继承和构造函数的显式继承是相同的东西,所以我们令他们相同看输出结果是否正确。

结果为true,得出结论实例对象的隐式原型=构造函数的显式原型

原型链

原型链是JavaScript中的一个重要概念,它描述了对象之间的关系和继承机制。每个JavaScript对象都有一个原型对象,它用于继承属性和方法。当试图访问对象的属性或方法时,JavaScript引擎会首先在对象本身查找,如果找不到,就会沿着原型链向上查找,直到找到为止。原型链的顶端是Object.prototype对象,它是所有对象的根源。

解释完原型链,我们通过一段代码来演示原型链:

js 复制代码
function Foo(){}
let foo = new Foo()   //foo和Foo皆为对象

通过上面得出的结论,我们可以得知 foo.proto === Foo.prototype,而对象就一定会具有隐式原型,Foo.prototype也是对象,所以Foo.prototype也具有隐式原型(Foo.prototype.proto ),那么我们通过实例对象的隐式原型=构造函数的显式原型结论可知,Foo.prototype.proto 也会等于某个构造函数的显式原型,而这个构造函数便是创造Foo的函数也是开头介绍的原型链的顶端(Object.prototype)对象,它是所有对象的根源,所以Object.prototype.proto === null。

简而言之,因为不断有对象出现,且每个对象都有会隐式原型,所以我们称顺着对象的隐式原型不断的向上查找上一级的隐式原型,直到找到目标或null的这种查找关系为原型链!

拓展

网易面试题:所有的对象身上都有隐式原型吗?

答:错,有特例,例如let c = Object.create(null)上是找不到隐式原型的,当你接收一个null为参数的时候是没有隐式原型的,不过绝大多数对象上都是有原型的。

相关推荐
问道飞鱼5 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k09336 分钟前
vue中proxy代理配置(测试一)
前端·javascript·vue.js
傻小胖8 分钟前
React 脚手架使用指南
前端·react.js·前端框架
程序员海军20 分钟前
2024 Nuxt3 年度生态总结
前端·nuxt.js
m0_7482567830 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web135085886351 小时前
前端node.js
前端·node.js·vim
m0_512744641 小时前
极客大挑战2024-web-wp(详细)
android·前端
若川1 小时前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点1 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛1 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode