一,js中的类
1,js类的概念
每个对象都是属性的集合,但是他们都是独立的。想让一部分对象共有一些属性,就可以使用类去定义。
js中的类是基于原型继承机制的。
2,类的使用
定义类:function Person() { ... }
封装普通属性:this.name = xxx; this.age = xxx;
封装原型属性:Person.prototype.eat = xxx;
实例化:var p = new Person();
3,new关键字原理
在调用new关键字实例化一个对象时,实际上进行了以下步骤:
(1)var p = {}; // 创建一个空对象
(2)p.__proto__ = Person.prototype; // 将对象的隐式原型__proto__指向构造函数的显示原型prototype
(3)Person.call(obj) ; // 将this指向改变,指向实例对象
二,继承
1,js继承的目的
让子类的实例也拥有父类的私有属性和公共方法
2,ES5的继承方法
2.1 使用原型链继承
方式:即利用原型链的机制,让子类的原型prototype等于父类的实例即可。
示例:
// 父类
function Parent() {
this.x = '123'; // 父类私有属性、方法
}
Parent.prototype.eat = function(a, b) { // 父类公有属性、方法
console.log(a + b);
}
// 子类
function Child() {
this.y = '456'; // 子类私有属性、方法
}
Child.prototype.fly = 'flyer'; // 子类公有属性、方法
// 继承
Child.prototype = new Parent;
// 使用:实例化一个子类对象
var c1 = new Child;
console.log(c1);
优点:实现简单;可以继承父类的私有和公有属性。
缺点:不能实现多继承;原型对象的属性,都被所有的子类实例共享。其中一个子类修改属性,其他的子类属性也跟着改变
2.2 使用构造函数通过call或apply继承
方式:即在子类的构造函数中,通过call或apply强制将父类的this指向修改为子类,这样子类便被强制继承了父类的私有属性。
示例:
// 父类
function Parent() {
this.x = '123';
}
// 子类
function Child() {
// 改变this指向为Child,将父类当做普通函数执行(没有父类实例,他原型上的属性就和父类没关系了)
Parent.call(this);
}
// 使用:实例化一个子类对象
var c1 = new Child;
console.log(c1);
优点:可以实现多继承,即call多个父类;解决了子类实例共享父类引用属性的问题
缺点:只能继承父类中私有的,公有的不行(并且是把父类私有的变成子类私有的)
2.3 组合继承
方式:即综合原型继承和构造函数使用call继承的方式。
理解:因为构造函数+call继承方式 解决了原型链继承方式 不能多继承和共享属性修改所有实例也跟着变化的缺点,但因为构造函数+call继承方式只能继承父类的私有属性,无法继承父类的公有属性,因此组合继承是为了解决这个方式实现的。
示例:
// 父类
function Parent() {
this.x = '123'; // 父类私有属性、方法
}
Parent.prototype.eat = function(a, b){ // 父类公有属性、方法
console.log(a + b);
}
// 子类
function Child() {
Parent.call(this); // 父类改变this指向为子类
this.y = '456'; // 子类私有的属性
}
// 组合原型方式继承
Child.prototype = Object.create(Parent.prototype); // 子类的原型指向父类的实例(通过Object.create方法)
Child.prototype.constructor = Child; // 手动增加构造函数
// 子类的公有属性、方法
Child.prototype.fly = 'flyer';
// 使用
var c1 = new Child;
console.log(c1)
缺点:
(1)组合创建的子类原型没有构造函数,需要手动创建。
(2)让子类的原型指向父类的实例需要操作子类Child.prototype.__proto__ = Parent.prototype;但是IE浏览器不允许操作__proto__隐式原型,因此需要使用Object.create()方法。
附:Object.create(proto, [property]) -- 以指定的原型proto创建对象(参数可以是null,但不能是undefined),property参数可选,新对象的属性设置
3,ES6的继承方法
方式:ES6增加关键字class、constructor、super、extends等关键字,实现继承。原理类似于ES5的组合继承。
方式:
// 父类
class Parent{
constructor() {
this.x = '123'; // 私有
}
eat(a, b) { // 公有
console.log(a + b);
}
}
// 子类,继承父类
class Child extends Parent{
constructor() {
super(); // 执行父类的构造函数
this.y = '456'; // 子类私有
}
fly() { // 子类公有
console.log('fluer');
}
}
// 使用
var c1 = new Child;
console.log(c1);
特点:super()函数相当于ES5中的call方式,执行了父类的构造函数,且可以传参。