狹義的抽象,也就是程式碼裡的抽象,就是把一些相關聯的業務邏輯分離成屬性和方法(行為),這些屬性和方法就可以構成一個物件。
這種抽像是為了把難以理解的程式碼歸納成與現實世界關聯的概念,例如小狗這樣一個物件:屬性可以歸納出「毛色」、「品種」、 「年齡」等等;方法(行為)可以歸納出「叫」、「跑」、「啃骨頭」等。
注意:這裡的抽像不是指抽象類,抽象類別我認為放封裝一節講比較合適。
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#一遍嗎?
於是這時候我們就引入了類別(class)的概念,類別即為類似,具有相同特徵的物件的原型,它的作用是創建物件(實例),類別本身並不存在記憶體中,當運行類別的程式碼時,一個物件(實例/instance)就被創建在記憶體中了,可以簡單的理解類別為創造物件的工廠。
Javascript(ES5)裡沒有類別(class)這東西,它是透過建構函數來實現類別的:
1 /*类的创建*/ 2 function Dog() { 3 //构造函数:人们一致协定把构造函数的名字(即类名),首字母大写,以便区分 4 this.hairColor = '白色'; 5 /*this指向被创造的对象(实例),如果不明白可以简单的理解为给对象(this)赋予hairColor这个属性 6 */ 7 this.breed = '贵宾'; 8 this.age = 2; 9 this.runSpeed = null; //string10 /*属性的声明一定要放在构造函数的最顶部;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 /*dog1、dog2、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物件(實例)的個數。
這時候就可以給這個Dog類別建立一個靜態屬性,靜態屬性是屬於類別的,所以它是不會隨物件(實例)的變化而變化,可以用來設定該類別的全域變量,全域參數配置等。
以下是新程式碼:
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的一个属性; }
当然,你成了老司机后可以一开始就把一个对象再抽象出许多成员对象,随你喜欢。
如果你喜欢作者的文章,记得收藏,你的点赞是对作者最大的鼓励;
作者会尽量每周更新一章,下一章是讲继承;
大家有什么疑问可以留言或私信作者,作者尽量第一时间回复大家;
如果老司机们觉得那里可以有不恰当的,或可以表达的更好的,欢迎指出来,作者会尽快修正、完善。
以上是JavaScript中抽象(demo by ES5、ES6、TypeScript)詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!