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
当查找一个对象的属性时,如果该对象没有这个属性,则会去该对象的原型对象上的属性上找。
// 自定义构造函数 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.prototype 为 Animal.prototype 的原型,实现了原型继承。
// 自定义构造函数 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
记得第一次看到这张图的时候,我的内心是崩溃的
但仔细看看 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... 努力记住吧...