语言是种工具,设计出来的根本还是解决问题。那么软件工程是重中之重,各种框架、类库、设计模式漫天飞舞。
js是一门新生的语言,最开始在工程问题上没有太深入的运用。但是随着互联网的发展壮大和用户体验变得越来越重要,js的工作量也是指数行上涨。面对日益复杂的前端交换和成千上万的代码管理。软件工程设计就成了一门必修课程。
如果想要构建大型软件,并且以一种合适的模型来对实际事物建模,那么面向对象编程方式,是一种绝佳的代码组织方式。许多语言都对面向对象编程有不同程度上的支持,比方说:C++、Java、php等。
而面向对象编程的核心思想就是:把一组数据和它可以进行的操作看作一个整体。这个模式是从开始的结构体演变而来,同时面向对象还提出类的概念:一组有相同属性事物的抽象模板。通过对模板传入数据,来制造一个个具体的实例对象。
在程序中,对面向对象进行代码实现的基础,就是拷贝操作。将一组数据和操作,原封不动的传给另一个对象,如此便十分快捷方便的创造了一个新对象。
但是,这种面对对象的设计模式,在js中就难以实现了。因为js中并没有类这个类似版模的概念,js中只有对象:一个无序的键值对而已。既然js只有对象,那么更提不上可以进行的实例化操作了。
为了支持面向对象,js在函数上做文章,设计出来了原型对象这个概念,用以支持面向对象。但是,这个函数对象只能一定程度上去模拟oo,js不会用向其他语言一样通过类模板进行复制生产对象,相反js所有生成的对象都是引用的同一个对象。
来看个简单的代码:
function Car() {this.a=a};
Car.prototype.cout=function(){alert('原型对象')};
var mCar=new Car(12);
<pre name="code" class="javascript"> mCar.a;
mCar.cout();
当我们看到'.'操作符时,按照以往的编程经验,我们自然而然会联想到,这个变量是对象下的一个属性。mCar.a确实是如此,a是一个在mCar上的属性。但是.cout()却不是这样一回事:它是定义在另外一个原型对象上的属性。这样就出现了一个诡异的现象:构造函数上根本没有这个.cout()函数,但是我们的新对象居然可以去‘无中生有’的去访问它,虽然我们知道这是原型链的功能, 但是这会极大降低我们代码的可读性。
es6新增加的语法糖class,看起来很像常规的面向对象:
//es5
function Car() {};
Car.prototype.toString=function(){alert('一个新的原型对象')};
var mCar=new Car();
mCar.cout();
//se6:class
class Car{
constructor(a){
this.a=a;
}
toString(){
alert('一个新的原型对象');
}
}
下class的写法,只不过是对函数的new调用封装了一层语法糖,本质上没有改变:新对象不过是引用原型对象属性的事实,new调用永远不会进行复制操作,一旦你对原型对象的属性进行了改变,那么所有由该方法创造的对象都会收到影响:
class Car{
constructor(a){
this.a=a;
}
toString(){
alert('原来的原型对象');
}
}
var mCar=new Car(5);
mCar.toString();//原来的原型对象
Car.prototype.toString=function(){alert('新的原型对象')};
mCar.toString();//新的原型对象!!