前言
如果想真正理解实例,原型,原型链是什么,以及他们的关系我们就必须搞清楚基本的概念性知识。
函数
构造函数部分我们要了解以下几个问题:
- 函数是什么?
- 构造函数与普通函数有什么区别?
- 什么是构造函数?
答:
- 函数是对象。
- 构造函数与普通函数没有区别,只不过按照惯例,构造函数命名要采取大驼峰写法。
- 使用了new操作符调用的函数,就是构造函数。函数本身并没有是不是构造函数的概念,只不过是你调用的方式不同,赋给了他不同的概念。
创建一个构造函数:
function Person(){};
console.log(Person instanceof Object); // true
let person = new Person(); // Person被作为构造函数调用,并不妨碍Perosn当作普通函数使用
Person(); // 我是当作普通函数被调用
实例
实例部分我们需要了解一个问题:
- 那么是什么实例?
答:
- 使用new操作符创建的新对象就是实例。
理解:你可以将Person函数当作人的模型,将lucy变量当作具体的一个人。
function Person(name){
this.name = name;
};
let lucy = new Person('lucy'); // lucy就是实例
console.log(lucy.name); // lucy
原型
原型部分我们需要了解一下几个问题:
- 原型的作用。
- 原型在哪里。
答:
- 原型是用于存放公共的属性和方法,并且在原型上定义的属性和方法可以被实例共享。
- 函数的prototype属性就是原型。
下面让我们具体体验下原型的作用,我们先不使用原型的知识来创建两个实例。
function Person(name) {
this.name = name;
this.greet = function() {
console.log(`Hello, My name is ${this.name}`);
};
}
lucy = new Person('Lucy');
lucy.greet(); // Hello, My name is Lucy
bob = new Person('Bob');
bob.greet(); // Hello, My name is Bob
那么让我们比较lucy.greet方法与bob.greet方法两者是否相等。
console.log(lucy.greet == bob.greet); // false
结果显而易见,两者并不相等。相同逻辑的函数被重复定义在了各自的实例对象上。这就造成了资源浪费。所以更好的解决方式就是定义在原型上,让所有实例共用一个方法。
我们来使用原型来重写上个案例:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, My name is ${this.name}`);
};
lucy = new Person('Lucy');
lucy.greet(); // Hello, My name is Lucy
bob = new Person('Bob');
bob.greet(); // Hello, My name is Bob
console.log(lucy.greet == bob.greet); // true,我们发现两者相同,因为它们都是指向原型上的该方法。
构造函数、原型和实例的关系
要了解原型链,首先我们需要知道它们三者之间的联系。下面以一张图案帮助大家理解。
由上图可知,有两个两个东西,构造函数Person与Person的实例father。这里面最重要的一点就是:
实例(father)上有一个属性(proto)指回Person.prototype。 那么我们验证一下:
function Person() {}
lucy = new Person();
console.log(lucy.__proto__ === Person.prototype); // true
当然我们还看见,这个知不知道都行:
构造函数(Person)的原型对象(prototype)上有内部属性constructor和其他自定义属性,且constructor属性指回构造函数。想要验证的可以输入:console.log(Person.prototype.constructor === Person)自行验证。
原型链
结合以上知识我们现在知道原型是可以被实例共享的,那么如果原型是另一个实例呢?这其实就是原型链的基本构想。具体实现如下:
function Person () {}
Person.prototype.greet = function() {
console.log(`Hello, My name is ${this.name}`)
}
function Lucy(name) {
this.name = name
}
Lucy.prototype = new Person();
Lucy.prototype.sayName = function() {
console.log(this.name)
}
let lucy = new Lucy('lucy');
lucy.sayName() // lucy
lucy.greet() // Hello, My name is Lucy
总结:原型这一部分知识比较绕,想要更加深入的了解还要看更多专业书籍和文章。就拿JavaScript高级程序设计这本书来说,它里面这一部分知识使用了很大篇幅才讲完,我这短短的博客自然有很多不足之处,所以本文仅供参考。
参考文献: JavaScript高级程序设计(第四版),我买的纸质书,这里就不放链接了[dog]。