10分钟搞懂 js 原型

原型很简单,10 分钟就能搞懂!!!

函数

先来看一下上古时期的工作中我们是如何定义函数的,直接使用 function 关键字来声明

js 复制代码
function fun() {}

但是有的时候我们会发现函数也会用下面的这种格式来定义

js 复制代码
function Fun() {}

除了第二个 函数名的首字母大写之外 ,本质上两者毫无区别

函数名首字母大写的意义

当我们需要将一个函数作为 构造函数 使用时,通常会将函数名首字母大写,为了看起来规范一些,仅此而已

实例化

构造函数 使用 new 关键字可以创建出不同的 实例(实例的本质就是一个对象),就好比说:你没有女朋友,但是你可以准备一个构造函数 new 很多女朋友出来,就是这个意思!

开造

  • 构造函数(女朋友构造器):
js 复制代码
function GirlFriend() {}
  • 创建第一个 gf1 (实例对象)
javascript 复制代码
const gf1 = new GirlFriend()

gf1.name = '小美'
gf1.age = 18

console.log(gf1)  // {name: '小美', age: 18}
  • 创建第二个 gf2 (实例对象)
javascript 复制代码
const gf2 = new GirlFriend()

gf2.name = '小丽'
gf2.age = 19

console.log(gf2) // {name: '小丽', age: 19}

关联

  • GirlFriend 是一个 构造函数
  • gf1gf2 是通过 new GirlFriend 创建出来的两个 实例对象

那如何将 实例对象构造函数 联系在一起呢?

  • 实例对象 上会默认存在一个属性叫做 __proto__,这里记作 隐式原型
  • 构造函数 上会默认存在一个属性叫做 prototype,这里记作 **显示原型 **

通常我们所说的 原型对象 也就是指这里的 prototype原型对象 上的 constructor属性可以直接访问该 构造函数 (这里建议手动打印观察一下)

默认情况下,实例对象__proto__ 指向 构造函数prototype,如果你想访问某个实例的原型对象,就可以通过如下关系来进行访问

javascript 复制代码
console.log(GirlFriend.prototype.constructor) // ƒ GirlFriend() {}
gf1.__proto__ === GirlFriend.prototype // true
gf2.__proto__ === GirlFriend.prototype // true

访问

当我需要访问 实例对象 上存在的属性,比如 name 时:

javascript 复制代码
console.log(gf1.name) // '小美'
console.log(gf2.name) // '小丽'

当我需要访问 实例对象 上不存在的属性,比如 feature 时:

javascript 复制代码
console.log(gf1.feature) // undefined
console.log(gf2.feature) // undefined

因为没有这个属性,自然而然就会打印 undefined

但如果说我想添加一个共同的属性给所有被 实例化的对象 时,我该如何去处理呢?

  • 上面已经说明,被同一个 构造函数 创建出来的 实例对象 ,默认情况下他们的 隐式原型 都会指向该构造函数的 显示原型 ,也就是 GirlFriend.prototype,因此我只需要在往这个原型上去添加就好
javascript 复制代码
GirlFriend.prototype.feature = 'beautiful'

此时再次访问实例上的 feature属性,最终即可得到正常的打印

javascript 复制代码
console.log(gf1.feature) // beautiful
console.log(gf2.feature) // beautiful

如果说我想单独给 gf1 添加不一样的 feature 再访问呢

javascript 复制代码
gf1.feature = 'pretty'

console.log(gf1.feature) // pretty
console.log(gf2.feature) // beautiful

但是,为什么我给 显示原型 添加的属性可以直接通过实例对象进行访问呢?

原理

  1. 每一个被 构造函数 创建的 实例对象 都是一个全新的 对象 ,我们可以为该对象添加本身特有的属性
  2. 当我们尝试访问 实例对象 上的某个属性时,如果存在则会直接返回该属性的值;如果不存在,就会沿着 实例对象__proto__ 继续向上访问,如果查找到则会返回该属性的值,如果没有找到,则会返回 undefined

注意

为了更加清晰的了解原型,这里我们再提及 js 中几个比较关键的点

普通对象 - object
  • 只要是一个普通对象object,就可以用 new Object() 来实例化(Object() 是一个内置的构造函数 ),也就是说,所有的对象字面量都是 Object() 的实例
  • Object 作为构造函数,Object.prototype 指向一个具体的 原型对象 ,该 原型对象 作为对象的实例,它的 __proto__ 值为 null,因而 Object.prototype.__proto__ = null 时也就走到了 原型的尽头
javascript 复制代码
const obj = {}
const obj1 = new Object()

console.log(obj.__proto__ === obj1.__proto__)  // true
console.log(obj1.__proto__ === Object.prototype) // true
console.log(Object.prototype.____proto__) // null

回到上面那个例子,当我要访问 实例对象 gf1 上的属性时:

  • 如果该属性存在,就会直接返回对应的值
  • 如果该属性不存在,就会沿着 gf1.__proto__ 进行查找,本质上查找的就是 GirlFriend.prototype 这个对象
    • 如果该属性存在,就会直接返回对应的值
    • 如果该属性不存在,那么 GirlFriend.prototype 作为 Object实例对象 ,其本身也是存在 __proto__ 属性的,所以会沿着 GirlFriend.prototype.__proto__来进行查找,本质上查找的就是 Object.prototype
      • 如果该属性存在,就会直接返回对应的值
      • 如果不存在,就会查找接着查找 Object.prototype.__proto__ ,此时 Object.prototype.__proto__ 值为 null ,最终没有找到该属性,打印 undefined

这个顺序很好理解

  1. gf1.xx
  2. gf1.__proto__.xx
  3. gf1.__proto__.__proto__.xx
  4. gf1.__proto__.__proto__.__proto__.xx

当最终 __proto__null 都没有找到时就会打印 undefined

因此,沿着 __proto__ 访问对象属性构成的这一条链也就是平时所说的 原型链

特殊对象 - function
  • function 也算是一类特殊的对象,因此可以直接通过属性的形式来进行变量的访问

  • 已经内置了 Function() 构造函数 ,因而 所有函数 都算作是 Function实例对象

    • Function 作为 构造函数 时,可以访问其 prototype 属性
    • Function 作为 实例对象 时,可以访问其 __proto__ 属性
javascript 复制代码
// 在 Function 这里就是:我实例化了我寄几
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.prototype.constructor === Function) // true
  • 内置的 Object() 也是一个函数,因此 Object 也是我 Function实例对象
javascript 复制代码
console.log(Object.__proto__ === Function.prototype) // true

经典图示

相关推荐
喵叔哟10 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django