一篇文章带你了解JS中的原型

前言

在JavaScript的世界中,原型(Prototype)是一项核心概念,它影响着对象的创建、继承以及方法的共享。在这篇文章中,在这篇文章中,我们将深入探讨JavaScript的原型,用通俗易懂的语言帮助大家理解这一概念。

希望读者在掌握这些知识后,能够更深入地理解JavaScript。


1、构造函数

在聊原型之前,我们先来说说构造函数,在JS中,有一种用于创建对象的特殊函数叫做构造函数。它其实与普通函数没有什么区别,任意一个函数被new调用时就成为了构造函数,首字母尽量要大写

JS中自带的构造函数

1.String 2.Number 3.Boolean 4.Object 5.Array 6.Function 7.Object

还有一个比较特殊的时间日期函数

js 复制代码
let str = new String()
let s = ' '

我们需要知道的是当你写下这段代码时,在v8引擎面前其实是一样的,它自动会把 let s = ' ' 即创建字符串字面量,转化为 let str = new String(),v8引擎对于各种数据类型都会执行该操作

每当我们new了一个函数就会生成一个实例对象,让我们打开浏览器的控制台来展开prototype看看

我们会发现里面有好多方法,原来我们创建了一个字符串,还能使用这么多方法,这些方法创造出来就是为了方便大家的使用,这个被我们展开的prototype翻译过来的意思就是原型


2、原型(显示原型) prototype

1.原型(prototype)是一个函数被定义出来天生就具有的属性

2.函数原型存在的意义:因为实例对象能访问到函数原型上的属性,所以原型存在的意义就是让某一种数据结构拥有更多的方法可用

现在假设我们有一个制造小米su7的工厂,我们可以让用户自定义他们想要的颜色,张三最近就对su7很感兴趣,他想要一辆红色的,李四想要一辆绿色的

js 复制代码
function Car(color, owner) {
  this.name = "su7"
  this.long = 5000
  this.height = 1400
  this.color = color
  this.owner = owner
}

let car1 = new Car('red', 'zhangsan')
let car2 = new Car('green', 'lisi')
console.log(car1, car2)

我们雷总的工厂效率很高啊一下就把他们两个的su7做好了

但是你们有没有发现我们给用户内容里面有些东西是多余的啊 这里面的name、long、height是用户无法改变的内容,他们都来这里买车了会不知道自己买的是不是su7吗,而且工厂要反复执行这三个公共属性,因此我们就可以用原型来优化这个问题

js 复制代码
Car.prototype.name = "su7"
Car.prototype.long = 5000
Car.prototype.height = 1400

function Car(color, owner) {
  // this.name = "su7"
  // this.long = 5000
  // this.height = 1400
  this.color = color
  this.owner = owner
}

let car1 = new Car('red', 'zhangsan')
let car2 = new Car('green', 'lisi')
console.log(car1, car2)

我们运行时就会发现我们的name long height就不见了,但是我们去用car1.name却又能找到su7,用car2.long能找到5000,我们也不需要重复执行代码

我们就可以发现原型的作用: 可以将一些固定的属性提取到原型上,减少重复的代码执行

我们再继续深入聊聊原型,官方已经给我们打造了这么多方法,既然我们能用是不是我们自己也能创建呢?

我们看到这段代码,常用的字符串的trim方法能去除字符串前后的空格(无法取消中间的空格)

js 复制代码
String.prototype.trim = function () {
  return 'mytrim'
}

let str = '    hello' // new String()

console.log(str.trim())

现在我们也想搞个trim方法,那我们现在用这个trim的时候到底是输出hello呢还是输出mytrim呢?

结果是mytrim,所以我们是可以重写官方给的方法的,在官方给的方法不足以满足我们的需求的时候,我们可以适当加将其修改成我们需要的样子,但是length的值我们修改了,它还是5,因此我们无法修改length的值,所以我们可以得到这个结论

我们无法通过实例对象对原型上的属性进行修改

小结:

1.函数身上一定有一个prototype 属性原型 也是一个对象

2.实例对象一定能拿到构造函数身上的原型

2.原型(prototype)是一个函数被定义出来天生就具有的属性

3.函数原型存在的意义:因为实例对象能访问到函数原型上的属性,所以原型存在的意义就是让某一种数据结构拥有更多的方法可用

4.可以将一些固定的属性提取到原型上,减少重复的代码执行

5.实例对象无法修改函数原型上的属性


3、对象原型(隐式原型) __ proto __

我们先来看看这张图片,我们先是定义了一个对象obj,按照上文所说,在v8引擎眼中相当于new了Object,然后我们能看到obj里面也有一个prototype但是这个prototype其实是__proto__,我们又发现我们的构造函数Object里的原型(prototype)长得居然和obj的__proto__一模一样,这就很有意思了,我们接下来就来慢慢了解这个__proto__

js 复制代码
const obj = {
  a: 1
}
obj.a
obj.toString()

现在我们需要知道一个规则,v8引擎去访问对象身上的一个属性,如果这个对象显示就拥有这个属性的时候,就会直接读取这个属性,就像obj中的a ,我们能直接读取到,如果找不到,就会去对象身上的隐式原型(_ proto _)找有没有这个属性,就像这个toSring(),我们在obj中看不到,我们就会去obj身上的隐式原型找

对象身上的隐式原型和构造函数的原型完全一样,我们又说过 实例对象一定能拿到构造函数身上的原型,这是为什么呢?

我先来打个比方,我是函数我有一个碗,你是对象你想吃我碗里面的东西,那你能怎么办,当然是你也去买个碗,然后我把碗里的倒到你碗里。也就是说对象的隐式原型会被赋值成创建该对象的构造函数的(显示)原型

4、new的原理

js 复制代码
function Person() {
  this.name = 'zhangsan'
  this.age = 18
}
let p = new Person() 

第一步 创建一个空的对象

let obj = {

}

第二步 将构造函数的this指向obj(显示绑定)

Person.call(obj)

第三步 正常运行构造函数中的内容

此时person里的this指向obj

把内容拷贝到obj上

第四步 obj的隐式原型等于构造函数的显示原型

obj._ _proto _ _ = Person.prototype

第五步 判断Person()的值如果是引用类型,则采用,否则返回obj

return Person() instanceof Object ?Person():obj

这里的第二步里面的call方法后面讲this的文章会提及

5、原型链

  1. 对象可以调用其构造函数的 prototype 原型对象的属性和方法
  2. 原型对象也是对象,同样也可以调用其构造函数的prototype原型对象的属性和方法
  3. 一层一层的形成一条链路就是原型链

小知识: 不是所有的对象都有隐式原型

object.create(null)就没有隐式原型

相关推荐
记得早睡~14 分钟前
leetcode150-逆波兰表达式求值
javascript·算法·leetcode
庸俗今天不摸鱼35 分钟前
Canvas进阶-4、边界检测(流光,鼠标拖尾)
开发语言·前端·javascript·计算机外设
[廾匸]1 小时前
cesium视频投影
javascript·无人机·cesium·cesium.js·视频投影
菲力蒲LY2 小时前
vue 手写分页
前端·javascript·vue.js
一丢丢@zml2 小时前
new 一个构造函数的过程以及手写 new
javascript·手写new
化作繁星3 小时前
React 高阶组件的优缺点
前端·javascript·react.js
zpjing~.~4 小时前
vue 父组件和子组件中v-model和props的使用和区别
前端·javascript·vue.js
FFF-X4 小时前
大屏自适应终极方案:基于比例缩放的完美适配实践(Vue3版)
javascript·html5
卓有成效的程序员4 小时前
为什么要用 const 和 let,而不是 var?
javascript
做一颗卷心菜4 小时前
Promise
开发语言·前端·javascript