发表于: 2017-04-11 15:34:54
1 1207
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
参考三:《Javascript高级程序设计》chapter 6
8.更多讨论
new命令的原理?
构造函数中的return语句的作用?
面向对象编程的继承原理?
鸣谢
感谢大家观看
BY : 黄雄 | 胡思豪|李维文
评论