javascript中的继承[二] 基于构造函数(《ObjectOriented JavaScript》第六章)

上篇末尾遗留的解释:

js中函数也是对象,因此上图一共有六个对象

其中Person 和 Coder 是函数,他们有prototype 属性

Person的prototype属性是原生的,是一个内部生成的obj,有toString方法

Coder的portotype属性认为的指向 Person的一个实例person

其他的不赘述。

(一)把对象共享的属性放在构造函数的prototype属性中

前一篇文章介绍原型链的过程,我们会发现,没有必要给所有的子对象都添加相同的方法,只要构造函数的prototype属性中有这个方法即可。

/*
function Person(name){
    this.name = name;
    this.sayHello = function(){alert(this.name);};
}
*/

function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert(this.name);
};

var p = new Person("jim");
p.sayHello();

这样,就不需要为每个对象添加sayHello方法了。

function Coder(language,name){
   this.language = language;
       this.name = name;
}

Coder.prototype = new Person("无名");
Coder.prototype.code = function(){
    alert("i am a "+this.language+" coder");
};

var c1 = new Coder("js","jim");
c1.sayHello();
c1.code();

这样,Coder的每个对象也都可以访问到 Person中的方法了

c1本身没有sayHello方法,会到Coder的prototype属性中找。而这个属性指向了Person的一个实例,这个实例当然有办法执行sayHello方法咯

好处:

1.不用为每个对象实定义各自的方法

2.“子类”能够继承父类的方法

坏处:

1.Corder.prototype属性中含有一些不必要的私有属性,如name

(二)单继承 prototype

出于提高效率的考虑,可不可以Coder.prototype 指向 Person.prototype 呢?

/*
function Person(name){
    this.name = name;
    this.sayHello = function(){alert(this.name);};
}
*/

function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert(this.name);
};

function Coder(language,name){
   this.language = language;
       this.name = name;
}

Coder.prototype = Person.prototype;
Coder.prototype.code = function(){
    alert("i am a "+this.language+" coder");
};

var c1 = new Coder("js","jim");
c1.sayHello();
c1.code();

这样做的好处:

1.少实例化一个对象

2.找sayHello方法时,原型链更短

坏处:破坏了“父类”Person的接口

(三)使用临时构造函数,只继承公共方法,不继承私有属性

function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert(this.name);
};

function Coder(language,name){
   this.language = language;
       this.name = name;
}

var F = function(){};
F.prototype = Person.prototype;
Coder.prototype = new F();

Coder.prototype.code = function(){
    alert("i am a "+this.language+" coder");
};

var c1 = new Coder("js","jim");
c1.sayHello();
c1.code();

好处:Coder.prototype 不含有 name等私有属性

(四)封装上面的过程

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

通过这个方法,我们就可以很方便地继承一个“类”

最后一行代码需要解释一下

原始的构造函数的prototype属性中的constructor指向构造函数,这个属性,是的实例化对象obj可以通过 obj.constructor调用构造函数,再次构造新的对象。

开发实践中在一定的情形下会出现这种情况,因此当我们把prototype属性指向其他对象时,我需要人工的声明下constructor属性。

现在我们看看 Coder 通过 extend函数如何 继承

function extend(Child,Parent){
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}
function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert(this.name);
};

function Coder(language,name){
   this.language = language;
       this.name = name;
}

extend(Person,Coder);
Coder.prototype.code = function(){
    alert("i am a "+this.language+" coder");
};

var c1 = new Coder("js","jim");
c1.sayHello();
c1.code();
var c2 = new c1.constructor("php","lily");
c2.code();

好处:封装了extend方法,方便。YUI的extend方法就是这么实现的

我为了突出重点,故意疏漏了 子类如何方法父类的方法属性( 如果有需要的话)。

到目前为止,我们都是使用原型链的方式实现继承,除了原型链还有别的途径吗??有的,通过复制prototype属性。

原型链继承的实现列表

1.把对象共享的属性放在构造函数的prototype属性中

2.单继承 prototype(复制prototype)

3.使用临时构造函数,只继承公共方法,不继承私有属性

4.封装3的过程

(五)通过复制prototype属性实现继承

我们除了通过原型链 实现 继承,还可以让Child 复制 Parent的 的prototype属性,这样做的好处:

Child的prototype没有链,有2.单继承的优点,也不会修改Parent的prototype,也不会继承私有方法

看似聚集了上面四种方法的所有优点

如何复制prototype属性呢?我们实现一个extend2的方法:

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

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

我们没有把Child的原型对象指向新的实例,只是复制了Parent.prototype中的所有属性,一次不需要再人工赋值constructor属性

function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert(this.name);
};

function Coder(language,name){
   this.language = language;
       this.name = name;
}

extend2(Person,Coder);
Coder.prototype.code = function(){
    alert("i am a "+this.language+" coder");
};

var c1 = new Coder("js","jim");
c1.sayHello();
c1.code();
var c2 = new c1.constructor("php","lily");
c2.code();

和之前通过extend实现继承的代码基本一致,只是多了一个2.

这样做的好处:

没有链了,貌似可以提高效率

坏处:

复制属性,会带来一些不必要的内存开销。

但是,prototype属性大部分是方法,这种情况下,只是多了一个指针指向方法而已,并没有真正重新实现一次方法。

这五种方式实现继承都基于一种假设:我们的对象实例都是通过构造函数生成的。

如果我们不用构造函数呢,还可以做些别的事情吗??详看后文




苦逼码农一个,
人力资源管理专业本科毕业,
懂点c#,
懂点javascript,
懂点sql,
懂点设计模式
...

@我是赵六六

q:329952402

原文地址:https://www.cnblogs.com/acjialiren/p/2983260.html