JS类
众所周知,JS没有类的概念,所谓类就是基于原型链和继承实现的,更多时候我们叫他对象,然后把对象叫做引用类型的实例。ES6中加入了class、extend关键字实现类和继承,仅仅是基于现有的原型继承的一种语法糖。
实现继承
实现继承是继承实际的方法。
EMScript只支持实现继承,实现继承主要依靠原型链实现,但不适宜单独使用,解决原型链的问题可以采用借用构造函数。使用最多的继承模式是组合继承。
原型链
原型链是实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
构造函数、原型和实例的关系:
(1)每一个构造函数都有一个原型对象,且构造函数都有一个属性(prototype)指向原型对象。
(2)原型对象都包含一个指向构造函数的指针(构造函数属性constructor)
(3)实例都包含一个指向原型对象的内部指针(内部属性[[prototype]])
所以原型链的具体实现是:重写原型对象,让一个原型对象等于另一个原型的实例。
不可以忘记默认的原型:
所有引用类型默认都继承了Object。就是a继承了b,b继承了c,重要的是c继承了Object。
原型链注意要点:
(1)子类型要覆盖超类型的某个方法或者子类型要添加超类型中不存在的方法,必须得在继承之后再覆盖或添加。
(2)不能用对象字面量创建原型方法,会重写原型链。
确定原型和实例的关系:
(1)instanceof()操作符测试实例与原型链出现过的构造函数
(2)isPrototypeOf()方法测试原型链出现过的原型就是该原型链所派生的实例的原型。
原型链的问题:
(1)通过原型来继承时,原先的实例属性会变成现在的原型属性。就会出现原型模式的缺点:引用类型值的原型属性会被所有实例共享。
(2)创建子类型的实例时,不能向超类型的构造函数传递参数。
function One(){
this.num=[1,2,3];
};
function Two(){};
//Two继承了One,Two变成了One的实例
//Two是子类型,One是超类型
Two.prototype=new One();
var f1=new Two();
f1.num.push(55);
var f2=new Two();
console.log(f1.num) //[1, 2, 3, 55]
console.log(f2.num) //[1, 2, 3, 55]
借用构造函数(伪造对象或经典继承)
优点:解决原型中包含引用类型值所带来的问题
要点:在子类型构造函数的内部调用超类型的构造函数。通过使用call()和apply()方法可以在(将来)新建的对象上执行构造函数
function One(numb){
this.num=[1,2,3];
this.numb=numb;
};
function Two(){
One.call(this,"44");
};
var f1=new Two();
var f2=new Two();
f1.num.push(55);
console.log(f1)
console.log(f2)
缺点:会发生构造函数存在的问题——无法进行函数复用
组合继承(伪经典继承)(使用最多)
原理:将原型链和借用构造函数组合。原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。
function One(numb) {
this.num = [1, 2, 3];
this.numb = numb;
};
One.prototype.sayNumb = function() {
console.log(this.numb)
};
function Two(numb, nums) {
One.call(this, numb);
this.nums = nums;
};
Two.prototype = new One();
//必须得写在继承后面
Two.prototype.sayNums = function() {
console.log(this.nums)
};
var f1 = new Two(11, 22);
var f2 = new Two(33, 44);
f1.num.push(55);
console.log(f1);
console.log(f2);
f1.sayNumb(); //11
f2.sayNumb(); //33
f1.sayNums(); //22
f2.sayNums(); //44
原型式继承
ES5通过Object.create()方法规范了原型式继承
function One(numb) {
this.num = [1, 2, 3];
this.numb = numb;
};
One.prototype.sayNumb = function() {
console.log(this.numb)
};
function Two(numb, nums) {
One.call(this, numb);
this.nums = nums;
};
//重点
Two.prototype = Object.create(One.prototype)
Two.prototype.sayNums = function() {
console.log(this.nums)
};
var f1 = new Two(11, 22);
var f2 = new Two(33, 44);
f1.num.push(55);
console.log(f1);
console.log(f2);
f1.sayNumb();
f2.sayNumb();
f1.sayNums();
f2.sayNums();