原型与原型链是JS中逃避不过的知识点,那么到底什么是原型与原型链呢?不急,且听我慢慢道来:
一、为什么会出现原型与原型链
关键字:实现继承,共享属性
众所周知,JS最初的想法只是想设计一门简单脚本语言,可是JS里毕竟都是对象,总需要有一个东西将其全部联系起来,于是Brendan Eich(JS语言设计的负责人)想到了通过new来构造实例对象,可是new有一个缺点-----无法共享属性和方法,构造出来的对象都是相互独立的,这可万万不行,太消耗资源了。如下,万一有一天我的宠物种类就全变成“猫猫”了呢,但是把pet1的种类修改过后,pet2的种类仍是“狗狗”,这怎么行呢?
function MyPet(name){
this.name = name;
this.species = "狗狗";
}
var pet1 = new MyPet("小白");
var pet2 = new MyPet("花花");
pet1.species = "猫猫";
pet2.species ===> "狗狗"
于是Brendan Eich便决定在构造函数上再设置一个prototype属性,将构造共有的属性与方法都写在这上面,那些不需要共享的就放在构造函数里面,这样不就大大的节约资源了吗?如下:
function MyPet(name){
this.name = name;
}
MyPet.prototype = {species:"狗狗"};
var pet1 = new MyPet("小白");
var pet2 = new MyPet("花花");
pet1.species = "狗狗";
pet1.species = "狗狗";
这样,当需要改变我的宠物种类时,只需要将prototype的species属性值改成“猫猫“就可以了
//修改过后
MyPet.prototype.species = "猫猫"
pet1.species ===》 "猫猫";
pet1.species ===》 "猫猫";
总结:这样一来,所有的实例对象就可以共享prototype的属性,而prototype就是我们所说的原型。
好了,前菜上完了,下面进入我们今天的主体部分。
二、详解
2.1 prototyep(原型)
根据上面的描述可以知道,prototype里会存放所有共享的属性和方法,所以在JS内部,prototype是作为一个对象的方式存在的
function fn(){}
console.log(typeof fn.prototype) ==> Object
所以,对于构造函数来说,拥有了prototype就可以将一些属性和方法传递给所有的实例对象了,如下:
function Student(name,age){
this.name = name;
this.age = age;
}
Student.prototype.grade = "大学";
var s1 = new Student("小红",24);
var s2 = new Student("小明",25);
console.log(s1.grade); //大学
console.log(s2.grade); //大学
在这里,s1,s2的原型(__proto__)指向的就是Student的prototype ;
虽然说s1,s2这两个实例身上并没有特意去写grade这一属性,但是由于在构造函数Student的prototype上添加了grade属性,所以s1,s2自然而然就可以顺着原型,去Student的prototype上得到,也就是说当某个实例本身没有该属性或方法时,就会去其原型上找到
所以,只要通过更改Student上prototype的属性值,s1,s2的grade也就会跟着改变了,如下:
Student.prototype.grade = "初中";
console.log(s1.grade); //初中
console.log(s2.grade); //初中
当然了,如果某个实例对象本身就有该属性,则会直接访问自身的,如下:
Student.prototype.grade = "大学"
s1.grade = "小学"
console.log(s1.grade); //小学
console.log(s2.grade); //大学
2.2 原型链
根据JS的规定,所有对象都有自己的原型对象。一方面,任何一个对象都可以充当其他人的原型,两一方面,由于原型对象也是对象,所以也有自己的原型。因此,就会形成一个原型链,就像上面说的,如果某一个实例对象没有该属性或方法,就会去其原型对象上找,直到找到为止,话不多说,先看代码,然后写一个完整的链条:
function Student(name,age){
this.name = name;
this.age = age;
}
Student.prototype.grade = "大学";
var s1 = new Student("小红",24);
var s2 = new Student("小明",25);
s1.grade = "初中";
console.log(s1.grade); //初中
console.log(s2.grade); //大学
s2.__proto__ == Student.prototype //true
当然了,如果连Student都没有我们要的属性的话那就只能再往上找了,在JS中,所有原型都可以追朔到Object.prototype上,这就是所有对象都有valueof和toString的原因,因为这两个方法就在Object.prototype上,但值得注意的是JS中Object.prototype.__proto__为null,因此原型链的尽头是null。
初次写博客,不足之处,还请见谅,欢迎随时补充!
最后,以上部分思想来自阮一峰老师的”Javascript继承机制的设计思想“,网址:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html