狭义的抽象,也就是代码里的抽象,就是把一些相关联的业务逻辑分离成属性和方法(行为),这些属性和方法就能够构成一个对象。javascript
这种抽象是为了把难以理解的代码概括成与现实世界关联的概念,好比小狗这样一个对象:属性能够概括出“毛色”、“品种”、“年龄”等等;方法(行为)能够概括出“叫”、“跑”、“啃骨头”等。java
注意:这里的抽象不是指抽象类,抽象类我认为放封装一节讲比较合适。浏览器
Javascript里建立一个对象有不少种方法,也很是简单,就以小狗这个对象为例:函数
1 var dog = { 2 hairColor: '白色', 3 breed: '贵宾', 4 age: 2, 5 shout: function() { 6 console.log('汪!汪!汪!'); //这里是你的业务逻辑代码,这里我就简单用这个来代替 7 }, 8 run: function() { 9 console.log('吃我灰吧,哈哈!'); 10 }, 11 gnawBone: function() { 12 console.log('这是本狗最幸福的时候'); 13 } 14 };
很是便捷,但这时候有个问题:我要建立不少只dog怎么办?每建立一只dog我都var一遍吗?this
因而这时候咱们就引入了类(class)的概念,类即为相似,具备相同特征的对象的原型,它的做用是建立对象(实例),类自己并不存在内存中,当运行类的代码时,一个对象(实例/instance)就被建立在内存中了,能够简单的理解类为创造对象的工厂。spa
Javascript(ES5)里没有类(class)这东西,它是经过构造函数来实现类的:prototype
1 /*类的建立*/ 2 function Dog() { 3 //构造函数:人们一致协定把构造函数的名字(即类名),首字母大写,以便区分 4 this.hairColor = '白色'; 5 /*this指向被创造的对象(实例),若是不明白能够简单的理解为给对象(this)赋予hairColor这个属性 6 */ 7 this.breed = '贵宾'; 8 this.age = 2; 9 this.runSpeed = null; //string 10 /*属性的声明必定要放在构造函数的最顶部; 11 有的属性可能一开始没有初始值,会在方法里才赋值,但你必定要在构造函数里声明一下 12 有必要的话再声明一下属性的类型 13 */ 14 } 15 Dog.prototype.shout = function() { 16 /*咱们把方法追加到构造函数的prototype属性,而不是直接在构造函数里用this.shout = function(){}; 17 这样的好处是会让Dog创造的全部对象都共享一个方法,从而节约内存; 18 通常来讲属性在构造函数里赋予,方法在prototype里赋予; 19 更多prototype的知识就看书去吧,这里不会深讲,做者要保持本章知识的封装性; 20 */ 21 console.log('汪!汪!汪!我是一只' + this.age + '岁的' + this.hairColor + this.breed); 22 //方法里经过this能够访问属性 23 } 24 Dog.prototype.run = function() { 25 this.runSpeed = '10m/s'; 26 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 27 } 28 Dog.prototype.gnawBone = function() { 29 console.log('这是本狗最幸福的时候'); 30 } 31 /*对象(实例)的建立与使用*/ 32 var dog1 = new Dog(); // 33 console.log(dog1.breed); //log: '贵宾' 34 dog1.shout(); //log: '汪!汪!汪!我是一只2岁的白色贵宾' 35 var dog2 = new Dog(); //建立多只dog(对象/实例) 36 var dog3 = new Dog(); 37 /*dog一、dog二、dog3这些对象的属性是各自的,但方法是共享的*/ 38 dog1.hairColor = '黑色'; //修改dog1的属性 39 console.log(dog1.hairColor); //log: '黑色';dog1属性已被修改; 40 console.log(dog2.hairColor); //'白色';其它对象不受影响; 41 console.log(dog3.hairColor); //log: '白色' 42 console.log(dog1.shout === dog2.shout); //log: true;dog1的shout方法和dog2的是同一个方法;
但新的问题又来了:我想建立一个棕色的泰迪怎么办呢?建立的时候传递参数给类的构造函数就能够解决这问题。
上个案例中说了,类建立的各个对象(实例)的方法的共享的,属性是各自的,但我就是想建立一个共享的属性怎么办呢?好比说我想建立一个instanceNumber属性来记录程序中dog对象(实例)的个数。code
这时候就能够给这个Dog类建立一个静态属性,静态属性是属于类的,因此它是不会随对象(实例)的变化而变化,能够用来设置该类的全局变量,全局参数配置等。对象
下面是新代码:blog
1 function Dog(hairColor, breed, age) { 2 this.hairColor = hairColor; //string,这种依赖参数的属性最好声明下类型或接口; 3 this.breed = breed; //string 4 this.age = age; //number 5 this.runSpeed = null; //string 6 Dog.instanceNumber++; 7 } 8 Dog.instanceNumber = 0; //建立静态属性 9 Dog.prototype.shout = function() { 10 console.log('汪!汪!汪!我是一只' + this.age + '岁的' + this.hairColor + this.breed); 11 } 12 Dog.prototype.run = function() { 13 this.runSpeed = '10m/s'; 14 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 15 } 16 Dog.prototype.gnawBone = function() { 17 console.log('这是本狗最幸福的时候'); 18 } 19 Dog.prototype.getInstanceNumber = function() { //为访问静态属性封装方法 20 return Dog.instanceNumber; 21 } 22 var dog1 = new Dog('白色', '贵宾', 2); 23 console.log(Dog.instanceNumber); //log: 1;虽然能够这样访问静态属性,而且还能够修改它,但坚定不推荐这样作 24 console.log(dog1.getInstanceNumber()); //log: 1;正确的作法!为何要这样作,在封装一节会详细讲 25 var dog2 = new Dog('棕色', '泰迪', 1); 26 console.log(dog1.getInstanceNumber()); //log: 2; 27 var dog3 = new Dog('黑色', '土狗', 3); 28 console.log(dog1.getInstanceNumber()); //log: 3; 29 dog1.shout(); //log: '汪!汪!汪!我是一只2岁的白色贵宾' 30 dog2.shout(); //log: '汪!汪!汪!我是一只1岁的棕色泰迪' 31 dog3.shout(); //log: '汪!汪!汪!我是一只3岁的黑色土狗'
接下来是ES6的类的建立方法,这段代码能够直接在Chrome浏览器运行,新手可不用管这部分,包括再下面的TypeScript代码。
1 class Dog { 2 constructor(hairColor, breed, age) { //表明这个类的构造函数 3 this.hairColor = hairColor; //string 4 this.breed = breed; //string 5 this.age = age; //number 6 this.runSpeed = null; //string 7 Dog.instanceNumber++; 8 } 9 shout() { 10 console.log('汪!汪!汪!我是一只' + this.age + '岁的' + this.hairColor + this.breed); 11 } 12 run() { 13 this.runSpeed = '10m/s'; 14 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 15 } 16 gnawBone() { 17 console.log('这是本狗最幸福的时候'); 18 } 19 getInstanceNumber() { 20 return Dog.instanceNumber; 21 } 22 }//ES6类的建立就比较舒服了,class把整个类用{}包裹在一块儿,写法也比较方便。 23 Dog.instanceNumber = 0;//遗憾的是ES6里也没有规范静态属性,仍是跟ES5同样的用法,听说ES7已有一个静态属性的提案 24 let dog1 = new Dog('白色', '贵宾', 2); 25 let dog2 = new Dog('棕色', '泰迪', 1); 26 let dog3 = new Dog('黑色', '土狗', 3); 27 dog1.shout(); //log: '汪!汪!汪!我是一只2岁的白色贵宾' 28 dog2.shout(); //log: '汪!汪!汪!我是一只1岁的棕色泰迪' 29 dog3.shout(); //log: '汪!汪!汪!我是一只3岁的黑色土狗' 30 console.log(dog1.getInstanceNumber()); //log: 3;
TypeScript建立对象,TypeScript还有许多有用的特性,但本章不方便介绍更多。
class Dog { hairColor: string;//class的全部属性必须在顶部所有声明,不然没法经过编译,这让类的建立更加规范; breed: string;//而且能够声明属性类型,若是给属性赋值的时候类型不正确也没法经过编译,固然,你也能够不声明类型或声明为any任何类型; age: number; runSpeed: string; static instanceNumber: number = 0;//TS静态变量的声明就比较舒服了,在class的{}里面,保证了代码的干净 constructor(hairColor, breed, age) { this.hairColor = hairColor; this.breed = breed; this.age = age; Dog.instanceNumber++; } shout() { console.log('汪!汪!汪!我是一只' + this.age + '岁的' + this.hairColor + this.breed); } run() { this.runSpeed = '10m/s'; console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); } gnawBone() { console.log('这是本狗最幸福的时候'); } getInstanceNumber() { return Dog.instanceNumber; } } let dog1 = new Dog('白色', '贵宾', 2); let dog2 = new Dog('棕色', '泰迪', 1); let dog3 = new Dog('黑色', '土狗', 3); dog1.shout();//log: '汪!汪!汪!我是一只2岁的白色贵宾' dog2.shout();//log: '汪!汪!汪!我是一只1岁的棕色泰迪' dog3.shout();//log: '汪!汪!汪!我是一只3岁的黑色土狗' console.log(dog1.getInstanceNumber());//log: 3;
上面的例子是咱们明确知道要建立一个对象(实例)dog,但实际开发当中是没有人告诉咱们须要建立哪些对象的,领导给咱们的只有需求,因此咱们要分析需求的业务逻辑,把需求分解成一个个对象。
好比说如今领导给咱们一个需求:作一个超市收银系统,分为两种角色:收银员和管理员,收银员能够查询物品信息、统计价格、录入帐单信息、打印小票,管理员能够查看帐单信息、统计帐单信息。
注意需求中的名词:收银员、管理员、物品信息、帐单、小票,这些就是自然的对象,这是最初步的抽象。
让咱们再注意动词:查询、统计、录入、打印,咱们是否是也能够抽象成对象?查询器?统计器?
而后咱们开始coding吧,不要纠结本身的抽象是否完美,做者很赞同Facebook的一句标语:Done is better than perfect(比完美更重要的是完成).
当某个对象的代码不断膨胀,慢慢超出控制的时候(做者本身的的标准是一个对象尽可能不超过300行代码),这时候你就得考虑更深层次的抽象了,查找这个对象里代码比较多的属性、方法、而后抽象成另外一个对象,把新对象做为原先对象的成员(属性)。
function Dog(){ this._tail = new Tail();//把尾巴tail抽象成另外一个对象,做为dog的一个属性; }
固然,你成了老司机后能够一开始就把一个对象再抽象出许多成员对象,随你喜欢。
若是你喜欢做者的文章,记得收藏,你的点赞是对做者最大的鼓励;
做者会尽可能每周更新一章,下一章是讲封装;
你们有什么疑问能够留言或私信做者,做者尽可能第一时间回复你们;
若是老司机们以为那里能够有不恰当的,或能够表达的更好的,欢迎指出来,做者会尽快修正、完善。