前情提要
年初准备找实习的我急急忙忙在网上下载了一堆前端面试八股,开始了废食忘寝的预习......
打开不同的资料,发现都能碰到同样的问题:【谈谈你对 JS 原型和原型链的理解?】
看一眼回答:
不死心再找教程看看:
(PS:没有说别的教程不好的意思,是我愚笨!!!
直到在哔哩哔哩大学看到了这个视频: 【js原型链、构造函数和类】
仅仅1分26秒的视频,详情请见评论区:
大半年过去了,再回顾一下,整理个文档。
一、构造函数
简单概括:构造函数是用 new 关键字 调用,并且首字母大写,本质上也是个函数。
举个栗子
假如你是Riot公司的一名程序员,领导让你使用js重新开发lol这款游戏。需要100多个创建英雄到系统内,如果一个一个写,要写到地老天荒。于是先写个构造函数:
ini
function Hero(name, title, role) {
this.name = name;
this.title = title;
this.role = role;
};
有了构造函数,创建英雄new一下就搞定了
ini
const Jiakesi = new Hero('贾克斯', '武器大师', '战士');
const Yidashi = new Hero('易大师', '无极剑圣', '刺客');
总的来说
所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。
JS中使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类(理解为模板,表示某一类实物的共同特征)。我们将通过这个构造函数创建的对象,称为是该类的实例。
优点
- 减少代码重复:使用构造函数可以减少代码的重复,特别是在需要创建大量具有相同属性的对象时。
- 初始化对象 :构造函数的主要用途是初始化对象,通过
this
关键字为对象设置属性和方法。这种方式使得对象的创建和初始化更加简洁和直观2。 - 语法简洁 :构造函数提供了一种简洁的书写方式,使得对象的创建更加方便和直观。通过
new
关键字调用函数,可以自动创建一个新的对象,并初始化其属性。 - 提高代码的可读性和可维护性:使用构造函数可以使代码更加模块化,提高代码的可读性和可维护性。
有点缺点
-
缺点1:如果构造函数中有很多的方法那么就会开辟很多的空间,浪费内存资源。
javascriptfunction Person(name,age){ this.name = name, this.age = age, this.sayhi = function(){ console.log('构造函数中的方法'); } } var Person1 = new Person('小白',12) var Person2 = new Person('大白',22) Person1.sayhi() Person2.sayhi() console.log(Person1 === Person2, Person1.sayhi === Person2.sayhi); //false false
-
缺点2: 如果在全局情况下声明函数,虽然解决了内存资源浪费的问题,但是又会出现全局变量污染的问题。
javascriptfunction say(){ console.log('构造函数中的方法'); } function Person(name,age){ this.name = name, this.age = age, this.sayhi = say } var Person1 = new Person('小白',12) var Person2 = new Person('大白',22) Person1.sayhi() Person2.sayhi() console.log(Person1 === Person2, Person1.sayhi === Person2.sayhi); //false true
-
缺点3: 可以重新声明一个对象专门存放这些方法,但是新的问题时,如果有很多个构造函数,就要声明很多个这样的对象。
javascriptvar obj = { say: function () { console.log('函数中的say方法'); }, hi: function () { console.log('函数中的hi方法'); } } function Person(name, age) { this.name = name, this.age = age, this.sayhi = obj.say } var Person1 = new Person('小白', 12) var Person2 = new Person('大白', 22) Person1.sayhi() Person2.sayhi() console.log(Person1 === Person2, Person1.sayhi === Person2.sayhi); //false true
为解决创建构造函数的弊端,可以直接使用【原型对象】。
二、 原型对象
给出概念
js规定每个函数都有一个原型属性 (__proto__
),指向一个原型对象 (prototype
)。
js继承机制的设计思想:原型对象 (prototype
) 的所有属性和方法都能被实例对象共享。
- 对象:new关键字创建的实例对象;
- 原型:又称原型对象,通常写作:构造函数名.prototype;
- prototype:构造函数的属性;
- proto:对象的属性
举个栗子
ini
// 这是一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
//在原型上添加属性和方法
Person.prototype.xp = 'all';
Person.prototype.sayHello = function () {
console.log('Hello, I'm + this.name')
}
// 创建(实例)一个对象
let p1 = new Person('Lily',18)
这里的Person.prototype是对象Person的原型
它们的关系如图:
构造函数通过new关键字创建对象(P1对象);
对象(P1对象)通过__proto__指向它的原型(Person.prototype);
原型(Person.prototype)中有所有对象公用的属性和方法(如xp和sayHello);
原型只能通过构造函数(constructor)来创建对象;
构造函数也可以通过prototype属性指向原型;
原型可以看作对象的本体,通过构造函数这个模板工具来创建对象。所以:
- 每个对象都有原型;
对象.__proto__ 是原型(Person.prototype)
- 每个原型都有与之对应的构造函数;
构造函数.prototype 是原型(Person.prototype)
三、原型链
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
javascript
// 原型对象本身是一个普通对象,而普通对象的构造函数都是Object
Person.prototype.__proto__ == Object.prototype
// Object的原型对象也有__proto__属性指向null,null是原型链的顶端
Object.prototype.__proto__ == null
总结:一切对象都是继承自Object
对象,Object
对象直接继承根源对象null
。