Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript面向对象之二(构造函数继承) #20

Open
Pomelo1213 opened this issue Apr 13, 2018 · 0 comments
Open

JavaScript面向对象之二(构造函数继承) #20

Pomelo1213 opened this issue Apr 13, 2018 · 0 comments
Labels
JavaScript JavaScript

Comments

@Pomelo1213
Copy link
Owner

学习一波阮一峰的博客 戳这里 👍

博客中是自己的理解,以及对大佬描述不清楚的地方进行了修正,也算是自己的一个再(xiao)产(tu)出(cao)吧

上一篇:JavaScript面向对象之一(封装)

构造函数进行继承


先来看个简单的:

function Animal(){
    this.type = 'animal;'
}

function Cat(name, color){
    this.name = name
    this.color = color
    //这里用call,个人觉得更好些
    Animall.call(this)
}

var cat = new Cat('po', 'orange')
console.log(cat.type) //animal

创建了一个Animal和Cat构造函数,然后在Cat里面调用Animal的构造函数,在将Cat实例化,就可以访问到Animal的属性了。
这个例子显然有问题,放在第一个就是用来找茬的。

什么问题呢?假如我想使用在Animal上的公共方法,像这样Animal.prototype.eat = function(){ console.log('animal eat') } ,用cat.eat()是访问不到的。

为什么呢?因为我们Cat和Animal的原型根本就没有关联起来呀。你看看咱们上面的代码,那个地方关联过?

使用原型进行继承


那下面我们就将两者的原型关联起来试试看

function Animal(){
    this.type = 'animal;'
}
Animal.prototype.eat = function(){ console.log('animal eat') }

function Cat(name, color){
    this.name = name
    this.color = color
}

Cat.prototype = new Animal()

var cat = new Cat('po', 'orange')
console.log(cat.type) //animal
console.log(cat.eat()) //animal eat

这种方法好!可以拿到属性和方法,一举两得。但是,这里有个陷阱(keng)!!!Cat.prototype = new Animal()之后,我们的Cat.prototype里面的所有方法都消失了!这是怎么回事?因为new,new做了四件事,这里再次回顾一下:

var temp = {}
temp.__proto__ = Animal.prototype
Animal.call(temp)
return temp

看到了吗?new会使用一个空对象并且将其返回。这样一来我们的Cat.prototype就被清空了(包括自身的constructor)。所以我们需要自己多做一件事Cat.prototype.constructor = Cat

如果我们不这样做,那么Cat.prototype的构造函数会是什么?我们在去看看new做的第二件事,这个空对象指向了Animal.prototype,所以Cat.prototype自身如果没有constructor属性的话就会去Animal.prototype上面去找。 Cat.prototype.constructor === Animal //true

所以,如果我们替换了prototype,需要手动去纠正它。

直接继承prototype


既然上面这种方法有坑,而且的的确确让你很容易漏掉,那我们改进一下:

function Animal(){}
Animal.prototype.eat = function(){ console.log('animal eat') }

Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat

var cat = new Cat('po', 'orange')
console.log(cat.eat()) //animal eat

既然我们想从Animal.prototype上面那东西,直接从上面拿不就行了?而且我还机智的填了上面会出现的坑。同时结果也是我想要的。

但是!!我们的Cat.prototype和Animal.prototype指向的是同一个原型,这会导致我在Cat.prototype上做了什么事,会同时发生在Animal.prototype上。这是为什么呢?MDZZ,这两个就是同一个东西呀,原型是堆中一块内存,Cat和Animal都指向这块内存,操作的是同一个东西,怎么会不影响?

与此同时,自以为聪明的填坑Cat.prototype.constructor = Cat ,此时的Cat和Animal的原型是同一个,修改了constructor之后,导致Animal的constructor变成了Cat。这种方法果断PASS。。。

利用空对象作为中介


var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat

我们使用中间对象进行过度,巧妙的将Cat.prototype和Animal.prototype解耦,这样就不会出现问题了。仔细观察会发现这个和new做的事情有异曲同工之处。

我们进行封装一下在使用

function extend(Child, Parent) {
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
}

extend(Cat,Animal);
var cat1 = new Cat('po' 'orange');
alert(cat1.eat()); // animal eat

这里Child.uber = Parent.prototype的意思类似于__proto__

拷贝继承


这里还有一种简单粗暴的方式

function extend2(Child, Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
        c[i] = p[i];
    }
    c.uber = p;
}

function Animal(){}
Animal.prototype.eat = function(){ console.log('animal eat') }

extend2(Cat, Animal);
var cat1 = new Cat('po' 'orange');
alert(cat1.eat()); // animal eat

直接遍历父类的原型,一个个复制到子类原型上即可。

感慨一下,大佬还是大佬呀。。。:smile:

@Pomelo1213 Pomelo1213 added the JavaScript JavaScript label Aug 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript JavaScript
Projects
None yet
Development

No branches or pull requests

1 participant