原型和原型链
原型和原型链
JS 生产对象
function Person(name, age) {
// 1.创建一个普通对象
// const obj = {}; // 存储构造函数的属性。
// 2.将这个新对象的隐式原型指向构造函数的显示原型
// obj.__proto__ = X.prototype; // obj 将继承 X.prototype 上定义的属性和方法。
// 3.执行构造函数,并且将this指向新对象,并且接受参数
// const res = X.call(obj, ...arguments);
this.name = name;
this.age = age;
// 4.如果构造函数返回的是引用类型,就返回这个引用类型,否则返回新对象
// return typeof res === "object" ? res : obj;
}
const why = new Person("why", 18);
console.log(why); // Person { name: 'why', age: 18 }
// 不使用 new 调用构造函数时,this 指向全局对象,且没有返回值,因此输出 undefined。
console.log(Person("why", 18)); // undefined它可以通过 new() 调用,也可以直接通过Person()调用,这其实就是JS函数二义性的由来
JavaScript 中的原型机制与委托
[[Prototype]] 属性
几乎所有 JavaScript 对象都有一个隐藏属性 [[Prototype]],它指向另一个对象,即对象的原型。这一原型对象构成了 JavaScript 中继承机制的基础。
// 定义一个原型对象
const animal = {
speak() {
console.log("Animal speaks");
}
};
// 创建一个新对象 dog,dog 的 [[Prototype]] 指向 animal
const dog = Object.create(animal);
// dog 对象可以访问 animal 的方法
dog.speak(); // 输出: Animal speaks
// 验证 dog 的 [[Prototype]]
console.log(dog.__proto__ === animal); // 输出: true
console.log(dog.__proto__); // 输出: { speak: [Function: speak] }属性查找过程
当访问某个对象的属性时,JavaScript 引擎首先检查该属性是否存在于对象本身。如果找不到,它会通过 [[Prototype]] 链接在原型对象上进行查找。如果仍未找到,查找过程将继续沿着原型链向上,直到找到该属性或达到原型链的顶端(即 null)。
// 定义一个原型对象
const animal = {
type: "Animal",
speak() {
console.log("Animal speaks");
}
};
// 创建一个新对象 dog,dog 的 [[Prototype]] 指向 animal
const dog = Object.create(animal);
dog.name = "Buddy"; // dog 对象有自己的属性 name
// 创建一个新对象 puppy,puppy 的 [[Prototype]] 指向 dog
const puppy = Object.create(dog);
puppy.age = 1; // puppy 对象有自己的属性 age
// 访问 puppy 对象的属性
console.log(puppy.name); // 输出: Buddy
console.log(puppy.age); // 输出: 1
console.log(puppy.type); // 输出: Animal
puppy.speak(); // 输出: Animal speaks
// 验证属性查找过程
console.log(puppy.hasOwnProperty('age')); // 输出: true (puppy 自有属性)
console.log(puppy.hasOwnProperty('name')); // 输出: false (name 是从 dog 继承的)
console.log(puppy.hasOwnProperty('type')); // 输出: false (type 是从 animal 继承的)构造函数与 .prototype 属性
函数在 JavaScript 的原型系统中扮演着特殊角色。每个函数都自动获得一个 prototype 属性,该属性引用一个对象,未来通过该函数作为构造函数创建的新对象将以该对象作为原型。
当使用 new 关键字调用构造函数时,会创建一个新对象,该对象的 [[Prototype]] 链接设置为构造函数的 prototype 属性指向的对象。在这个过程中,没有属性的复制,新对象只是简单地链接到它的原型。
示例
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person("Alice");
alice.greet(); // 输出: Hello, my name is Alice*在这个示例中,Person是一个构造函数,alice 是通过 new Person("Alice") 创建的实例。alice 的 [[Prototype]] 指向 Person.prototype,因此可以调用 greet 方法。
三角关系

所有通过构造函数使用 new 关键字创建的对象,都遵循特定的原型链关系。这一关系适用于自定义构造函数以及内置构造函数。
原型链全貌

使用 Object.create() 创建对象
除了使用构造函数,我们还可以使用 Object.create(proto) 方法来创建和链接对象。该方法创建一个新对象,并将其 [[Prototype]] 链接设置为 proto,从而建立对象之间的关联,而无需通过构造函数。
示例
const car = {
drive() {
console.log("Driving");
}
};
const myCar = Object.create(car);
myCar.honk = function() {
console.log("Honk! Honk!");
};
myCar.honk(); *// 输出: Honk! Honk!*
myCar.drive(); *// 输出: Driving*
在这个示例中,`myCar` 通过 `Object.create(car)` 创建,`myCar` 可以访问 `car` 的 `drive` 方法。委托,而非继承
尽管“原型继承”这一术语常用于描述 [[Prototype]] 机制,但它可能会产生误导,因为它暗示了一种实际上在 JavaScript 中并不存在的复制过程。更准确地说,这种机制可以被理解为委托:调用方法的对象将执行该方法的责任委托给通过其 [[Prototype]] 链接的对象。
行为委托的优点
原型链和行为委托提供了多个优点:
- 代码重用:对象可以共享功能而无需重复代码。
- 灵活性:对象可以通过改变其原型链接动态修改其行为。
- 效率:由于对象不必存储自己的共享方法副本,因此内存占用减少。
重要点总结
- JavaScript 并没有传统意义上的类,
[[Prototype]]机制以对象的关联和行为委托为核心。 - 函数通过其
.prototype属性和new关键字的构造函数调用在建立原型链中发挥关键作用。 Object.create()提供了一种更直接的方法来创建委托的对象关联。- 理解 JavaScript 中的
[[Prototype]]机制,关键在于理解委托,而不是继承。
原型链相关方法
1.Object.getPrototypeOf()
Object.getPrototypeOf() - JavaScript | MDN
function Animal() {}
const monkey = new Animal();
console.log(Object.getPrototypeOf(monkey) === Animal.prototype); // true- instanceof 操作符
function Animal() {}
const monkey = new Animal();
console.log(monkey instanceof Animal); // true
console.log(monkey instanceof Object); // true- isPrototypeOf()
Object.prototype.isPrototypeOf() - JavaScript | MDN
function Animal() {}
const monkey = new Animal();
console.log(Animal.prototype.isPrototypeOf(monkey)); // true
console.log(Object.prototype.isPrototypeOf(Animal)); // true- hasOwnProperty()
Object.prototype.hasOwnProperty() - JavaScript | MDN
function Ob() {
this.say = function () {
return "我是一个对象";
};
}
function Animal(name, age) {
this.name = name;
this.age = age; // 这里可以使用传入的 age
}
Animal.prototype = new Ob();
const monkey = new Animal("悟空", 18);
console.log(monkey); // Obj { name: '悟空', age: 18 }
console.log(monkey.say()); // 我是一个对象
console.log(Animal.hasOwnProperty("say")); // false
console.log(Animal.hasOwnProperty("name")); // true