JavaScript中构建伪类子类的几种错误方法

JavaScript中有几种‘类’的构建模式:对象修饰、函数、原型、伪类,都相对比较好理解,总结构建伪类子类的几种错误方法。

首先,模拟一个场景,比如汽车,汽车有很多种,基于汽车的通用部分,我们创建父类(也可以叫做基类或者超类,以下都称父类)Car,子类BMW,实例mini和bus:

var Car = funciton(loc){
 this.loc = loc;
};
Car.prototype.move = function(){
 this.loc++;
};

var BMW = function(loc){
 //如何构造?下文代码替换处
};

//调用
var bus = new Car(30);
bus.move();

var mini = new BMW(80);
mini.move();
mini.speedUp();
```


那么,该如何构造子类捏?

### 不建议:直接重复

替换代码:

   this.loc = loc;

这会在每一个实例中创建一个loc属性,并且会指向对应的数字。虽然可以达到效果,但是有弊端:

1.失去了父类和子类的关系;
2.实际代码中代码逻辑要复杂的多,那么需要重复的部分也很多,冗余;
3.一旦需要修改,那么需要修改多处,很容易遗忘&出错;

### 错误:仅调用Car函数

替换代码:

   new Car(loc);

除了在调用的时候创建的对象外(bus),这行代码将会产生一个新的对象。

换一个角度,在BMW用new调用Car函数时,Car函数中的this指向的是Car的一个全新的对象。

带关键字new运行任何函数的作用是在函数体中’隐藏的’添加了两行代码:

在第一行添加了:this = Object.create(Car.prototype);将this等同于一个全新的对象(规则上不允许在代码中直接对this赋值,但在解释器中可以运行);

在最后一行添加了:return this;

在这里,首先我们用new函数调用了BMW,已经有一行’隐藏的‘:this = Object.create(BMW.prototype);这样就有了两个新对象,然而我们不需要第二个。

那么试着赋值给关键字this能否解决?

### 错误:给this赋值

替换代码:

   this = new Car(loc);

规则错误,上面说过了~~在代码里不能直接给this关键字赋值。即使能,也依旧不能解决有两个不同的对象的问题,有一个是不需要的;

那么如果直接调用捏?

   Car(loc);

依旧不行~~

在调用的时候,Car中的this会指向global,也就是说,Car函数将会在全局作用域的上下文环境中运行(Car函数被作为一个自由函数调用,实际上将会绑定一个指向数字80的全局变量loc),mini实例将完全不会被Car函数里的代码影响;

### 推荐:使用call

替换代码:

   Car.call(this,loc);

调用Car函数,并且把参数绑定到mini上,这样只会创造一个实例,并且使BMW在创建实例mini的时候调用了Car的构造器~~

### 原型链的处理

使用call虽然完成了子类的构造,但是此时可以发现,mini.move()无法运行,这是因为mini是BMW的实例,原型链是委托在BMW.prototype上的,而BMW.prototype上并没有定义move函数,move函数是定义在car.prototype上的。(注:BMW.prototype是委托在Object.prototype上的)

因此,需要将BMW的原型委托到Car的原型上,同时不要忘了constructor~~

在代码中添加:

   BMW.prototype = Object.create(Car.prototype);
   BMW.prototype.constructor = BMW; //否则实例的constructor会指向Car


这里有一个最常见的错误,就是尝试实例化父类构造器,作为委托给它原型的方法:
```javascript
var Car = function(loc){
 this.loc = loc;
};
Car.prototype.move = function(){
 this.loc++;
};

var BMW = function(){
 Car.call(this,loc);
};
BMW.prototype = new Car();
```
这几乎和Object.create方法实现了相同的效果,但是唯一的区别是它在创建这个新对象的时候运行了Car函数,这点不太好~~

Object.create并不是一个很新的语言特性。

常见做法规定设置BMW.prototype等于一个新的Car实例,但是这样会引起很多问题,每次我们构造一个类似BMW的子类或者其他Car的子类,都会调用Car这个函数作为整个过程的一部分,父类构造函数在执行时也许要求一些参数(比如loc),但是没有任何方式传递他们,因为新的Car的原型并没有实际意义上的位置属性,唯一拥有有意义的位置属性的实物是Car或者BMW的实例,所有的BMW在抽象概念上没有一个有意义的位置,因此在这里传递loc输入变量是没什么用的,这个函数运行的时候会把他的所有输入绑定为undefined。

如果代码行需要处理这些输入,比如这个点访问:

```javascript
var Car = function(loc){
 this.loc = loc.valueOf();
}
```

那么loc是undefined,所以undefined.valueOf()将会导致一个错误……

构造一个足够强健的构造器去避免这种错误是非常难的,实际工作中的代码逻辑也很复杂,着实没必要,用Object.create就行~

最后完整的代码:
```javascript
var Car = funciton(loc){
 this.loc = loc;
};
Car.prototype.move = function(){
 this.loc++;
};

var BMW = function(loc){
 Car.call(this,loc);
};
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW; 

//调用
var bus = new Car(30);
bus.move();

var mini = new BMW(80);
mini.move();
mini.speedUp();
```

就酱~

你可能感兴趣的:(JavaScript中构建伪类子类的几种错误方法)