开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
原型(一)
构造函数
了解原型之前我们需要明白另外一件事情,什么是构造函数。
function student(){} 这是一个函数,我们可以直接调用它student()因为它的函数体是空的,所以不会执行任何操作。
但我们还有另外一种办法调用它 那就是new let a = new student()这句代码意思是 创建一个 student 类型的对象 a 把student()函数当构造函数使用。
其实所有的函数只要使用new来调用都是构造函数,反过来说构造函数和函数的区别就是是否用new来调用。
function student() {}
let a = new student();
console.log(a); //student {}
console.log(a instanceof student); //true
下面我们给出一个稍微标准点的构造函数。
function student(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}
const a = new student("小红", "18");
const b = new student("小明", "20");
console.log(a.name); // 小红
a.sayName(); // 小红
console.log(b.name); // 小明
b.sayName(); // 小明
console.log(a.sayName === b.sayName); //false
这里我们通过构造函数实例化了两个对象a和b,但是请注意最后一行,a.sayName和b.sayName并不是同一个函数,这代表需要在内存中存放两个同样的sayName函数,如果我实例化一百个对象,那么就需要存放一百个同样的函数在内存中,这显然是不好的,后面我们会通过原型来优化这个“不好”的构造函数。
构造函数的原型
每个函数创建时js就会为这个函数创建一个prototype属性,这个属性是一个对象,也就是我们说的原型(原型对象)。
默认情况下原型对象会自动获得一个constructor属性,指回与之关联的构造函数。
function student() {}
console.log(student.prototype);// {constructor: ƒ student()}
console.log(student.prototype.constructor === student); //true
现在我们知道了,每个函数都有一个原型(原型对象),其中默认包括一个constructor属性,而这个属性指向关联的构造函数,可以理解为 谁的 原型对象 里面的constructor属性 指向它自己,小红的原型对象里面的constructor指向小红自己,小明的则指向小明自己。
实例的原型
除了构造函数之外,被构造函数new出来的实例也是有原型对象的,在chrome浏览器中可以通过__proto__属性来访问。(注意proto前面和后面是两个下划线)
function student() {}
let a = new student();
console.log(a.__proto__);// {constructor: ƒ student()}
这里我们发现实例的原型和构造函数的原型看起来一样,我们不妨测试一下。
function student() {}
let a = new student();
console.log(a.__proto__ === student.prototype); // true
其实实例的原型就是从他的构造函数那里得来的,所以得出结论;
1.构造函数有一个原型。
2.原型中有一个constructor属性,指向构造函数本身。
3.实例的原型是从构造函数得到的,实例的原型===构造函数的原型。
原型的作用
我们已经知道原型是一个对象,有一个默认的constructor属性,那么如果给原型对象新增一些属性呢?
function student() {}
student.prototype.name = "小红";
student.prototype.age = "18";
student.prototype.sayName = function () {
console.log(this.name);
};
console.log(student.prototype);// {name:'小红',age:'18',sayName: ƒ (), constructor: ƒ student()}
student.prototype.sayName();// 小红
可以看到,成功添加并且可以使用,下面我们来创建一个实例。
function student() {}
student.prototype.name = "小红";
student.prototype.age = "18";
student.prototype.sayName = function () {
console.log(this.name);
};
const a = new student()
console.log(a.name); // 小红
a.sayName();// 小红
这里发现一个问题,我们实例化一个对象a但是我们并没有给a赋任何属性,但是我们访问a.name和a.sayName(),却能正常访问,输出的值刚好是原型中对应的值,why?
因为通过对象访问属性时,会首先搜索对象本身有没有这个属性,如果有则返回,如果没有则去搜索对象的原型有没有这个属性。
因此调用a.name会发生两步搜索。
第一步js引擎会问 对象a有name这个属性吗? 答案是没有。
第二步js引擎会问 对象a的原型有name这个属性吗? 答案是有,返回原型的name属性。
调用a.sayName()时会发生同样的搜索。
还记得我们那个“不好”的构造函数吗?我们现在利用原型来改造一下。
function student(name, age) {
this.name = name;
this.age = age;
}
student.prototype.sayName = function () {
console.log(this.name);
};
const a = new student("小红", "18");
const b = new student("小明", "20");
a.sayName(); // 小红
b.sayName(); // 小明
console.log(a.sayName === b.sayName); //true
问题顺利解决,不管创建多个对象,sayName这个函数也只需要在内存中存储一次了。
菜鸟前端分享学习经验,希望对你有所帮助。