发表于: 2017-04-11 15:34:54

1 1209



1.今天完成的事情

(1)终于终于终于快完成任务四,也调试的差不多了,明天再稍微加一些小的东西,比如点叉叉结束可以立即终止任务等就ok了。

(2)今天公司停电,去四川省图书馆自习了一波,也是很嗨,工作日人不是很多,就是那边网不是太好,以后空闲的时候可以往那边走走,上上自习还是很不错的。

(3)加不杀人,可以直接空过的时候,之前的写法会让step混掉,刚开始想的是判断死的人数的奇偶行,奇数表示刚完成杀人,偶数表示刚完成投票,写好之后,空过的时候就出问题了。这种方法pass,然后又写了个条件,点击杀人界面的确认按钮就改变这个条件,再用这个条件判断是否点击过杀人按钮就行了,很简单一个事情,轴了很久,主要是自己代码写的太乱了,调试的时候有些难受,如果明天任务四完成的顺利,就抽时间用Jquery把代码再精简一遍,看还有哪些可以抽象出来的逻辑。

2.明天要完成的事情

完成任务四,Jquery重写任务四,开始任务五

3.遇到的问题

就是上述那个杀人可以空过的问题,感觉被自己坑了

4.收获

get省图书馆一个自习的好去处

终于要完成任务四了,还是很开心

下面是昨天PPT的文字链接~

【成都-第七十七期】简述面向对象编程

1.背景介绍

什么是面向对象编程?

面向对象编程”(Object Oriented

Programming,缩写为OOP)是目前主流的编程范式。它的核心思想是将真实世界中各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

主要概念为:

 把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)/泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派(dynamic dispatch)。

 Javascript是一种基于对象(object-based)的语言,遇到的东西几乎都是对象,但是它不是一种面对对象的语言。像其他语言里面的class(类),它就没办法直接用了。(听说ES 6可以用了,笔者一直学的ES5,6暂未研究,有兴趣的同学可以去看看教程)。

2.知识剖析

2.1对象的概念

因为JS是一个基于对象的语言,所以我们遇到的大多数东西几乎都是对象。例如函数就是一个对象,如果你要在js里面新建一个对象,这样写其实就是创建了一个object的实例。

对象就是一个容器,封装了属性和方法。

属性就是对象的状态,比如下面的name属性。

方法就是写在对象里面的函数,也就是对象的行为,比如下面的sayName方法。

var person = new object()
person.name = "Tom"; <!-- person对象的name属性 -->
person.sayNmae = function() {
alert(this.name);<!-- person对象的sayName方法 -->
    }

2.2 工厂模式

“面向对象编程”的第一步,就是要生成“对象”。

但是很多时候我们不得不面临重复生成很多对象的情况,如果我有一千个人要记录他们的信息,像上面这种方法写的话,大大增加了代码的重复量,为了解决这个问题,人们开始使用工厂模式的一种变体,写法如下页。

虽然工厂模式解决了代码复用的问题,但是却没办法显示实例(person1)和对象o之间的关系,比如aler(person1 instanceof o); //false

代码演示:

function Person(name,age, job) {
this.name = name;
   this.age = age;
   this.job = job;
   this.sayName = function() {
alert(this.name)
};
}
person1 = new Person("Tom",20,"Engineer")
person2 = new Person("Damon",22,"Waiter")

2.3 构造函数

后来就出现了构造函数,用来创建特定类型的对象,可以将实例和对象联系起来,用到了JS中的“this”,写法如下:

 这样对象和实例之间就有关系了,以new这种方式调用构造函数会经历4个步骤:

(1)创建一个新对象。

(2)将构造函数的作用域赋给新对象(这个this就指向了这个新对象)。

(3)执行函数内代码(给对象添加属性)

(4)返回新对象。

代码演示:

function Person(name,age, job) {
this.name = name;
   this.age = age;
   this.job = job;
   this.sayName = function() {
alert(this.name)
};
}
person1 = new Person("Tom",20,"Engineer")
person2 = new Person("Damon",22,"Waiter")

构造函数特点:

上面代码中,Persoon就是构造函数,它提供模板,用来生成对象实例。为了与普通函数区别,构造函数名字的第一个字母通常大写。

 构造函数的两个特点:

1.函数体内部使用了this关键字,代表了所要生成的对象实例。

2.生成对象的时候,必需用new命令,调用函数。

如果忘了使用new命令,直接调用构造函数会导致构造函数变成普通函数,就不会生成实例对象,并且此时的this这时代表全局对象,将造成一些意想不到的结果。

var Vehicle = function (){
this.price = 1000;
};

var v = Vehicle();
v.price
// Uncaught TypeError: Cannot read property 'price' of undefined

上面代码中,调用Vehicle构造函数时,忘了加上new命令。结果,price属性变成了全局变量,而变量v变成了undefined。

因此必须小心,记得使用new命令。

2.4原型和原型链

原型prototype

  JavaScript的每个对象都继承另一个对象,后者称为“原型” (prototype)对象。只有null除外,它没有自己的原型对象。原型对象上的所有属性和方法,都能被派生对象共享。这就是JavaScript继承机制的基本设计。通过构造函数生成实例对象时,会自动为实例对象分配原型对象。每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。

原型链

 对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototypechain)。比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。

  "原型链”的作用是,读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。

 需要注意的是,一级级向上,在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

利用原型(prototype)的继承特性,我们可以将我们的函数写成:

function Person() {
};
Person.prototype.name = "Tom";
Person.prototype.age = "20";
Person.prototype.job = "engineer";
Person.prototype.sayName = function() {
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
alert(person1.sayName == person2.sayName); //true

因为原型的继承,person1和person2的prototype都指向Person的prototype,所以这两个函数其实是相等的。但是用工厂函数或者构造模式, alert(person1.sayName == person2.sayName);就绝对不会为真了。

奇淫巧技1:每次写属性都要加一个prototype是不是很麻烦,其实还有另外一种写法:

function Person() {
}
Person.prototype = {
name : "Tom";
age : "20";
job : "engineer";
sayName : function() {
alert(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
alert(person1.sayName == person2.sayName); //true

2.5 构造函数的继承

让一个构造函数继承另一个构造函数,是非常常见的需求。

也有多种方法实现,各有优缺点。比如现在有一个动物对象的构造函数,和一个猫对象的构造函数。

function Animal() {
this.species = “动物;
}
function Cat(name,color) {
this.name = name;
   this.color = color;
}

如何才能使Cat继承Animal呢?
2.5.1 构造函数绑定

第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:

function Cat(name,color){
Animal.apply(this, arguments); //加的
   this.name = name;
   this.color = color;
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

2.5.2 prototype(原型)模式

第二种方法更常见,使用prototype属性。如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。相当于将Cat原先的原型对象删除,重新赋一个Animal实例的值。但是任何一个prototype对象都有一个constructor属性,指向它的构造函数。这个时候Cat的构造函数也改变了,变成了Animal。

2.5.3 prototype(原型)模式

所以我们需要“Cat.prototype.constructor = Cat”将Cat的构造函数重新指向为Cat,不然的话会很容易出问题。

这是很重要的一点,编程时务必要遵守。如果替换了prototype对象,

b.prototype = new a();

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

b.prototype.constructor = b;

第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。现在我们将Animal对象改写

function Animal() {
Animal.prototype.species = "动物";
}

然后,将Cat的prototype对象,指向Animal的prototype对象,这样就完成了继承。

Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。所以Animal.prototype的构造函数也变成了Cat。这个时候我们就需要引入一个空对象作为中转的中介,无论Cat的constructor如何变,只会影响到中转对象F而无法影响到父对象Animal了。

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

然后我们将上述方法封装成为一个函数,使用起来就很方便了

function extend(Child, Parent) {
var F = function(){};
   F.prototype = Parent.prototype;
   Child.prototype = new F();
   Child.prototype.constructor = Child;
   Child.uber = Parent.prototype;
}

使用的时候方法如下:

extend(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

奇淫巧技2:封装函数的时候怎么方便怎么写不必太过考虑语义化的东西,比如写个状态机,直接将状态用数字表示,这样比字符串的形式好判断多了。但是一点也不语义化。

3.常见问题

必须要声明new来创建实例对象吗?

为了保证构造函数必须与new命令一起使用,一个解决办法是,在构造函数内部使用严格模式,即第一行加上use strict。

function Fubar(foo, bar){
'use strict';
   this._foo = foo;
   this._bar = bar;
}

Fubar();
// TypeError: Cannot set property '_foo' of undefined

上面代码的Fubar为构造函数,use

strict命令保证了该函数在严格模式下运行。由于在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,导致不加new调用会报错(JavaScript不允许对undefined添加属性)。

另一个解决办法,是在构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。

function Fubar(foo, bar){
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
   }

this._foo = foo;
   this._bar = bar;
}

Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1

上面代码中的构造函数,不管加不加new命令,都会得到同样的结果。

5.编码实战

用面对对象编程的思想写状态机

6.扩展思考

面向对象与面向过程的区别?

传统的过程式编程(procedural programming)由一系列函数或一系列指令组成;而面向对象编程的程序由一系列对象组成。

每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。因此,面向对象编程具有灵活性、代码的可重用性、模块性等特点,容易维护和开发,非常适合多人合作的大型应用型软件项目。

7.参考文献

参考一:阮一峰 http://javascript.ruanyifeng.com/oop/basic.html

参考二:阮一峰 http://www.ruanyifeng.com/blog/search.html?cx=016304377626642577906%3Ab_e9skaywzq&cof=FORID%3A11&ie=UTF-8&q=Javascript+%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B&sa.x=9&sa.y=8

参考三:《Javascript高级程序设计》chapter 6

8.更多讨论

new命令的原理?

构造函数中的return语句的作用?

面向对象编程的继承原理?

鸣谢

感谢大家观看

BY : 黄雄 | 胡思豪|李维文


返回列表 返回列表
评论

    分享到