JS原型和继承

JS原型与继承

首先原型和继承在 JavaScript 中一直都是比较核心重点并且难点的理论,今天我们就来看看其中的奥秘点,

1. 开篇

先看如下代码出自 JavaScript 高级程序设计第三版 P163:

function SuperType(){
    this.property = true;
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}

SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
alert(instance.getSuperValue());

请大家猜一猜最后alert出的结果是什么?

大家先思考一下再看下面的内容。


2. 关于原型与原型链问题:

2.1 什么是prototype以及 proto

_proto_:

任何一个对象Object都有 _proto_,它是每一个对象的私有属性,是天生自带的。

prototype:

不是任何对象都有prototype,只有构造函数有prototype,是后天赋予的。

2.2 什么是原型链查找

一句话:

调用一个对象的属性或方法,若该对象中没有,就去这个对象的_proto_中查找。这个对象的_proto_指向自己构造函数的prototype属性。

如下图所示: image 如下代码:

var Person = function () {
    this.sleep = 'zzzzz';
}
Person.prototype.sayHello = function () {
    console.log('hello world');
}
var Nodezhang = new Person();
Nodezhang.sayHello();  // => hello world

其实 Nodezhang 这个对象下面只有一个sleep属性,是没有sayHello方法的。 但是通过原型链查找会查 Nodezhang._proto_也就是查找它的构造函数的Person.prototype,Person.prototype下有sayHello这个方法,所以会在控制台输出hello world.


3. 六种继承方式

  1. 纯原型链继承.
  2. 借用构造函数继承.

    3. 组合继承.(重点推荐使用!)

  3. 原型式继承.

  4. 寄生式继承.

  5. 寄生组合式继承.

3.1 纯原型链继承

直接上代码:

function Father() {
    this.likeFood= ['牛排','饺子','啤酒','可乐']
}
Father.prototype.saylikeFood = function () {
    console.log(this.likeFood);  
};

function Son() {
}
Son.prototype = new Father();

var zhangsan = new Son();
zhangsan.likeFood.push('西瓜');
zhangsan.saylikeFood(); // ["牛排", "饺子", "啤酒", "可乐", "西瓜"]

但是这样的方式有问题。接着上面的代码再写:

var lisi = new Son();
lisi.saylikeFood(); // ["牛排", "饺子", "啤酒", "可乐", "西瓜"]

看出问题了吧,zhangsan直接修改了其构造函数的likeFood, 导致我们再实例的对象也收到了修改的影响, 因此这种继承方式有缺陷

没错你会看见包含引用类型值的原型属性会被所有实例共享。

3.2 借用构造函数继承

function Father(name) {
    this.name = name;
    this.sayName = function () {
        console.log(this.name);
    }
}

function Son(name, age) {
    Father.call(this, name);
    this.age = age;
}

var zhangsan = new Son('zhangsan', 17);

这种继承方式并没有利用到原型以及原型链的概念,它主要利用call的特性,call的第一个参数传入this,后面的参数传入函数所需的参数。 这种方式归根结底其实就是在实例一个对象的时候,向这个对象的上面添加所需的属性和方法。

问题在于:按照这种方式,每次new一个对象,就是实例化一个对象,都会向这个对象身上添加一堆属性和方法。添加属性是没问题的,但是每次在对象身上添加的方法,这个函数就要重写一次。方法都在构造函数中定义了。

函数不能进行复用,这就是最大的问题!

3.3 组合继承

组合继承其实分别是拥有以上两种方法的优点,同时也规避了以上两种方法的缺点。 这种方法的应用是最广泛的,是最普遍的,举个列子来看一下:

function SuperType (name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototyoe.sayName = function () {
    alert(this.name);
}

function FooType (name, age) {
    // 继承属性
    SuperType.call (this.name);

    this.age = age;
}

// 继承方法
FooType.prototyoe = new SuperType(); // 光只有这一句会让 FooType 继承 SuperType 的属性+方法。
FooType.prototyoe.constructor = FooType; // 加上这句就会实现仅仅是继承了 SuperType 的方法而不改变其属性。
FooType.prototyoe.sayAge = function () {
    alert(this.age);
}

var instance1 = new FooType("Nicholas", 29);
instance1.colors.push ("black");
alert(instance1.colors); // "red,blue,green,black"
instance1.sayName();    // "Nicholas"
instance1.sayAge();     // "29"

var instance2 = new FooType("Greg", 10);
alert(instance2.colors); // "red,blue,green"
instance2.sayName();    // "Greg"
instance2.sayAge();     // "10"

除了组合继承外,还有三种继承方式:

原型式继承、寄生式继承 和 寄生组合式继承。详情自行百度或看js高程3版P170.

js  基础