面向对象
- 面向过程的思维方式:就是把解决问题的关注点放到解决问题的每一个详细步骤上;
- 面向对象的思维方式:就是把解决问题的关注点放到解决问题需要的一系列对象身上。
- 面向对象是一种思维方式,和我们代码关系不大,它把解决问题的关注点放到了解决问题需要的一系列对象身上。
- 面向对象是对面向过程的封装。
什么是对象:
- 万物皆对象;
- JavaScript 中的所有事物都是对象:字符串、数字、数组、日期,等等;
- 在 JavaScript 中,对象是拥有属性和方法的数据。
- 在js中,
键值对的组合
就是对象,键值对有属性和方法。
属性和方法
- 属性是与对象相关的值,用来描述对象的相关信息,也叫对象的特征;
- 方法是能够在对象上执行的动作,对象的行为。
面向对象和面向过程的简单比较
- 使用面向过程思维向body中添加div:
<script>
var div = document.createElement("div");
div.style.width = "100px";
div.style.height = "100px";
div.style.backgroundColor = "red";
document.body.appendChild(div);
</script>
- 使用面向对象思维解决:
<script src="../jquery-1.11.1.min.js"></script>
<script>
$("body").append("<div style='height:100px;width:100px;border:1px solid red;background-color: green'></div>");
</script>
函数封装
- 优点:使代码复用性更高;
- 缺点:
- 全局变量污染, 解决方式:使用面向对象。
- 代码结构不清晰,维护不方便: 在对象里面再将功能相近的函数放到一个对象里。
var person = {
learn:function (course){
console.log("我正在主修" +couse);
}
eat:function(meal){
console.log("我正在吃"+meal);
}
}
person.eat("午饭");
- 使用对象进行封装后的优势:
- 暴露在全局的只有一个对象名,不会造成全局变量污染;
- 使用对象将代码进行功能模块的划分,有利于日后的维护。
JS高级讲义
面向对象的三大特性
-
三大特性:封装、继承、多态。
-
封装
把一些属性和方法装到一个对象里面,让它们变得有意义。 -
继承
把别的对象的方法和属性拿过来用,就是继承 -
混入式继承(最常用):for in
<!--混入式继承:for in-->
<script>
var obj = {
name:"刘德华",
say:function () {
console.log("世界,你好,我是刘德华!!!");
}
};
var person = {};
for (var k in obj) {
//k可以获取到对象的每一个属性
//obj[k]可以获取到对象的每一个属性的值
//这里使用k给对象新增属性时候,不可以使用点语法;同时,这里获取属性的值时也不可以使用点语法
person[k] = obj[k];
}
person.say();
</script>
- 注:这里使用k给对象新增属性时候,不可以使用点语法;同时,这里获取属性的值时也不可以使用点语法。
- 多态
多态是在强类型语言中比较常用,JavaScript中没有相应的体现。
使用父类的对象接受子类的对象(JavaScript中用不到);
多态:父类的属性和方法可以供所有子类共享,但是父类不能访问子类的属性和方法,使用多态来隐藏不同
创建对象的四种方式
- 使用字面量创建对象
var o = {key:value, key:value...}
-
只能创建一次性对象,用一个创建一个,不能复用,如果要创建多个对象,会造成代码冗余,资源浪费;
-
使用内置构造函数创建对象
var o = new Object();
var o = new Array();
-
创建出来的对象都是空的对象,利用对象的动态特性手动的去为对象添加属性和方法。造成代码重复。
-
封装简单的工厂函数(不推荐使用)
function createObj(singer,song){
var obj = new Object();
obj.name = singer;
obj.song = song;
obj.sing = function (){
console.log("让我来为你唱首歌);
}
return obj;
}
var obj1 = createObj("那英","默");
var obj2 = createObj("南金岭","逆流成河");
-
自定义构造函数
- 构造函数是用来初始化对象的(给对象新增成员),构造函数首字母要大写,以示区分;
- new是用来创建对象的。
function Person(){
//默认隐含操作,把new出来的Object赋值给this
this.name = xxx;
this.age = 20;
this.study = function(){
//code
}
//默认隐含操作:返回this
}
var p = new Person();
//如果调用的时候没有参数,小括号可以省略,可以使用var p = new Peron;
0.构造函数名首字母要大写;
1.构造函数一般和new关键字一起使用;
2.构造函数返回值默认为新创建好的对象, 如果手动返回基本数据类型,不影响默认返回值,如果返回的是对象,那么新创建出来的对象将不会返回,取而代之的是return后面的对象。一般都不会去写这个return语句。
-
构造函数与普通函数的区别:
- 构造函数也属于函数的一种;
- 构造函数是用来初始化对象的,一般和new关键字一块使用;
- 构造函数里面的this指向使用new创建出来的对象;
- 构造函数的返回值:默认返回我们创建
出来的对象。
-
构造函数(constructor)的执行步骤:
1.使用new关键字创建对象;
2.调用构造函数,隐含操作:将new创建出来的对象赋值给构造函数内的this;
3.使用this给新创建出来的对象增加成员;
4.默认返回新创建出来的这个对象(普通的函数,如果不写返回语句,会返回undefined)。 -
对象是无序的键值对的集合。
-
js中提供了两个方法来调用其他对象的方法:
-
call;
-
apply。
<!--获取对象的具体类型-->
var typeStr = Object.prototype.toString.call(想要获取类型的对象);
//[object Array]
typeStr = typeStr.slice(8,-1);
- 注:
function Animal(name,age,barkWay){
this.name = name;
this.age = age;
this.bark = barkWay;
}
Animal("","",function (){
console.log("我是张学友!!!");
});
window.bark(); //我是张学友!!!
- 如果像使用正常函数一样使用构造函数,构造函数中的this将不再指向新创建出来的对象(因为根本就没有创建对象);
- 此时构造函数中的this指向的是window全局对象;
- 这时使用this给对象添加成员的时候,全部都添加到window上。
原型
构造函数存在问题
构造函数中的方法,每新创建一个对象的时候,该对象都会重新的创建一次这个方法,每个独享独占一个方法
但是该方法内容完全相同,所以造成资源浪费
1.解决办法1
将构造函数内的方法,进行提取,放在构造函数外面,并将该函数赋值给构造函数内的方法;
那么创建出来的对象,都会指向构造函数外面的这个函数,达到共享的目的;
此时使用这种方式写好的方法中的this指向的就是调用该方法的对象(谁调用指向谁)(默认的指向window);
问题:1.全局变量增多,造成全局变量污染;2.代码结构混乱,不容易维护
2.解决办法2
使用原型
原型是什么?
在构造函数创建出来的时候,系统会默认的创建并关联一个对象,这个对象就是原型,原型对象默认是空对象。
默认的原型对象中会有一个属性constructor指向该构造函数。
- 如何访问构造函数的原型:构造函数.prototype。
- prototype是构造函数的属性,和对象没有关系。
原型的作用
原型对象中的成员(属性和方法),可以被使用和它关联的构造函数创建出来的所有对象使用。
所以我们可以将构造函数中需要创建的函数,放到原型对象中存储,这样就解决全局变量污染及代码结构混乱的问题。
1\. 实例化:通过构造函数创建对象的过程。
2\. 实例:通过构造函数实例化出来的对象就是该构造函数的一个实例。
3\. 说实例的时候,一定要指定对应的构造函数:xxx对象是xxx构造函数的实例。
原型对象的使用
1\. 构造函数.prototype;
2\. 使用对象的动态特性,为原型对象添加成员(添加成员较少时候推荐使用);
3\. 直接替换原型对象(添加成员比较多的时候推荐使用)。
注意事项:
直接替换原型对象,会导致替换之前创建的对象的原型和替换之后创建的对象的原型不是同一个。
原型的使用该注意事项
1.使用对象访问属性和方法的时候,会先在对象中查找,如果找到了就直接使用;如果没有找到,就去原型中查找。如果原型中还没有,如果是属性,就是undefined,如果是方法,就会报错。
2.使用对象设置属性的时候,只会在对象本身中查找,不会去原型中查找,如果在对象本身中没有找到这个属性,则给该对象新增一个属性,如果在对象中有这个属性,修改这个属性;
3.如果在原型对象中有引用类型的属性,那么使用对象进行修改该属性内容,则其他所有跟这个原型对象相关的对象都会受到影响;
4.一般情况下不会将属性添加到原型对象中,只会将需要共享的方法,添加到原型对象中。
image
__proto__
- 通过构造函数访问原型对象:
Person.prototype;
- 通过对象访问原型对象:
p.__proto__;
:
1.这个属性不是标准属性,所以存在通用性问题;
2.为了保证通用性,一般不推荐使用这个属性;
3.调试的时候,可以使用这个属性;
4.这个属性是原型中的属性。
constructor
-
原型对象在创建出来的时候,会默认的有一个constructor属性,指向对象的构造函数;
-
通过对象访问构造函数:
p.constructor('','');
,相当于直接使用Person('','');
; -
替换原型时候的注意事项:
在新替换的原型中,constructor属性会变成Object,会影响三角结构关系的合理性;
so,在新替换的原型中,为了保证整个构造函数---原型---对象
之间的合理性,应手动添加constructor属性,赋值为关联的构造函数(constructor: Person)。
Person.prototype = {
constructor : Person
};
网友评论