# 原型继承

JS中的继承跟其他面向对象语言的继承有点不太一样，要正确的了解JS中的继承还得了解JS的前世今生。

#### 一、从JS的诞生之初说起
要理解Javascript的设计思想还得从它的诞生说起。

1994年网景公司（Netscape）发布了 Navigator 浏览器 0.9 版。这是历史上第一个比较成熟的网络浏览器，轰动一时，但是这个版本的浏览器只能用来浏览，不具备与访问者交互的能力。比如网页上有一个地方需要用户输入“用户名” ，当时浏览器无法判断访问者是否真的填写了，只有让服务器判断，如果没有填写，服务器就返回错误，这太浪费时间和服务器资源了。

因此，网景公司需要一种网页脚本语言，使得浏览器可以与网页互动，工程师 Brendan Eich 负责开发这种新语言。他觉得没有必要设计的很复杂，这种语言只要能够完成一些简单的操作就足够了，比如判断用户有没有填写表单。

1994年正是面向对象编程（OOP）最兴盛的时期，C++是当时最流行的语言，Java语言的1.0版即将在第二年推出，Sun 公司正在大肆造势。

Brendan Eich 无疑收到了影响，JS里面所有的数据类型都是对象，这一点与Java非常相似，但是他随即遇到一个问题，到底要不要设计继承机制？

#### 二、Brendan Eich的选择
如果 真的是一种简易的脚本语言，其实不需要有“继承”机制，但是JS里面都是对象，必须有一种机制，将所有的对象都联系起来，所以他最后还是设计了“继承”。

但是他没有引入“类”的概念，因为一旦有了“类”，JS就是一门完全面向对象的语言了，增加了初学者的难度，他考虑到 C++ 和 Java 都使用 new 命令，生成实例。

C++ 写法

ClassName *object = new ClassName(param);
Java 写法

Foo foo = new Foo();
因此引入了 new 命令，用来从原型对象生成一个实例对象，但是JS没有“类”的概念，怎么表示原型对象？

这时他想到 C++ 和 Java 都是使用 new 命令，会调用类的构造函数（constructor）。他就设计了一个简化版，在 JS 语言中，new 命令后跟的不是类，而是构造函数。

举例来说，用狗的构造函数表示狗对象的原型。

function Dog(name){
    this.name = name;
}
对这个构造函数使用 new， 就会生成一个狗对象的实例。

var dog1 = new Dog("阿拉斯加");
console.log(dog1.name);    //阿拉斯加
注意：构造函数中的 this 关键字，指向了新构建的实例对象。

#### 三、new 运算符的缺点
用构造函数生成的实例对象，有一个缺点就是无法共享属性和方法。

比如，在Dog对象的构造函数中，设置一个实例对象的共有属性category

function Dog(name){
         this.name = name;
     this.categey = "dog";
}

var dog1 = new Dog("阿拉斯加");
var dog2 = new Dog("萨摩");

console.log(dog1.category);         //dog
console.log(dog2.category);         //dog

dog1.category = "阿拉斯加";                  
console.log(dog1.category);         //阿拉斯加
console.log(dog2.category);         //dog
这时候2个对象的category属性是独立的，修改其中一个，不会影响另一个。

这样 的弊端就是每一个实例对象，都有自己的属性和方法的副本，无法做到数据的共享，对内存的极大浪费。

#### 四、prototype 属性的引入
考虑到这一点，Brendan Eich 决定为构造函数设置一个 prototype 属性。

这个属性包含一个对象，所有的实例对象需要共享的属性和方法都保存在这个对象里面，那些不需要共享的属性和方法就放在构造函数里面。

实例对象一旦创建，将自动引用 prototype 对象的属性和方法， 也就是说实例对象的属性和方法，分成两种，一种是自己（子类）的属性和方法，另一种是引用的（父类）。

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

Dog.prototype.category = "dog";
var dog1 = new Dog("阿拉斯加");
var dog2 = new Dog("萨摩");
console.log(dog1.category); //dog
console.log(dog2.category); //dog
dog1.category = "啸天犬";
console.log(dog1.category); //啸天犬
console.log(dog2.category); //dog
dog1.__proto__.category = "啸天犬";
console.log(dog1.category); //啸天犬
console.log(dog2.category); //啸天犬
现在 category 属性存放在 prototype 对象里面，被子类共享，只要修改 prototype 对象里面的 category 值，子类都会被改变。

修改可以通过2种方法修改

1、dog1.__proto__.category = "啸天犬";
2、Dog.prototype.category = "啸天犬";
五、总结
由于所有的实例对象共享同一个 prototype 对象，那么外界看起来就是 prototype 对象就像是实例对象的原型， 而实例对象就好像继承了 prototype 对象一样。
