自从面向对象的编程思想出现以来,这个概念已经被炒烂了,只要编程开发你们都会拿面向对象来讲事,好像只要跟面向对象沾边就会显得逼格很高同样,不过确实逼格提升了。要知道,面向对象只是一种手段,最终目的是为了提升咱们项目的重用性、灵活性和扩展性。前端
为了迎合面向对象的程序设计思想,JavaScript也经过本身的语法实现了本身的一套面向对象机制。不过我想问下,前端开发当中有多少人使用过面向对象当中的继承?程序员
JavaScript面向对象的实现确实有点不三不四的感受。下面先简单说明下JavaScript当中面向对象的实现,涉及的东西比较深,要对constructor、prototype有必定的理解,否则看起来会很吃力,或者你也能够跳开这部份内容,直接看CoffeeScript面向对象的实现。编程
JavaScript的实现一个类,能够采用下面的方式:后端
function Animal(name) { this.name = name; } Animal.prototype.printName = function () { console.log(this.name); }; var animal = new Animal('animal'); animal.printName();
C++、Java...某些程序员可能就吐槽了,我TM都连个关键字class都没看到,这就是类了?app
做为一个前端开发,我竟无言以对。。。函数
选取JavaScript当中几种继承方式,做一个简单的说明,想要学习更深的内容,请经过搜索引擎吧。学习
function Animal(name) { this.name = name; this.printName = function () { console.log(this.name); }; } function Cat(name) { this.inherit = Animal; this.inherit(name); //Animal.call(this, name); //Animal.apply(this, [name]); } var cat = new Cat('cat'); cat.printName();//cat
注释是经过call和apply实现的方式,其实本质是同样的。继承实现了,闲着无聊,咱们来打印下:this
console.log(cat instanceof Animal);//false
此次别说后端开发看不下去,我都忍不了了。这种方式只能说是经过JavaScript的语法机制,模拟实现继承,看起来好像是那么回事,否则怎么叫对象冒充呢。搜索引擎
function Animal(name) { this.name = name; } Animal.prototype.printName = function () { console.log(this.name); }; function Cat() { } Cat.prototype = new Animal('cat'); var cat = new Cat(); cat.printName();//cat
打印:spa
console.log(cat instanceof Animal);//true
此次对了,cat也是Animal类的实例了,有点面向对象的的意思了。我又闲着无聊了(约么?),再来打印
console.log(cat.constructor); //[Function: Animal]
咦,又不对了,为啥cat的constructor指向Animal,不该该指向Cat么?
问题出在Cat.prototype = new Animal('cat')上,直接给prototype赋值,改变了constructor的指向,这个时候,咱们还要作个处理
function Animal(name) { this.name = name; } Animal.prototype.printName = function () { console.log(this.name); }; function Cat() { } Cat.prototype = new Animal('cat'); Cat.prototype.constructor = Cat; var cat = new Cat(); console.log(cat.constructor); //[Function: Cat]
可是又有人吐槽了,new Cat()为啥把名称放到new Animal()里面,看起来太奇怪了,综合一下就有了第三中——混合型。
实际上instanceof的判断原理是跟原型链是相关的。你们自行脑洞!
function Animal(name) { this.name = name; } Animal.prototype.printName = function () { console.log(this.name); }; function Cat(name) { Animal.call(this, name); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat = new Cat('cat'); cat.printName();//cat
看起来舒服点了,也就是舒服那么一点点。
直接拿混合型的继承来讲明了,这个比较简单
function Animal(name) { this.name = name; } Animal.prototype.printName = function () { console.log(this.name); }; function Cat(name) { Animal.call(this, name); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; Cat.prototype.printName = function () { console.log('The name is:' + this.name); }; var cat = new Cat('cat'); cat.printName();//The name is:cat
CoffeeScript的面向编程的语法同JavaScript比较起来,真是天上地下。一个阳春白雪,一个下里巴人。可是有一点咱们要记住:CoffeeScript只是编译到JavaScript,它只是在JavaScript的基础上进行了语法的抽象,本质上仍是JavaScript。
CoffeeScript采用class关键字声明类,整个语法看起来更加简明流畅。
#编译前 class Animal constructor: (name)-> @name = name printName: -> console.log(@name) animal = new Animal 'animal' animal.printName() #animal #编译后 var Animal, animal; Animal = (function () { function Animal(name) { this.name = name; } Animal.prototype.printName = function () { return console.log(this.name); }; return Animal; })(); animal = new Animal('animal'); animal.printName();
constructor是构造函数,就上面的例子来讲,还能够简写,实际上效果是同样的
class Animal constructor: (@name)-> printName: -> console.log(@name) animal = new Animal 'animal' animal.printName() #animal
CoffeeScript将咱们习惯性的书写方式变成丰富的语法糖。说到这里我就想说一句了,能不能把构造函数换个字符,constructor丫太长了。
继承使用的是extends关键字
#编译前 class Animal constructor: (@name)-> printName: -> console.log(@name) class Cat extends Animal cat = new Cat 'cat' cat.printName() #cat #编译后 var Animal, Cat, cat, extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; Animal = (function () { function Animal(name) { this.name = name; } Animal.prototype.printName = function () { return console.log(this.name); }; return Animal; })(); Cat = (function (superClass) { extend(Cat, superClass); function Cat() { return Cat.__super__.constructor.apply(this, arguments); } return Cat; })(Animal); cat = new Cat('cat'); cat.printName();
咱们简单分析下编译后的extend函数,对JavaScript原型链不是很熟的能够跳过这段。两个参数分别是子类child和父类parent,第一段代码:
for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; }
这段代码就是将父类上面的属性拷贝到子类上,由于JavaScript当中函数也是对象,能够扩展属性的。什么意思?看代码
class Animal constructor: (@name)-> printName: -> console.log(@name) Animal.prop = 'Animal prop' class Cat extends Animal console.log Cat.prop #Animal prop
第二段代码:
function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor();
可能你们看不大明白,我稍微改动下,换种写法
child.prototype = new parent(); child.prototype.constructor=child;
这里就是咱们上面提到的原型链继承。再看最后段代码:
child.__super__ = parent.prototype;
这里是为了在子类中调用父类的方法,实现多态,看下面的例子就知道了。
编译后的代码太长,就不粘贴了,看CoffeeScript代码更易于学习。
class Animal constructor: (@name)-> printName: -> console.log(@name) class Cat extends Animal printName: -> console.log 'Cat name:' + @name cat = new Cat 'cat' cat.printName() #Cat name:cat
class Animal constructor: (@name)-> move: (meter)-> console.log(meter) class Cat extends Animal move: -> console.log 'Cat move' super 4 cat = new Cat 'cat' cat.move() #Cat move 4
有任何问题,欢迎你们批评指出,咱们共同进步。