js继承的6种方式

废话没有,直接上代码

假设要继承的对象是person

function Person() {
    this.name = "person";
}

/*
* Person.prototype是person的原型对象,里面可以放一些原型对象的属性和方法
*/
Person.prototype.getName = function () { 
    console.log(this.name);
}

一、原型链继承

function Person() {
    this.name = "person";
}
Person.prototype.getName = function () {
    return this.name;
}

function Man() {
    this.age = 12;
}

Man.prototype = new Person();
//Man.prototype.constructor = Man没什么实际的作用
//Man.prototype.constructor意思是Man的原型对象的构造函数,
//它原本应该指向Man,但是经过上句Man.prototype = new Person()之后,constructor指向了Person
//为了不混乱原型链,最好修改成指向Man,如下:
Man.prototype.constructor = Man;
var man1 = new Man();
console.log(man1.age);//访问自己的属性,age=12
console.log(man1.name);//自己的属性中没有,找到Person中的name=“person”

//自己的方法没有getName,父元素构造方法中也没有,所以接着去父元素的原型对象中去找,找到getName
console.log(man1.getName()); 

优点:

1、简单,易实现;

2、父对象新增的属性和方法,子对象都能访问到

缺点:

1、我现在想通过Man的实例给person传递参数,会发现不能实现;

2、所有实例共享的构造函数的引用属性和原型对象中的引用属性,改变一个,其他的也都会改变。例如:

function Person() {
    this.name = "person";
    this.arr = [7,8,9];
}
Person.prototype.list = [1,2,3];
Person.prototype.getName = function () {
    return this.name;
}

function Man() {
    this.age = 12;
}

Man.prototype = new Person();
Man.prototype.constructor = Man;
var man1 = new Man();
var man2 = new Man();

//所有实例共享引用属性
man1.arr.push(10);
man1.list.push(4);
console.log(man2.arr);//[7,8,9,10]
console.log(man2.list);//[1,2,3,4]

3、原型链继承只能继承一个父对象,不能实现多继承;

针对以上三个缺点,如果我想要给父对象传递参数,或者不想要共享父对象的引用属性,或者想要继承多个父对象的时候,我可以下面这种“构造继承”

二、构造继承

function Person(name) {
    this.name = name||"person";
    this.arr = [7,8,9];
}
Person.prototype.getName = function () {
    return this.name;
}

function Man(name) {
    Person.call(this,name);//这里可以给父级构造函数传参
    this.age = 12;
}

var man1 = new Man("xiaoming");
var man2 = new Man("xiaohong");
console.log(man1.name);

//属性相当于复制,不会共享引用属性
man1.arr.push(10);
console.log(man1.arr);//[7,8,9,10]
console.log(man2.arr);//[7,8,9]

这里跟 原型链继承 有个比较明显的区别是并没有使用prototype继承,而是在子类里面执行父类的构造函数,相当于把父类的代码复制到子类里面执行一遍,所以不会共享父类的引用属性,这样做的另一个好处就是可以给父类传参,也可以实现多继承(call多个父类对象)

缺点:

1、不能继承父对象原型对象的属性和方法

这里你可能会有疑问:为什么call方法只能使用父对象构造函数的属性方法;而new一个对象就可以同时使用父对象构造函数父对象原型对象的属性方法了呢?可以看附录1:js中new一个对象的过程

我们会发现以上两种继承方式都有不足之处,所以我们可以结合两种继承方式,我们成为组合继承

三、组合继承

function Person(name) {
    this.name = name||"person";
    this.arr = [7,8,9];
}
Person.prototype.list = [1,2,3];
Person.prototype.getName = function () {
    return this.name;
}

function Man(name) {
    Person.call(this,name);//这里可以给父级构造函数传参
    this.age = 12;
}

Man.prototype = new Person();
Man.prototype.constructor = Man;
var man1 = new Man("xiaoming");
var man2 = new Man("xiaohong");
console.log(man1.name);

//构造函数的属性相当于复制,不会共享构造函数的引用属性
man1.arr.push(10);
console.log(man1.arr);//[7,8,9,10]
console.log(man2.arr);//[7,8,9]

//原型对象是原型链继承的,引用属性是共享的
man1.list.push(4);
console.log(man1.list);//[1,2,3,4]
console.log(man2.list);//[1,2,3,4]

组合继承:

1、可以继承父对象的原型对象的属性和方法

2、可以继承多个构造函数(call多个父类对象)

3、可以给父级构造函数传参

4、构造函数中的引用属性不会共享;

缺点:

1、调用了两次父级构造函数,构造继承方式中的call复制的属性会屏蔽原型链中继承过来的属性,因为在使用寻找子对象属性时,会优先查找子对象中的属性,再去查找原型链中的属性。(仅仅多消耗了点内存)

四、直接继承

function Person(name) {
    this.name = name||"person";
}
Person.prototype.getName = function () {
    return this.name;
}

function Man(name) {
}

Man.prototype = Person.prototype;
Man.prototype.constructor = Man;
Man.prototype.pp = "oo";
console.log(Person.prototype.pp)

缺点:

1、不能继承构造函数中的属性和方法(解决办法:可以和构造继承结合,就可以继承父对象构造函数中的属性和方法了)

2、子对象共享父对象的原型对象的属性和方法(解决办法:可以加一个中间对象,如下原型式继承

五、原型式继承

function Person(name) {
    this.name = name||"person";
}
Person.prototype.getName = function () {
    return this.name;
}

function Man(name) {
    this.age = 12;
}

function create(o) {
    function f() {};
    f.prototype = o.prototype;
    return new f();
}

Man.prototype = create(Person);
Man.prototype.constructor = Man;
Man.prototype.pp = "oo";
console.log(Person.prototype.pp)

优点:

1、在直接继承的基础上,增加了一个中间空对象,这使在修改子对象的原型对象时,不会影响到父对象的原型对象

缺点:

1、不能继承构造函数中的属性和方法(解决办法:可以和构造继承结合,就可以继承父对象构造函数中的属性和方法了)

补充:

ES5新增的Object.create()方法拥有和上面create()方法同样的效果,但是Object.create()方法接受两个参数:一个用作要继承的父对象的对象原型和(可选的)一个为要增加的额外属性;如下所示:

function Person(name) {
    this.name = name||"person";
}
Person.prototype.getName = function () {
    return this.name;
}

function Man(name) {
}

Man.prototype = Object.create(Person,{
    self: {
        value: "123"
    }
});
Man.prototype.constructor = Man;
console.log(Man.prototype.self);
Man.prototype.pp = "oo";
console.log(Person.prototype.pp)

六、寄生构造组合继承

function Person(n) {
    this.tt = n || "persion";
}
Person.prototype.getName = function () {
    return this.tt;
}

function Man(name) {
    Person.call(this,name);//这里可以给父级构造函数传参
    this.age = 12;
}

Man.prototype = Object.create(Person,{
    self: {
        value: "123"
    }
});
Man.prototype.constructor = Man;
var man1 = new Man("xiaoming");
console.log(man1.tt);

完美的方式,优点:

1、可以继承父对象的构造函数和原型对象中的属性和方法,构造函数中的引用属性不会共享

2、修改子对象的原型对象时,不会影响父对象的原型对象

3、可以实现多继承,(call多个父对象)

 

华丽的分割线:====================================================================================

附录1:js中new一个对象(Person)的过程

1、创建一个新对象:

  var obj = {};

2、设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;

  obj.__proto__ = Person.prototype;

3、使用新对象调用函数,函数中的this被指向新实例对象:

  Person.call(obj);

4、将初始化完毕的新对象地址,保存到等号左边的变量中

注:每一个实例对象的原型对象是不共享的

 

 

你可能感兴趣的:(前端知识)