js原型与原型链

function Person() {}; const p = new Person();

核心:

javascript 复制代码
实例对象 p
 │
 │  p.__proto__  (即: Object.getPrototypeOf(p))
 ▼
Person.prototype  <-- [构造函数 Person 的显式原型]
 │                 <-- 此处通常包含 constructor: Person
 │
 │  Person.prototype.__proto__
 ▼
Object.prototype  <-- [所有普通对象的最终原型]
 │                 <-- 此处包含 toString, hasOwnProperty 等通用方法
 │
 │  Object.prototype.__proto__
 ▼
null              <-- [原型链终点]
  1. 第一层:实例与原型的连接

‌p.proto => Person.prototype‌

‌描述‌:实例对象 p 是通过构造函数 Person 创建的。因此,p 的内部原型(即 proto)指向了 Person函数的显式原型对象(Person.prototype)。
‌通俗理解‌:"p 是 Person 的孩子,所以 p 继承了 Person.prototype 上的所有属性和方法。"

  1. 第二层:原型与顶级原型的连接

‌Person.prototype.proto => Object.prototype‌

‌描述‌:Person.prototype 本身也是一个普通的 JavaScript 对象。在 JavaScript 中,几乎所有普通对象默认都继承自 Object 的 prototype。因此,Person.prototype 的内部原型指向了 Object.prototype。
‌通俗理解‌:"Person.prototype 虽然是一个特定的原型对象,但它本质上还是一个普通对象,所以它也继承了最基础的 Object.prototype 上的通用方法(如 toString、hasOwnProperty)。"

  1. 第三层:原型链的终点

‌Object.prototype.proto => null‌

‌描述‌:Object.prototype 是 JavaScript 原型链的顶端。它不再继承自任何其他对象,因此它的内部原型指向 null。
‌通俗理解‌: 这是查找的尽头。当 JavaScript 引擎沿着原型链向上查找属性时,如果到了 Object.prototype 还没找到,再往上就是 null,表示没有更多祖先了,查找结束并返回 undefined。

总结性描述: ‌"实例 p 的原型指向 Person 的原型对象,Person 的原型对象又指向通用的 Object 原型对象,而 Object 原型对象是原型链的顶层,其原型为 null。"

原型链查找过程:

javascript 复制代码
function Person() {};const p = new Person();

p.sayHi();  查找过程 如下:

p (实例)

 │

 ├─ 1. 查找 p.sayHi ? → 无 (自有属性)

 │    ↓ 通过 p.__proto__

 │

 Person.prototype (构造函数原型)

 │

 ├─ 2. 查找 Person.prototype.sayHi ? → 无 (除非手动添加)

 │    ↓ 通过 Person.prototype.__proto__

 │

 Object.prototype (顶级原型)

 │

 ├─ 3. 查找 Object.prototype.sayHi ? → 无

 │    ↓ 通过 Object.prototype.__proto__

 │

 null (终点)

 │

 └─ 4. 查找结束 → 返回 undefined → 报错 TypeError‌
javascript 复制代码
1.构造函数和普通函数的区别:
    1.构造函数也是一个普通的函数,创建方式和普通函数一样,但构造函数习惯上首字母大写。
    2.普通函数的调用方式:直接调用person();构造函数的调用方式:需要使用new Person();
function aa() {}
原型对象:在声明了一个函数后,浏览器会自动按照一定的规则创建一个对象,这个对象就叫原型对象。这个原型对象其实是存储在内存当中。
在声明了一个函数后,这个构造函数(声明了的函数)中会有一个prototype属性,这个属性指向的就是这个构造函数(声明了的函数)对应的原型对象;原型对象中有一个属性constructor,这个属性指向的是这个构造函数(声明了的函数)
aa.prototype // {constructor: f}
aa.prototype.constructor // aa() {}
===> aa === aa.prototype.constructor // true
2.所有的引用类型(数组,函数,对象)可以自由扩展属性(null除外)
3. 所有的引用类型都有一个'__proto__'属性(也叫隐式原型,它是一个普通的对象)。
aa.__proto__ // f() {native code}
4.所有的函数都有一个'prototype'属性(也叫显式原型,它也是一个普通的对象)
aa.prototype // {constructor: f}
5.所有的引用类型,它的'__potot__'属性指向它的构造函数的'prototype'属性。
let all = new a()
all.__proto__ === a.prototype // true
// 一个普通的对象,它的构造函数是Object,所以:
all.prototype.__proto__ === Object.prototype
6.当试图得到一个对象的属性时,如果这个对象本身不存在属性,那么它就会去它的'__proto__'属性(也就是它的构造函数的'prototype'属性)中寻找
function b(name, age) {
    this.name = name
    this.age = age
}
let b1 = new b('sb', 20)
b1.message() // bn.message is not a function
b.prototype = {
    message: function() {
        console.log('My message is:' + this.name + '||' + this.age)
    }
}
let b1 = new b('sb', 20)
b1.message() // My message is:sb||21
7.当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它	的构造函数的'prototypr'属性中去找。又因为'prototypr'属性是 一个对象,所以它也有	一个'__proto__'属性,就会去它的构造函数的'prototype'的'__proto__'属性中去找,直	到找到Object.prototype为止,如果Object.prototype中也没有这个属性,那么就会	返回undefined。
function a(name, age) {
    this.name = name
    this.age = age
}
let a1 = new b('sb', 20)
a1.message() // a1.message is not a function
// 找不到就会去找它的构造函数的prototype上有没有这个方法
a.prototype.message() // a.prototype.message is not a function
// 找不到,(a.prototype也是一个对象),就找它的构造函数的prototype
Object.prototype.message() // Object.prototype.message is not a function
Object.prototype.message = function () {
    console.log('this is' + this.age + '的' + this.name)
}
a1.message() // this is 20的sb
a1能调用到的message()就是原型链的机制

原型链继承示例:

javascript 复制代码
// 原型链继承示例
function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};
function Child(name, age) {
    // 借用构造函数继承属性
    Parent.call(this, name);
    this.age = age;
}
// 原型链继承方法
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 添加子类特有方法
Child.prototype.sayAge = function() {
    console.log(this.age);
};
// 测试
const child1 = new Child('小明', 18);
const child2 = new Child('小红', 20);
child1.sayName(); // 小明
child1.sayAge();  // 18
child2.sayName(); // 小红
child2.sayAge();  // 20
// 验证属性隔离
child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
console.log(child2.colors); // ['red', 'blue'] - 不受影响
// 验证方法继承
child1.sayName(); // 小明
child2.sayName(); // 小红
相关推荐
weixin_523185321 小时前
Java内存模型详解:栈、堆、方法区、本地方法栈与程序计数器
java·开发语言
橘子星1 小时前
打破串行枷锁:深入理解 JS 同步、异步与 Promise 实战
前端·javascript
渣波1 小时前
全栈开发的“影分身”之术(mock):别再手动造数据了,你的 CRUD 不配让我等!
前端·javascript
换个昵称都难1 小时前
WebRTC QoS 实战:从原理到弱网优化
开发语言·php·webrtc
爱吃生蚝的于勒2 小时前
QT开发第三章——常用控件
linux·服务器·开发语言·前端·javascript·c++·qt
未若君雅裁2 小时前
工厂模式详解:简单工厂、工厂方法与抽象工厂
java·开发语言
我命由我123452 小时前
由 ImageView 获取到的 Drawable 对象,它的 intrinsicWidth、intrinsicWidth 与实际图片的尺寸
java·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime
xuankuxiaoyao2 小时前
Axios-图书列表案例
开发语言·前端·javascript
guslegend2 小时前
Java 创建对象有几种方式
java·开发语言