在es6之前,是没有extends继承的,我们都是通过构造函数,原型对象来模拟,实现了继承.
为什么这么说呢?
这是因为我们是利用构造函数来继承属性,利用原型对象来继承方法的
.现在我们就可以通过call( ),apply( ),bind( )这三个方法也可以实现继承.
call( );
用法
fn.call(thisArg, arg1, arg2, ...)
thisArg : 当前调用函数this的指向对象;
arg : 传递的参数
call( )方法的作用
- 可以调用函数
function fn() {
console.log("123");
}
fn.call(); //123
这里我利用call方法也可以正常的调用函数
- 可以修改函数里面的this指向
首先我先看一下函数fn里面的this指向的是window
function fn() {
console.log("123");
console.log(this); //window{....}
}
var obj = {
name: '小明'
}
fn.call(); //123
fn.call(obj) //{name: "小明"}
这时 f n 这个函数里面的this就指向了obj这个对象
call()里面的第一个参数是指向的对象
如果我要传递参数的时候就可以这样做
function fn(a, b, c) {
console.log(a + b + c);
}
var obj = {
name: '小明'
}
fn.call(obj, 1, 2, 3) //6
直接跟普通函数一样传参就好了.
call( )里面的第一个参数是不参与传递的,只是改变this的指向
利用构造函数来继承属性
这两个构造函数里面的this指向
Cat构造函数里面的this指向的是它的对象实例
Dog构造函数里面的this指向的是它的对象实例
function Cat(name, age) {
this.name = name;
this.age = age;
}
function Dog(namem, age) {
}
如果我的Dog构造函数需要使用到Cat构造函数里面的属性,那我就可以把Cat里面的属性继承过来
这时候我可以这样
function Cat(name, age) {
this.name = name;
this.age = age;
}
function Dog(namem, age) {
//这里的this指向的是Dog构造函数的实例
Cat.call(this,name,age)
}
var dog = new Dog('小白', 2)
console.log(dog); //Dog{name:"小白",age:2}
在Dog构造函数里面通过call( )方法调用了Cat构造函数,然后把Cat构造函数的this改成了Dog构造函数的this,这样Dog构造函数就可以使用Cat构造函数里面的属性了. 然后把需要的参数传进去.
验证一下可以看到成功了, 这样就借用了构造函数来实现继承属性
核心原理:在子构造函数里面调用父构造函数,同时把父构造函数的this指向子构造函数的this,
利用原型对象来继承方法
function Cat(name, age) {
this.name = name;
this.age = age;
}
Cat.prototype.eat = function() {
console.log('我会吃饭');
}
function Dog(name, age) {
Cat.call(this, name, age)
}
var dog = new Dog('小白', 2)
console.log(dog);
现在我在Cat原型对象上添加了一个eat的方法,这时我Dog构造函数需要继承我这个方法,那我应该怎么做呢???
那我能不能这样呢,把我Dog构造函数的原型对象指向Cat的原型对象呢? 那我试试
function Cat(name, age) {
this.name = name;
this.age = age;
}
Cat.prototype.eat = function() {
console.log('我会吃饭');
}
function Dog(name, age) {
Cat.call(this, name, age);
}
//这一步
Dog.prototype = Cat.prototype;
var dog = new Dog('小白', 2)
console.log(dog);
写完后我验证一下,可以看到是可以拿到这个方法的
好了,那就实现了方法的继承,是不是很美好.
问题来了
那如果Dog里面有自己独享的方法呢?我再来看看
我在Dog原型对象上面写了一个Dog独享的方法
Dog.prototype.eatBone = function() {
console.log('我会啃骨头');
}
毫无疑问,Dog的实例对象可以访问到这个方法,那我Cat构造函数能不能访问到这个方法呢.我打印一下,看看有木有问题哇
那我去Cat构造函数的原型对象上看看哈…
console.log(Cat.prototype);
咦…看到了吧,本来我Dog构造函数里面的eatBone是自己独享的
可是我打印出来的时候,这个方法也被Cat构造函数共享了.
这就是大问题了,也就是说,我不想给你的你却也能得到,是不是很伤心呢?
接下来就解决这个问题;
原因就是我那一步操作
Dog.prototype = Cat.prototype;
也就相当于我把Dog构造函数的原型对象指向了Cat构造函数的原型对象虽然可以把Cat里面原型对象上的方法继承过来,但是反过来说Dog里面原型对象上的一些自己的方法也被传过去了;
所以我们千万不能这样做,因为如果修改了子原型对象的话,父原型对象也会跟着变
那我们该怎么解决这个问题???
我们可以这样
Dog.prototype = new Cat();
然后我再验证
console.log(Cat.prototype)
看成功了,给Dog原型对象上添加方法,对Cat原型对象没有任何影响
我们来看看原理这一步操作的原理
Dog.prototype = new Cat();
我 new Cat( ),就相当于创建了一个实例对象(实例化一个对象).
然后把实例化的对象赋值给了Dog.prototype,就相当于把Dog的原型对象指向了刚刚创建的实例对象
那我在问你,我Cat的实例对象能访问到我Cat的原型对象吗?
那肯定是可以的对不对;说到这相信大家都明白怎么回事了吧
我画个图给你们瞧瞧,
这样大家都明白了吧, 再捋一捋
我 new Cat( ),就相当于创建了一个实例对象(实例化一个对象).
然后把实例化的对象赋值给了Dog.prototype,就相当于把Dog的原型对象指向了刚刚创建的实例对象
又因为实例对象里面有__proto__原型,通过这个原型我们就可以访问原型对象.而这个原型对象上有eat( )这个需要被继承的方法.就这样,我Dog的原型对象就可以间接的访问到Cat的原型对象,就可以拿到那里面的方法.
我Dog里面私有的方法还是自己的,就算改变了自己的原型对象,对Cat的原型对象也没有任何影响;
这样做法还是有一个问题的,不知道大家有没有发现
Dog.prototype = new Cat();
我这样是不是一个赋值操作呀,这样写过后,我Dog.prototype里面的constructor属性呢???(上一篇文章说道)
是吧,所以我们到最后还是要把它加上
//重新指回Dog构造函数
Dog.prototype.constructor = Dog;
最后完美成功实现继承了
function Cat(name, age) {
this.name = name;
this.age = age;
}
Cat.prototype.eat = function() {
console.log('我会吃饭');
}
function Dog(name, age) {
Cat.call(this, name, age)
}
Dog.prototype = new Cat();
//重新指回Dog
Dog.prototype.constructor = Dog;
// Dog.prototype = Cat.prototype; //不能这样做
Dog.prototype.eatBone = function() {
console.log('我会啃骨头');
}
var dog = new Dog('小白', 2)
console.log(dog);
console.log(Cat.prototype);