1 继承的基本概念
继承是指一个对象直接使用另一对象的属性和方法
js里常用的如下三种继承方式:
通过原型链方式实现继承(对象间的继承)
类式继承(构造函数间的继承)
组合继承
由于js不像Java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现。
2 程序中的继承方法
1 原型继承
/**
* 优点:
* 实例是父类的实例也是子类的实例
* 父类新增原型方法或者原型属性 子类都能访问到
* 简单 易于实现
* 缺点:
* 无法实现多继承
* 来自原型对象的引用属性和实例是所有子类共享的
* 创建子类实例的时候,无法向父构造函数传参
* 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
* **/
function Animal(name) {
this.name=name;//属性
this.sleep=function () {//实例方法
console.log(this.name+'正在睡觉')
}
}
//原型方法
Animal.prototype.eat=function (food) {
console.log(this.name+'正在吃'+food)
};
//原型链继承---核心:将父类的实例作为子类的原型
function Cat() {
}
Cat.prototype=new Animal('小花猫');
var cat=new Cat();
console.log(cat);
// cat._proto_====>Animal 可以使用Animal里面的name属性 sleep方法
// 也可以使用 cat._proto_._proto_的eat方法 即animal的原型方法
console.log(cat.name);
cat.eat('鱼');
cat.sleep();
2 构造函数继承(类式继承)
/**
* 构造继承 相当于复制父类的实例给子类 (没有用到原型)
* 只能使用父类的属性和方法 不能继承原型属性和方法 所以cat不能使用eat方法
* **/
function Animal(name) {
this.name = name;//属性
this.sleep = function () {//实例方法
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function (food) {
console.log(this.name + '正在吃:' + food);
};
function Cat(name) {
Animal.call(this, name);//Cat就可以使用Animal的
// this.name=name
}
var cat = new Cat('小黑猫');
console.log(cat);//只能使用Animal的属性和实例方法 不能使用原型的方法
//cat的_proto_(指向的是一个函数)是cat 构造器是Cat
console.log(cat.name);
cat.sleep();
cat.eat('鱼'); //不能使用原型方法,所以此处会报错
3 组合继承
function Animal(name) {
this.name = name;
this.sleep = function () {
console.log(this.name + "正在睡觉");
};
}
Animal.prototype.eat = function (food) {
console.log(this.name + "正在吃" + food);
};
function Cat(name) {
//借用构造函数继承属性成员
Animal.call(this, name)
}
//修改Cat原型的指向
Cat.prototype = new Animal();
Cat.prototype.show = function () {//可以在子类的原型上加方法 不影响父类
console.log("玩的很开心");
};
var cat = new Cat('小花猫');
console.log(cat);//cat可以直接使用animal的属性和实例方法
cat.sleep();
cat.eat('鱼');
cat.show();
3 Call()、 apply()、bind()介绍
函数内 this
指向的不同场景
函数的调用方式决定了 this
指向的不同:
调用方式 | 非严格模式this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
call的用法
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
语法格式:
函数.call(thisArg, arg1, arg2, ...);
thisArg
可选的。在 function 函数运行时使用的 this 值。
如果指定了 null 或者 undefined 则内部 this 指向 window
arg1, arg2, ...
指定的参数列表。
描述:
call()
允许为不同的对象分配和调用属于一个对象的函数/方法。
call()
提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
apply的用法
apply()
方法调用一个具有给定this
值的函数,以及作为一个数组提供的参数。
语法格式:
函数.apply(thisArg, [argsArray])
thisArg
必选的。在 func 函数运行时使用的 this 值。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。
apply()
与 call()
非常相似,不同之处在于提供参数的方式。
apply()
使用参数数组而不是一组参数列表。
bind()的用法
bind()
方法创建一个新的函数,在 bind()
被调用时,这个新函数的 this
被指定为 bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法格式:
函数.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
4 、ES6 中的继承
class Parent {
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
}
}
class Child extends Parent{
constructor(x,y,color){
super(x,y);
this.color = color;
}
toString(){
return super.toString();
}
}
/*
* 通过extends关键字继承了Parent类所有的属性和方法。
* 子类的constructor方法 和 toString方法中都出现了super关键字,
* 在这里表示父类的构造函数,用来新建父类的this对象。
* 子类必须在constructor方法中调用super方法,因为子类的this对象必须通过父类的构造函数完成创建,
* 并且得到与父类同样的实例属性和方法。如果不调用,子类就得不到this对象,就不能进行二次加工。
* 要注意的一个地方是,在子类的构造函数中,只有调用super才可以使用this关键字,
* 所以this关键字要放到super关键字之后,
* 是因为子类实例的构建,基于父类实例,只有super方法能调用父类实例。
*/
/*
* ES5的继承实质是先创建子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))
* ES6的继承实质是先将父类实例对象的属性和方法加到this上面(所以需先调用super方法),然后再用子类构造函数修改this
*/
class Child extends Parent{
}
//等同于
class Child extends Parent{
constructor(...args){
super(...args);
}
}
/*
* 如果子类没有定义constructor方法,这个方法会被默认添加,
* 也就是说不管有没有显示的定义,子类都有constructor方法。
*/
class Parent {
}
class Child extends Parent{
constructor(){
super();
}
}
/*
* super这个关键字既可以当做函数使用,也可以当做对象使用
* 作为函数调用时,代表父类的构造函数,子类构造函数必须执行一次super函数
* super虽然代表了父类Parent的构造函数,但是返回的是子类Child的实例
* 即super内部的this指的是Child,super在这里就相当于:
* Parent.prototype.constructor.call(this)
* super作为函数使用时,super()只能用在子类的构造函数中*/
class Parent {
outPut(){
console.log(1)
}
}
class Child extends Parent{
constructor(){
super();
console.log(super.outPut())
}
}
/*
* super作为对象时,在普通方法中指向父类的原型对象,静态方法中指向父类
* super在普通方法中代表Parent.prototype,所以super.outPut()就相当于
* Parent.prototype.outPut()
* 注意,父类实例上的方法或属性是无法通过super调用的*/