【JavaScript】原型

116 阅读3分钟

1. 什么是原型?

在 JavaScript 中,每个对象都有一个原型(prototype),它是对象的一种特殊属性。原型对象包含了对象的属性和方法,当我们访问对象的属性或方法时,如果对象本身不存在这些属性或方法,JavaScript 引擎会自动去原型对象中查找。

2. 原型链

原型链是 JavaScript 中对象之间的连接,它由原型对象组成的链式结构。当我们访问一个对象的属性或方法时,JavaScript 引擎首先查找对象本身是否有该属性或方法,如果没有,它会沿着原型链向上查找,直到找到对应的属性或方法或者到达原型链的顶端(Object.prototype)为止。

3. 如何访问原型

在 JavaScript 中,每个对象都有一个 prototype 属性,用于指向其原型对象。我们可以使用 Object.getPrototypeOf(obj) 方法来获取对象的原型。

let obj = {};
let prototype = Object.getPrototypeOf(obj); // 获取 obj 的原型对象

另外,ES6 中引入了 __proto__ 属性,它可以直接访问对象的原型,但不推荐使用,因为它不是标准的 JavaScript API。

4. 如何设置原型

在 JavaScript 中,可以使用构造函数和 Object.create() 方法来设置对象的原型。

  • 使用构造函数(初衷是为了模拟类的形式,批量生产对象;但是底层还是基于原型来创建对象):
function Person(name) {
    this.name = name;
}

let person1 = new Person('Alice');
let person2 = new Person('Bob');

// 方法会挂到原型对象上面
Person.prototype.sayHello = function() {
    console.log('Hello, ' + this.name + '!');
};

person1.sayHello(); // 输出 'Hello, Alice!'
person2.sayHello(); // 输出 'Hello, Bob!'
  • 使用 Object.create() 方法:
let person = {
    name: 'Alice'
};

// let student = Object.create(person);
// student.age = 20;
// 等同于
let student = Object.create(person, {
    age: {
        value: 20,
        enumerable: true
    }
})

console.log(student.name); // 输出 'Alice'
console.log(student.age)  // 20
console.log(person.name)  // 'Alice'
console.log(person.age)  // undefined
// person 就是 student 这个对象的原型对象
console.log(student.__proto__ === person)  //true

当查找一个对象的属性时,如果该对象没有这个属性,则会去该对象的原型对象上的属性上找。

image.png

// 自定义构造函数 Fn 一般以大写字母开头
function Fn(name, age) {
    this.name = name
    this.age = age
}
Fn.prototype.showMessage = function () {
    console.log(`我的名字是${this.name},我的年龄是${this.age}`)
}
// 实例对象 me
let me = new Fn('xiu', 18)
// 原型对象 { showMessage: [Function (anonymous)] }
console.log(me.__proto__)
console.log(me.__proto__ === Fn.prototype) // true
console.log(Fn.prototype.constructor === Fn) // true
console.log(Fn.__proto__ === Object.__proto__)  // true
console.log(Fn.__proto__ === Number.__proto__)  // true

总结一下下:

  • JavaScript中每个对象都有一个原型对象。可以通过__proto__属性来访问到对象的原型对象。
  • 构造函数的 prototype 属性指向一个对象,这个对象是该构造函数实例化出来的对象的原型对象。
  • 原型对象的 constructor 属性也指向其构造函数。
  • 实例对象的 constructor 属性是从它的原型对象上访问到的。
  • 所有构造函数的原型对象都是同一个对象。

5. 原型继承

原型继承是 JavaScript 中实现对象之间继承关系的一种方式。当一个对象的原型对象是另一个对象时,该对象就继承了另一个对象的属性和方法。

function Animal(name) {
    this.name = name;
}

Animal.prototype.sayName = function() {
    console.log('My name is ' + this.name);
};

function Dog(name, breed) {
    Animal.call(this, name);
    this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    console.log('Woof! Woof!');
};

let dog = new Dog('Buddy', 'Labrador');
dog.sayName(); // 输出 'My name is Buddy'
dog.bark(); // 输出 'Woof! Woof!'

在上面的例子中,Dog 对象继承了 Animal 对象的属性和方法,通过设置 Dog.prototypeAnimal.prototype 的原型,实现了原型继承。

image.png

// 自定义构造函数 Fn 一般以大写字母开头
function Fn(name, age) {
    this.name = name
    this.age = age
}
Fn.prototype.showMessage = function () {
    console.log(`我的名字是${this.name},我的年龄是${this.age}`)
}
// 实例对象 me
let me = new Fn('xiu', 18)
// 原型对象 { showMessage: [Function (anonymous)] }
console.log(me.__proto__)
// 构造函数原型对象
console.log(Fn.__proto__)  // {}
console.log(me.__proto__.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null) // true

image.png

记得第一次看到这张图的时候,我的内心是崩溃的

331378DC.png

但仔细看看 Object 和 Object.prototype 和 构造函数原型对象 的关系和最初的小三角(该文章第一张图)意思是完全一样的。

这里我认为图中关于 new Function 为 Object 的说法并不准确,如果有大佬看到,希望不吝赐教!

// 自定义构造函数 Fn 一般以大写字母开头
function Fn(name, age) {
    this.name = name
    this.age = age
}

Fn.prototype.showMessage = function () {
    console.log(`我的名字是${this.name},我的年龄是${this.age}`)
}
// 实例对象 me
let me = new Fn('xiu', 18)

console.log(new Function())  //[Function: anonymous]
console.log(Object) // [Function: Object]
console.log(Object === new Function()) // false
console.log(Object.__proto__ === Fn.__proto__) // true
console.log(Fn.__proto__.constructor === Function)// true
console.log(Fn.__proto__ === Function.__proto__)// true
console.log(Fn.__proto__ === Function.prototype)// true

所以难理解的就是 Function。emmm... 努力记住吧...