写给前端小白:我终于搞懂了JS原型和原型链

在学习JS时,你是不是被prototype__proto__和原型链整的晕头转向,跟着我,这篇文章带你吃透他们。

为什么要有原型?

先看一个例子,我们想创建多个"人"的对象,每个人都有名字,并且能打招呼。

js 复制代码
function Person(name) {
  this.name = name;
  this.sayHello = function() {
    console.log('你好,我是' + this.name);
  };
}

let p1 = new Person('小明');
let p2 = new Person('小红');

这样写没有任何问题,但是存在一个性能隐患

new一次,就会在内存中创建一个sayHello函数,如果创建成千上万个函数,就会浪费大量内存。

原型就是用来解决这个问题的:把公共的函数放到一个共享的地方,每个实例都能访问到。

一.显示原型prototype

每个函数天生自带一个prototype属性

我们可以把公共的属性和方法挂载到原型上,创建实例后,实例就可以直接调用这些属性和方法。

那么让我们改进一下上面的代码:

js 复制代码
function Person(name) {
  this.name = name;          // 实例自身显式拥有的属性
}

// 把公共方法放到原型上
Person.prototype.sayHello = function() {
  console.log('你好,我是' + this.name);
};

let p1 = new Person('小明');
let p2 = new Person('小红');

p1.sayHello();  // 你好,我是小明
p2.sayHello();  // 你好,我是小红

console.log(p1.sayHello === p2.sayHello); // true  是同一个函数

这样写的好处

  • 节约内存,sayHello只在原型上存了一份。
  • 所有实例自动共享原型上的方法。
  • 减少构造函数在执行时的性能开销。

【注意】:实例对象无法修改原型上的属性值

实例对象的属性来源

  • 实例对象中显示拥有的属性 来自于 构造函数中定义的属性
  • 实例对象隐式中拥有的属性 来自于 构造函数的原型上

二.隐式原型__proto__

每个对象都拥有__proto__属性,该属性也是一个对象,指向创建该对象的构造函数的prototype

js 复制代码
实例对象.__proto__ === 构造函数.prototype

验证一下

js 复制代码
console.log(p1.__proto__ === Person.prototype); // true 

V8 在访问对象中的一个属性时,会先访问对象中显示存在的属性,如果没有,就会去对象的隐式原型上查找

constructor构造器属性,记录该实例对象是由谁创建的。

每个原型对象上都有一个constructor属性,指向构造函数本身。

js 复制代码
console.log(Person.prototype.constructor === Person); // true
console.log(p1.constructor === Person);                // true(通过原型链找到)

所以,你可以通过实例对象.constructor知道这个对象是由哪个构造函数创建的。

三.原型链

V8 在访问对象中的一个属性时,会先访问对象中显示存在的属性 ,如果没有,就会去对象的隐式原型 上查找,如果还没有,就顺着隐式原型 一直往上找,直到找到null 为止,这个查找关系,就叫原型链查找

例子

js 复制代码
Grand.prototype.house = function () {
  console.log("汤臣一品");
};
function Grand() {
  this.card = 100000000;
}
Father.prototype = new Grand();
function Father() {
  this.lastName = "张";
}
Child.prototype = new Father(); //{lastName: '张"}
function Child() {
  this.age = 18;
}
// new Object()
const p = new Child(); // p.__proto__ = Child.prototype.__proto__ == Object.prototype.__proto__ = null

p.house();  // 汤臣一品

p自身没有house,去Child中查找,也没有,去Father中查找,还是没有,再去Grand中查找,最终找到。

了解整理

text 复制代码
构造函数 Person
  │
  ├── prototype ──→ Person.prototype
  │                    ├── sayHello (方法)
  │                    ├── constructor ──→ Person
  │                    └── __proto__ ──→ Object.prototype
  │
实例 p1
  │
  ├── name (自身属性)
  └── __proto__ ──→ Person.prototype (和上面同一个对象)

最后的话

刚开始接触原型时,我总觉得 prototype__proto__ 长得太像,容易混淆。
记住一句话就豁然开朗

prototype 是函数才有的属性,用来放共享的东西;

__proto__ 是所有对象都有的属性,用来指向它的原型。**

原型链就是顺着 __proto__ 一直往上找,直到 null

相关推荐
有颜有货3 分钟前
PMC生产排产的4种算法,一次讲清
java·服务器·前端
小虎牙0076 分钟前
Android kotlin图片库Coil源码详解
android·前端
随风一样自由15 分钟前
【前端领域】前端开发核心应用场景与落地实践
前端·前端框架
an3174238 分钟前
弹窗数据流设计的两种高阶架构实践
前端·vue.js·架构
谢尔登1 小时前
【React】 状态管理方案
前端·react.js·前端框架
用户938515635071 小时前
手把手教你实现一个 MCP 文件读取服务器:从协议到代码的深度解析
javascript·人工智能
用户2136610035721 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月1 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript
用户938515635071 小时前
RAG 实战:从零搭建语义搜索系统,彻底告别关键词匹配的尴尬
javascript·人工智能
李明卫杭州1 小时前
Vue2 中 v-model 处理不同数据结构的技巧
前端·javascript·vue.js