对象的[[Prototype]] js中任何对象内都有一个隐藏属性:[[Prototype]],若访问的属性名在当前对象中没有找到,js会自动到[[Prototype]]所引用的对象中去寻找。[[prototype]]可以通过设置proto 属性来间接 设置(实际上proto 是[[Prototype]]的setter),如下例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let  animal = {    eats: true      walk() {         alert("Animal walk" );     } }; let  rabbit = {    jumps: true  }; rabbit.__proto__ = animal; alert( rabbit.eats );  alert( rabbit.jumps );  rabbit.walk();  
函数的原型属性(prototype) js中的所有函数对象,都自动会创建一个prototype属性,属性值是一个原型对象”prototype”,里面包含一个constructor属性,其属性值设置为函数对象本身。当使用new调用这个函数来创建一个对象的时候,这个对象的[[Prototype]]会指向哪个原型对象,如下例:
1 2 3 4 5 6 7 8 function  Rabbit (let  rabbit = new  Rabbit(); console .log(rabbit.constructor == Rabbit); 
用函数原型实现面向对象 把对象的方法都放在函数原型当中,如下面两个对象:
1 2 3 4 5 6 7 8 9 function  Rabbit (name )     this .name = name; } Rabbit.prototype.jump = function (     alert(`${this .name}  jumps!` ); }; let  rabbit = new  Rabbit("My rabbit" );
1 2 3 4 5 6 7 8 9 function  Animal (name )     this .name = name; } Animal.prototype.eat = function (     alert(`${this .name}  eats.` ); }; let  animal = new  Animal("My animal" );
要实现Rabbit继承Animal,只需要让Rabbit的函数原型的[[Prototype]]指向Animal的函数原型即可。如下代码及图示:
1 2 3 4 5 6 7 8 Rabbit.prototype.__proto__ = Animal.prototype;  Rabbit.prototype = Object .create(Animal.prototype) let  rabbit = new  Rabbit("White Rabbit" );rabbit.eat();  rabbit.jump(); 
创建出rabbit对象实例之后,完整的原型链还包括了Object公共原型,看起来是这样的:
class关键字 js实现面向对象的基本原理就是之前的那些,js把这些实现集合成语法糖,可以直接使用class关键字像其他语言那样直接定义类
1 2 3 4 5 6 7 8 9 10 11 12 13 class  User      constructor (name) {         this .name = name;     }     sayHi() {         alert(this .name);     } } let  user = new  User("John" );user.sayHi(); 
上面这段代码,其实就等价于下面这段
1 2 3 4 5 6 7 8 9 10 function  User (name )     this .name = name; } User.prototype.sayHi = function (     alert(this .name); } let  user = new  User("John" );user.sayHi(); 
一个js中的类,可以写成以下这种形式
1 2 3 4 5 6 7 8 9 10 11 class  MyClass      constructor (...) {              }     method1(...) {}     method2(...) {}     get something(...) {}     set something(...) {}     static  staticMethod(..) {}      } 
Myclass实际上是一个变量,它的值是constructor构造函数,类里的方法如果不加static关键字,就是Myclass的原型内的方法,如果加上static关键字,那么这个函数就是Myclass函数对象内的方法,和Myclass构造函数返回的具体实例无关。
类继承的语法糖如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Rabbit  extends  Animal           constructor (...args) {         super (...args);              }     hide() {         alert(`${this .name}  hides!` );     }     stop() {         super .stop();          this .hide();      } } 
需要补充的是,当类的原型对象组成继承链的时候,相应的构造函数对象也同时组成了继承链,这样子类也可以调用父类的static方法,如下图所示
super关键字 super关键字用来访问当前类的父类的方法,它的原理是这样的:
类原型的函数对象里会自动创建一个隐藏的[[HomeObject]]属性,该属性是指向这个原型对象的引用,这样每个类方法都可以通过[[HomeObject]]来访问它真正的原初的this,而不会被人为乱七八糟的调用以及call或者绑定所干扰对真正this值的判断。有了真正的原初的this,就可以通过访问this.[[Prototype]]来获得父类的原型对象了,从而实现了super的效果。
下面用普通的Object写法来展示super的原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let  animal = {  name: "Animal" ,   eat() {              alert(`${this .name}  eats.` );   } }; let  rabbit = {  __proto__: animal,   name: "Rabbit" ,   eat() {              super .eat();     } }; let  longEar = {  __proto__: rabbit,   name: "Long Ear" ,   eat() {              super .eat();     } }; longEar.eat();   
需要注意的是,只有写成类方法形式的函数才会生成[[HomeObject]],如果写成”Method : function()”的形式,就不会有[[HomeObject]]。