内容 | 链接 |
---|---|
2023前端面试笔记 | HTML5 |
2023前端面试笔记 | CSS3 |
理解原型和原型链可以帮助我们更好地理解 JavaScript 中的面向对象编程
,实现属性和方法的共享和继承
。同时,掌握原型和原型链的知识也是深入学习 JavaScript 的关键。
在 JavaScript 中,每个对象(除了 null 和 undefined)都有一个原型对象(prototype)。
原型对象是一个普通的对象,它包含了共享的属性和方法。
现在有1个类A,我想要创建一个类B,这个类B是以类A为原型的,并且能进行扩展。我们称B的原型为A。
class Student {
constructor(name, score) {
this.name = name;
this.score = score;
}
introduce() {
console.log(`我是${this.name},我考了${this.score}分`);
}
}
const student = new Student("张三", 99);
现在有一个Student类,以及它的实例化对象student,当你在student身上使用了它不存在的属性或方法,就会去隐式原型对象__proto__上找
__proto__
=>(指向student实例化对象的原型 => Student类)
student这个实例化对象身上并没有introduce方法,但是它却能够使用,因为它的隐式原型对象__proto__身上有该方法,__proto__之所以有该方法,是因为它指向的是Student类的原型对象,而原型对象身上有introduce方法
student实例化对象的隐式原型对象全等于
Student类的显示原型对象
function person(name, age) {
this.name = name;
this.age = age;
}
person.prototype.saying = function () {
console.log("你好呀,我是person显示原型对象上的saying函数");
};
person.prototype.work = "前端开发工程师";
const p1 = new person("前端百草阁", 21);
这里把saying方法挂到了p1的原型身上,我们也可以挂到person构造函数的身上,但是这样就会有一个问题,每次调用构造函数时都会为新的对象创建一个新的方法。如果有多个对象实例,它们将各自拥有自己的方法。
这可能会导致内存占用增加,因此在一些情况下,将方法定义在构造函数的原型上
更为常见和推荐。
function person(name, age) {
this.name = name;
this.age = age;
this.saying = function () {
console.log("");
};
}
const p1 = new person("前端百草阁", 21);
就如上图所说,实例自身访问不到的属性或方法,就会往自己的原型身上找(proto)
自己的原型找不到就会往原型的原型身上找,直到找到最顶端(Object)
function person(name, age) {
this.name = name;
this.age = age;
}
person.prototype.saying = function () {
console.log("你好呀,我是person显示原型对象上的saying函数");
};
person.prototype.work = "前端开发工程师";
const p1 = new person("前端百草阁", 21);
class Person {
constructor(name) {
this.name = name;
}
drink() {
console.log("每个人都会喝水");
}
}
const person = new Person("普通人");
class Teacher extends Person {
constructor(name, subject) {
super(name);
this.subject = subject;
}
teach() {
console.log("每个老师都会教书");
}
}
const teacher = new Teacher("哈基米老师", "猫猫课");
在这里的原型链中,如果你对teacher实例化对象使用了drink方法,对象本身是没有drink方法的,但是他可以通过原型链来查找,谁身上具有这个方法,把它拿过来使用
任何一个人都可以使用hasownproperty方法,因为他是原型链顶端Object身上的方法,所有人都可以访问到
如果指定的属性是对象的直接属性——即使值为 null 或者 undefined,hasOwnProperty() 方法也会返回 true。如果属性是继承的,或者根本没有声明该属性,则该方法返回 false。与 in 运算符不同的是,该方法不会在对象原型链中检查指定的属性。
这里附上我自己画的原型链继承图,用图看会清晰很多
Teacher.prototype.__proto__ === Person.prototype // true
p1的原型是构造函数,不知道有没有小伙伴和我一样疑惑 对象有自己的原型很正常 为什么构造函数也有自己的原型呢
函数是第一类对象(first-class object)
,被称为一等公民
。函数与对象共存,我们也可以认为函数就是其他任意类的对象。由此可见,对象有的功能,函数也会拥有
。
函数也是对象,唯一不同的地方在于,函数是可以调用的(invokable),也就是说函数会被调用以便执行某些动作。
综上所述,构造函数
通过 prototype
属性指向原型对象,原型对象包含了共享的属性和方法
,而对象实例
通过原型链
(__proto__属性指向原型对象)继承了原型对象的属性和方法
。通过 constructor
属性,我们可以追溯到对象实例的构造函数
。这些概念共同构成了 JavaScript 中的原型继承机制
。