原型与原型链 
[[toc]]
原型与原型链 首先,JS是真正的“面向对象”的语言,而其他我们所熟知的例如C++、Java等,严格意义上说,是“面向类”的语言,仔细想想,还真是那么回事儿。其次,JS中,调用构造器之后,对象并不是它原型的一份拷贝,而是被链接 到原型上。
Object 我们先看一段代码:
function  Person (name ) {         this .name = name; } Person.prototype.sayName = function (welcome )  {      	console .log(welcome, this .name); } var  person1 = new  Person('Smiley' );     person1.sayName('Hello' ); 
 
在执行这段代码之前,有个东西是一直存在的,就是有些人所说的“原型的原型”。如下图所示,我们用圆形代表function,用方形代表object:
左边的圆形是Object构造函数,就是我们一般使用var obj = new Object()时最普普通通的的构造函数。右边的方形是Object的prototype,这个东西没有名字(虽然它很重要)。左边有一个箭头指向右边,意味着Object有一个属性叫做prototype,这个属性指向的是右边的那个方块,而向左指的箭头,意味着右边方块的constructor属性是左边的Object构造函数。
这些东西在上面程序运行之前就一直存在的。
构造函数 好的,我们开始看第一行代码,运行第一行代码之后,会生成一个叫Person的构造函数,而这个构造函数的prototype属性,指向的就是它的prototype,如下图方块所示:
Person与Person.prototype之间的关系,与Object和Object.prototype类似,不同的是,Person.prototype会通过proto 指向Object.prototype。
接下来看第5行代码:我们在Person.prototype上面添加一个sayName方法,如上图所示,Person.prototype这个方块中有sayName方法。
new调用构造器 使用new这个关键字的时候,JS编译器会做四件事情:
function  realizeNew (con, ...args )  {    let  obj = {};     obj.__proto__ = con.prototype;     let  ret = con.apply(obj, args);          return  typeof  ret === 'object'  && ret !== null  ? ret : obj; } function  Person (name,age ) {    this .name=name;       this .age=age;     this .run=function ( ) {           alert(this .name+'在运动' );     } }     realizeNew(Person,'和振峰' ,24 ) 
 
如上图所示,我们先创建一个person1的空对象,然后把person1通过proto 指向原型对象,指向构造函数中的代码,person1就获得了一个叫做name的属性,最后返回。
我们最后运行第10行代码:person1.sayName(‘Hello’);
person1上有sayName这个方法么?没有,那么就顺着person1的proto 向上找,找到Person.prototype。Person.prototype上面有sayName方法么?有的,那么执行这个方法。这个方法内部使用了this.name,那么这个this的指向是什么么?我们需要看sayName的call site,是person1调用的sayName,隐式调用,this就指向person1,而person1的name就是Smiley。
是不是觉得很神奇,最后调用时候使用的属性和方法都是我们希望使用的那个,person1.sayName(‘Hello’)看似很容易理解,JS初学者都能很容易说出最后输出结果,但是这其中的过程,恐怕只有理解了原型和原型链才能真正说明白。
明白了这些之后,我们看几个相等关系:
console .log(Person === Person.prototype.constructor);console .log(person1.__proto__ === Person.prototype);
 
我们再也不用死记硬背这个关系了,而是通过上面的图直接可以推到出来。
console.log(person1.constructor)是什么的呢?
es5的几种继承方式 对象冒充实现继承 function  Person ( ) {    this .name='张三' ;       this .age=20 ;     this .run=function ( ) {           alert(this .name+'在运动' );     } }       Person.prototype.sex="男" ; Person.prototype.work=function ( ) {      alert(this .name+'在工作' ); } function  Web ( ) {    Person.call(this );     } var  w=new  Web();w.work();   
 
原型链实现继承 function  Person (name,age ) {    this .name='张三' ;       this .age=20 ;     this .run=function ( ) {           alert(this .name+'在运动' );     } }       Person.prototype.sex="男" ; Person.prototype.work=function ( ) {      alert(this .name+'在工作' ); } function  Web (name,age ) {    } Web.prototype=new  Person();    var  w=new  Web();w.work();   
 
原型链+对象冒充的组合继承模式 function  Person (name,age ) {        this .name=name;           this .age=age;         this .run=function ( ) {               alert(this .name+'在运动' );         } }       Person.prototype.sex="男" ; Person.prototype.work=function ( ) {         alert(this .name+'在工作' ); }    function  Web (name,age ) {    Person.call(this ,name,age);    } Web.prototype=new  Person(); var  w=new  Web('赵四' ,20 );   w.work(); 
 
class实现继承源码 ES6
class  B   {  constructor (props) {     this .name = props.name;   } } class  A  extends  B   {  constructor () {          super ({ name : 'B'  });          console .log(this );   } } 
 
ES5
function  __extends (child, parent )  {     Object .setPrototypeOf(child, parent);         function  __ ( )  {          this .constructor = child;   }      child.prototype =     parent === null        ? Object .create(parent)       : ((__.prototype = parent.prototype), new  __()); } var  B = (function  ( )  {    function  B (props )  {         this .name = props.name;     }     return  B; }()); var  A = (function  (_super )  {    __extends(A, _super);     function  A ( )  {         var  _this = _super.call(this , { name : 'B'  }) || this ;                   console .log(_this);         return  _this;     }     return  A; }(B)); 
 
ES5/ES6 的继承除了写法以外还有什么区别? 
class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。 
 
const  bar = new  Bar(); function  Bar ( )  {  this .bar = 42 ; } const  foo = new  Foo(); class  Foo   {  constructor () {     this .foo = 42 ;   } } 
 
class 声明内部会启用严格模式。 
 
function  Bar ( )  {  baz = 42 ;  } const  bar = new  Bar();class  Foo   {  constructor () {     fol = 42 ;    } } const  foo = new  Foo();
 
class 的所有方法(包括静态方法和实例方法)都是不可枚举的。 
 
function  Bar ( )  {  this .bar = 42 ; } Bar.answer = function ( )  {   return  42 ; }; Bar.prototype.print = function ( )  {   console .log(this .bar); }; const  barKeys = Object .keys(Bar); const  barProtoKeys = Object .keys(Bar.prototype); class  Foo   {  constructor () {     this .foo = 42 ;   }   static  answer() {     return  42 ;   }   print() {     console .log(this .foo);   } } const  fooKeys = Object .keys(Foo); const  fooProtoKeys = Object .keys(Foo.prototype); 
 
class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。 
 
function  Bar ( )  {  this .bar = 42 ; } Bar.prototype.print = function ( )  {   console .log(this .bar); }; const  bar = new  Bar();const  barPrint = new  bar.print(); class  Foo   {  constructor () {     this .foo = 42 ;   }   print() {     console .log(this .foo);   } } const  foo = new  Foo();const  fooPrint = new  foo.print(); 
 
必须使用 new 调用 class。 
 
function  Bar ( )  {  this .bar = 42 ; } const  bar = Bar(); class  Foo   {  constructor () {     this .foo = 42 ;   } } const  foo = Foo(); 
 
class 内部无法重写类名。 
 
function  Bar ( )  {  Bar = 'Baz' ;    this .bar = 42 ; } const  bar = new  Bar();class  Foo   {  constructor () {     this .foo = 42 ;     Foo = 'Fol' ;    } } const  foo = new  Foo();Foo = 'Fol' ;