1.构造函数、原型和实例的关系
在了解原型链之前我们首先要清楚这三部分之间的关系,做一个比喻,假设我们需要生产某个产品,那么构造函数就是加工产品的工厂,原型就是产品的生产图纸,实例就是生产出来的产品。
我们通过图纸可以生产出大大小小许多的产品,他们都含有图纸所有的特点,并且通过工厂来加工。
上面这句话“翻译”过来就是,我们通过原型可以定义许许多多的实例,所有的实例都继承有原型的所有属性,并且通过构造函数来生成。
下面举个例子来说明:
//首先创建一个构造函数,输入两个参数名字和运动项目
function Student(name, sports) {
this.name = name; this.sports = sports;
this.goClass = function () {
console.log(this.name + "上课");
};
}
//用这个构造函数创建两个实例对象
var stu1 = new Student("小明", "篮球");
var stu2 = new Student("小红", "排球");
//将它们输出
console.log(stu1);
console.log(stu2);
这是上面的输出结果,可以看到两个实例的内容:
接下来我们对它们的原型做出一些改动:
//给构造函数原型加入新的属性 age
Student.prototype.age = 20;
//接下来访问实例中的age
console.log(stu1.age);
console.log(stu2.age);
访问成功:
说明实例对象继承原型属性成功!
2.__proto__和prototype
这两个都是原型链中的相关内容,接下来我们捋一捋他们的关系。
__proto__:指向上级构造函数的原型(prototype)。
prototype:当前对象的原型(构造函数特有)。
我们可以从上面console.log(stu1)的结果中看看:
我们可以看到实例对象stu1的__proto__指向的是Object,结合上面__proto__的含义我们可知这里指向的是stu1的上级构造函数Student的原型,这里我们需要知道的是,JavaScript 中Object是最高等级的对象,也是其他对象的原型。
我们可以使用另一种方法来验证:
//分别输出两者
console.log(stu1.__proto__);
console.log(Student.prototype);
//比较两者是否完全相同
console.log(stu1.__proto__ === Student.prototype);
返回了true,可以看到的是,这两者的内容完全相同!
3.原型链
接下来就是一长串原型链的说明,让大家明白原型链的整个构造。
这里我们顺便介绍一下creat(),更加方便后面的介绍。
create():可以让一个对象继承自另一个对象,并且拥有这个对象的全部属性,也可以拥有自己的属性。
//用creat创建一个newStu继承于stu1
var newStu = Object.create(stu1);
newStu.name = "小刚";
newStu.sports = "足球";
console.log(newStu);
//原型链展示
//输出newStu的原型
var protoOfnewStu = Object.getPrototypeOf(newStu);
console.log(protoOfnewStu);
//输出newStu原型(stu1)的原型
var protoOfstu1 = Object.getPrototypeOf(protoOfnewStu);
console.log(protoOfstu1);
//输出newStu原型(stu1)的原型(Student)的原型
var protoOfStudent = Object.getPrototypeOf(protoOfstu1);
console.log(protoOfStudent);
//输出newStu原型(stu1)的原型(Student)的原型(Object)的原型
var protoOfObj = Object.getPrototypeOf(protoOfStudent);
console.log(protoOfObj);
我们可以看到newStu的原型是stu1,stu1的原型是Student,Student的原型是Object,Object的原型是null。
上面就是一条完整的原型链,最高级是Object,而Object的原型则是null。原型链的存在让对象间的继承逻辑更为的紧密!