Javascript深入理解之继承与原型链

Javascript深入理解之继承与原型链

1.构造函数、原型、实例的关系
* 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针[[Prototype]] *

function Foo() {
    this.a = 123;
}

Foo.prototype.b = 456;

let AA = new Foo();

console.dir(Foo.prototype);
console.dir(Foo.prototype.constructor);
console.dir(AA.__proto__);

// Foo为构造函数
// AA为实例
// Foo.prototype为原型对象
// Foo.prototype的constructor指向构造函数
// AA的__proto__指向原型对象

1.原型链

* 关于原型链的定义,我们来看一下MDN里面的定义“在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。” *

2.原型链实例与继承

function Foo() {
    this.nameOne = 123;
    this.toOne = function() {
        return this.nameOne;
    }
}

function FooTwo() {
    this.nameTwo = 456;
    this.toTwo = function() {
        return this.nameTwo;
    }
}


FooTwo.prototype = new Foo();

/* 当我们对对象的prototype属性进行替换时,可能会对对象constructor属性产生一定的副作用,因此,在对prototype进行替换后,要对对象的constructor进行重置,避免陷入原型陷进。*/ 
FooTwo.prototype.constructor = FooTwo;

let aa = new FooThree();
console.log(aa.nameOne);    // => 123
console.log(aa.nameTwo);    // => 456
console.log(aa.toOne());    // => 132
console.log(aa.toTwo());    // => 456   

关于原型陷阱请看JavaScript深入理解之原型
* 上例中:定义了两个类型:Foo和FooTwo,每个类型分别有一个属性和方法,且FooTwo继承了Foo,继承是通过创建Foo的实例,并将该实例赋给Foo.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例,即原来存在于Foo的实例中的方法现在也存在于FooTwo.prototype中了,这样就继承了Foo的属性和方式,实现了继承。通俗一点说,A构造函数要继承B构造函数的属相和方法,则将B的实例赋值给A的prototype属性。*

看下面一个例子

function Foo() {
    this.name = 123;
    this.toString = function() {
        return this.name;
    }
}

function FooTwo() {
    this.nameTwo = 456;
}

function FooThree() {
    this.name = 789;
    this.toStr = function() {
        return this.name;
    }
}

FooTwo.prototype = new Foo();
FooThree.prototype = new FooTwo();

FooTwo.prototype.constructor = FooTwo;
FooThree.prototype.constructor = FooThree;

let aa = new FooThree();
console.log(aa.nameTwo);        // => 456
console.log(aa.toString());     // => 789
console.log(aa.toStr());        // => 789

// aa同时是三个构造器的实例
console.log(aa instanceof Foo);         // => true
console.log(aa instanceof FooTwo);      // => true
console.log(aa instanceof FooThree);    // => true

* 上例中:两个输出都打印789,因为在继承中,继承的方法和属性, 而且是单纯的方法和属性,FooThree实际上是下面这样:*

function FooThree() {
    this.nameTwo = 456;
    this.name = 789;            // 因为我们设置constructor指向FooThree
    this.toStr = function() {
        return this.name;
    }
}

3.构造函数模式继承

function Foo(name) {
    this.name = name;
    this.say() = function() {
        return this.name;
    }
}

function FooTwo() {
    // 继承Foo,同时还传递参数
    Foo.call(this, 123);
}

let aa = new FooTwo();

console.log(aa.name);   // => 123
console.log(aa.say());  //TypeError: say is not a function

* 构造函数模式缺点在于只能继承属性,不能继承方法 *

4.组合继承

function Foo(name) {
    this.name = name;
}

Foo.prototype.say = function() {
    return this.name
}

function FooTwo(name, age) {
    // 继承属性
    Foo.call(this, name);
    this.age = age;
}

// 继承方法
FooTwo.prototype = new Foo();
FooTwo.prototype.constructor = FooTwo;
FooTwo.prototype.say = function() {
    return this.name;
}

let aa = new FooTwo('aa', 26);

console.log(aa.name);       // => aa
console.log(aa.age);        // => 26
console.log(aa.say());      // => aa

5.其它继承方式还有:原型式继承、寄生式继承等

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值