JS简记-委托

js中不存在“类”,js除基本类型外,其余都是对象。如果试图在js身上使用“类”思维来编程,会使程序变得异常复杂,且晦涩难懂。由于对象的存在,在js中更适合使用“委托”方式来实现类的“继承”(其实此时已不能称为继承)。

先来看“类”的思想:

 1 function Hello(){
 2 }
 3 Hello.prototype.helloWorld = function() {
 4     console.log("hello ");
 5 }
 6 function World(){
 7 }
 8 World.prototype = Object.create(Hello.prototype);//继承Hello
 9 World.prototype.helloWorld = function(){
10     Hello.prototype.helloWorld.call(this);//伪多态
11     console.log("world !");
12 }
13 var helloWorld = new World();//实例化World
14 helloWorld.helloWorld();
15 console.log(helloWorld instanceof World);//true
16 console.log(helloWorld instanceof Hello);//true
17 console.log(World.prototype.isPrototypeOf(helloWorld));//true
18 console.log(Hello.prototype.isPrototypeOf(helloWorld));//true

 

再来看“委托”的思想:

 1 var Hello = {
 2     hello(){
 3         console.log("hello ");
 4     }
 5 };
 6 var World = Object.create(Hello);//World关联到Hello
 7 World.world = function(){
 8     this.hello();
 9     console.log("world !");
10 }
11 var helloWorld = Object.create(World);//产生一个关联到World的新对象,可以在新对象上做进一步操作,比如生成新属性
12 helloWorld.world();//hello world !
13 console.log(World.isPrototypeOf(helloWorld));//true
14 console.log(Hello.isPrototypeOf(World));//true

 

使用“委托”后,不再需要伪多态,同时由于省去了晦涩的prototype(prototype的存在就是为了提供“原型继承”机制),使得代码更加简洁。而且,在原型链的判断上也更加清楚。

 

ES6引入了全新的class关键字,使得js看上去拥有了“类”的能力,但其实则是语法糖。

 1 class Animal{
 2     constructor(variety) {
 3         this.variety = variety;
 4     }
 5     variety(){
 6         return this.variety;
 7     }
 8 }
 9 class Dog extends Animal{
10     constructor(variety){
11         super(variety);
12     }
13     roar(){
14         console.log(super.variety() + ": " + "wang wang");//这里也可以不需要super,直接调用variety()
15     }
16 }
17 var dog = new Dog("dog");
18 dog.roar();//dog: wang wang

下面就是dog对象的内部结构,可以看到其还是使用了原型链。

 再来看一下Dog“类”,其和function的结构类似(都有arguments、caller、length、name),且原型链上都有Function.prototype。最关键的是Dog.prototype.__proto__其实就是Animal.prototype,这很好理解了。

通过上面观察,发现class关键字并不神奇,真正神奇的是super关键字。查阅资料发现,class中的每一个方法,js引擎内部都维护着一个叫做[[HomeObject]]的引用,该引用指向方法的宿主对象,通过该对象就可以产生super语义,其内部机制如下:

  1. 首先根据被调用方法的[[HomeObject]]属性值(即当前方法的归属对象),通过Object.getPrototypeOf()获取原型;
  2. 获取到原型后,检索同名方法;
  3. 绑定this指向并执行方法函数。

比如,针对Dog类的roar方法,js引擎就维护着一个[[HomeObject]](可以看成是roar方法内部的一个属性,但是无法获取):

super关键字与this关键字不同,this在运行时动态确定,super在声明时确定,也就是说[[HomeObject]]在声明一个类时就已被赋值了(这很好理解):

 1 class Animal{
 2     constructor(variety) {
 3         this.variety = variety;
 4     }
 5     variety(){
 6         return this.variety;
 7     }
 8 }
 9 class Dog extends Animal{
10     constructor(variety){
11         super(variety);
12     }
13     roar(){
14         console.log(super.variety() + ": " + "wang wang");//这里也可以不需要super,直接调用variety()
15     }
16 }
17 var dog = new Dog("dog");
18 dog.roar();//dog: wang wang
19 var roar = dog.roar;
20 var variety = "dog2";
21 roar.call(window);//es6默认采取“use strict”,输出dog2: wang wang

 

虽然class关键字使js有了“类”的模样,但其仍然有很多限制,比如class中无法直接定义类属性(java中的static字段),如果有需求,还是需要通过prototype来定义(注意属性屏蔽)。

 

posted @ 2018-05-07 23:10  holoyong  阅读(353)  评论(0编辑  收藏  举报