原型链
Created At :
Views 👀 :

构造函数,原型和实例的关系:
每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.
如果试图引用对象(实例instance)的某个属性,会首先在对象内部寻找该属性,直至找不到,然后才在该对象的原型(instance.prototype)里去找这个属性.
原型链继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 问题1:原型中包含的引用类型属性将被所有实例共享; 问题2:子类在实例化的时候不能给父类构造函数传参; function Animal() { this.colors = ['black', 'white'] } Animal.prototype.getColor = function() { return this.colors } function Dog() {} Dog.prototype = new Animal()
let dog1 = new Dog() dog1.colors.push('brown') let dog2 = new Dog() console.log(dog2.colors) // ['black', 'white', 'brown']
|
借用构造函数实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| //父类:人 function Person () { this.head = '脑袋瓜子'; this.emotion = ['喜', '怒', '哀', '乐']; //人都有喜怒哀乐 this.eat = function () { console.log('吃吃喝喝'); } this.sleep = function () { console.log('睡觉'); } this.run = function () { console.log('快跑'); } } //子类:学生,继承了“人”这个类 function Student(studentID) { this.studentID = studentID; Person.call(this); } //Student.prototype = new Person();
var stu1 = new Student(1001); console.log(stu1.emotion); //['喜', '怒', '哀', '乐']
stu1.emotion.push('愁'); console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"] 们通过借用构造函数继承,保证了 stu1 和 stu2 都有各自的父类属性副本,从而使得各自 emotion 互不影响。但同时带来的问题是,stu1 和 stu2 都拷贝了 Person 类中的所有属性和方法,而在 Person 类中,像 eat ( ), sleep ( ), run ( ) 这类方法应该是公用的,而不需要添加到每个实例上去,增大内存,尤其是这类方法较多的时候。 var stu2 = new Student(1002); console.log(stu2.emotion); //["喜", "怒", "哀", "乐"]
|
组合继承(结合使用两种继承模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 方法挂载到父类的原型对象上去,实现方法复用,然后子类通过原型链继承 //父类:人 function Person () { this.head = '脑袋瓜子'; this.emotion = ['喜', '怒', '哀', '乐']; //人都有喜怒哀乐 } //将 Person 类中需共享的方法放到 prototype 中,实现复用 Person.prototype.eat = function () { console.log('吃吃喝喝'); } Person.prototype.sleep = function () { console.log('睡觉'); } Person.prototype.run = function () { console.log('快跑'); } //子类:学生,继承了“人”这个类 function Student(studentID) { this.studentID = studentID; Person.call(this); } Student.prototype = new Person(); //此时 Student.prototype 中的 constructor 被重写了,会导致 stu1.constructor === Person Student.prototype.constructor = Student; //将 Student 原型对象的 constructor 指针重新指向 Student 本身
var stu1 = new Student(1001); console.log(stu1.emotion); //['喜', '怒', '哀', '乐']
stu1.emotion.push('愁'); console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"] var stu2 = new Student(1002); console.log(stu2.emotion); //["喜", "怒", "哀", "乐"]
stu1.eat(); //吃吃喝喝 stu2.run(); //快跑 console.log(stu1.constructor); //Student 首先,我们将 Person 类中需要复用的方法提取到 Person.prototype 中,然后设置 Student 的原型对象为 Person 类的一个实例,这样 stu1 就能访问到 Person 原型对象上的属性和方法了。其次,为保证 stu1 和 stu2 拥有各自的父类属性副本,我们在 Student 构造函数中,还是使用了 Person.call ( this ) 方法。如此,结合原型链继承和借用构造函数继承,就完美地解决了之前这二者各自表现出来的缺点。
|
寄生式组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; }
Parent.prototype.getName = function () { console.log(this.name) }
function Child (name, age) { Parent.call(this, name); this.age = age; }
function object(o) { function F() {} F.prototype = o; return new F(); // 通过构造一个介于 Parent 与 Child 之间的对象,并使该对象的 prototype 属性指向 Parent 的 prototype对象, // 来避开通过调用 Parent 构造函数的方式来产生一个 prototype 指向Parent prototype对象的对象。 }
function prototype(child, parent) { // 不直接child.prototype=parent.prototype呢? // 原因 : 当我们想给 Child 的prototype里面添加共享属性或者方法时,如果其 prototype 指向的是 Parent 的 prototype,那么在 Child 的 prototype 里添加的属性和方法也会反映在 Parent 的 prototype 里面, // 这明显是不合理的,这样做的后果是当我们只想使用 Parent 时,也能看见 Child 往里面扔的方法和属性。 // 所以需要每个构造函数都需要持有自己专用的prototype对象 var prototype = object(parent.prototype); prototype.constructor = child; child.prototype = prototype; }
prototype(Child, Parent);
var child1 = new Child('kevin', '18');
console.log(child1);
|
class 实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Animal { constructor(name) { this.name = name } getName() { return this.name } } class Dog extends Animal { constructor(name, age) { super(name) this.age = age } }
|
确定原型和实例的关系
第一种是使用 instanceof 操作符, 只要用这个操作符来测试实例(instance)与原型链中出现过的构造函数,结果就会返回
1 2 3
| var d = new Date(); d instanceof Date;//=>true d是Date的实例 d instanceof Object;//=>true 所有对象都是Object的实例
|
第二种是使用 isPrototypeOf() 方法, 同样只要是原型链中出现过的原型,isPrototypeOf() 方法就会返回true,
1 2 3
| var d = new Date(); Date.prototype.isPrototypeOf(d);//=>true Object.prototype.isPrototypeOf(d);//=>true
|
---