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 <-- [原型链终点]
- 第一层:实例与原型的连接
p.proto => Person.prototype
描述:实例对象 p 是通过构造函数 Person 创建的。因此,p 的内部原型(即 proto)指向了 Person函数的显式原型对象(Person.prototype)。
通俗理解:"p 是 Person 的孩子,所以 p 继承了 Person.prototype 上的所有属性和方法。"
- 第二层:原型与顶级原型的连接
Person.prototype.proto => Object.prototype
描述:Person.prototype 本身也是一个普通的 JavaScript 对象。在 JavaScript 中,几乎所有普通对象默认都继承自 Object 的 prototype。因此,Person.prototype 的内部原型指向了 Object.prototype。
通俗理解:"Person.prototype 虽然是一个特定的原型对象,但它本质上还是一个普通对象,所以它也继承了最基础的 Object.prototype 上的通用方法(如 toString、hasOwnProperty)。"
- 第三层:原型链的终点
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(); // 小红