面向对象的基本概念
面向对象和面向过程分别是什么?
都是思维方式.
面向过程的思维方式是将解决问题的关注点放在解决问题的每一个详细步骤上。
面向对象的思维方式是将解决问题的关注点放在解决问题所需要的一系列对象上。
面向对象其实就是对面向过程封装
什么是对象
万物皆对象
JavaScript中的对象就是无序键值对的集合
什么是属性什么是方法
JavaScript对象中,如果键对应的值是数据,那么这个键就称为属性
如果键对应的值是函数,那么这个键就称为方法
名词提炼法
在一个句子中,只要是名词就可以把他看做一个对象,这种提炼对象的方式就叫名词提炼法
面向对象编程举例
- 使用面向过程的思维方式去考虑问题,写出来的代码存在如下问题:
- 代码复用性太差
- 结构混乱
- 全局污染
- 使用函数进行封装,解决复用性的问题,但又出现以下问题
- 全局污染
- 代码结构混乱
- 使用对象进行封装,解决了以上所有问题
面向对象的三大特性
封装
在面向对象程序设计方法中,封装(英语:Encapsulation)是指,一种将抽象性函数接口的实现细节部分包装、隐藏起来的方法。同时,它也是一种防止外界调用端,去访问对象内部实现细节的手段,这个手段是由编程语言本身来提供的。这两个概念有一些不同,但通常被混合使用。封装被视为是面向对象的四项原则之一。
适当的封装,可以将对象使用接口的程序实现部分隐藏起来,不让用户看到,同时确保用户无法任意更改对象内部的重要数据。它可以让代码更容易理解与维护,也加强了代码的安全性
将数据和行为全部封装到对象内,实现一些功能,给外界提供一些接口,外界在使用功能的时候只需要通过接口来调用,而不需要关系对象内部的具体实现,这就是封装
继承
继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
一个对象没有某些属性和方法,另外一个对象有,拿过来用,就是继承
多态
JS中没有多态
父类指针指向子类对象
创建对象的方式
字面量方式
var obj = {
key:value,
key:value,
key:value
};
通过字面量创建对象,每次创建的对象都只能当次使用,下次如果还要使用,就得再写一次,复用性差。
内置构造函数
var obj = new Object();
每次创建的对象都是空的对象,都得去手动的添加成员,所以也有复用性问题
自定义构造函数
function Person(){
this.name = "li2Dog";
this.age = 18;
}
var p = new Person();
构造函数
构造函数的概念
构造函数也是一个函数,和普通函数不同的地方在于,构造函数是用来初始化对象的。
构造函数的特征
- 首字母大写
- 通常和new关键字配合使用
- 不需要写return语句,默认返回新创建的对象
- 如果手动写了return语句
- 如果是值类型的数据,则对返回值没有任何的影响
- 如果是引用类型的数据,则替换掉默认的返回值
构造函数的执行顺序
- 先使用
new
关键字创建对象 - 调用构造函数,将
this
指针赋值为新创建出来的对象 - 在构造函数内部,使用
this
为新创建出来的对象新增成员 - 默认的返回新创建出来的对象
如果将构造函数当做普通函数来调用
- 函数内部的
this
指向了window
- 返回值默认的为
undefined
原型
原型的概念
构造函数在创建出来的时候,系统会默认的给构造函数创建并关联一个空的对象,这个对象就是原型
原型的作用
原型中的成员可以被何其关联的构造函数所创建出来的所有的对象共享
原型的访问形式
构造函数.prototype
对象.__proto__ 非标准属性,有兼容性问题
原型的使用方式
- 利用动态特性为原型对象添加属性和方法
- 直接替换原型对象
原型的使用注意事项
- 一般情况下将方法放在原型对象中,将属性放在对象中
- 在获取属性的时候,需要遵守属性搜索原则
- 在设置属性的时候,不需要遵守属性搜索原则
- 在替换原型对象的时候,替换之前创建的对象的原型和替换之后创建的对象的原型不一致
继承
混入式继承(mix-in)
var obj = {};
var obj1 = {
name:"周三",
age:18
};
for(var k in obj1){
obj[k] = obj1[k];
}
原型继承
1.使用混入的方式给原型对象添加属性和方法
function Person(){}
var parentObj = {
name : "周天儿",
age : 7
};
for(var k in parentObj){
Person.prototype[k] = parentObj[k];
}
var p = new Person();
- 直接修改原型对象
function Person(){}
Person.prototype.name = "";
Person.prototype.age = 18;
var p = new Person();
- 替换原型对象
function Person(){}
Person.prototype = {
name : "周天儿",
age : 7
};
var p = new Person();
经典继承
var parentObj = {
name : "周天儿",
age : 7
};
//创建一个继承自parentObj的对象obj
var obj = Object.create(parentObj);
原理:原型继承
兼容性问题解决(内置对象不可以随便修改)
function myCreate(obj){
if(Object.create){
return Object.create(obj);
}else{
function F(){}
F.prototype = obj;
return new F();
}
}
原型链
每一个对象都有原型,每一个原型都是对象,每一个原型也都有原型,就形成了链式结构,称为原型链
- 首先画构造函数
- 在画原型对象
- 换出构造函数和原型对象的关系
- 画出实例
- 滑出实例和构造函数以及原型的关系
- 把原型多做对象,找出原型的构造函数
- 从第一步重复
Object.prototype的成员
名称 | 说明 |
---|---|
constructor | 属性,用来指向和原型相关的构造函数,可配置 |
hasOwnProper | 方法,用来检测属性是否存在对象本身 |
isPrototypeOf | 方法,用来检测对象是否另外一个对象的原型 |
propertyIsEnumerable | 方法,用来检测属性是否属于对象本身,并且能否被遍历 |
toString | 方法,将对象转换为字符串,Object.toString方法默认的返回值[object 构造函数名] |
toLocaleString | 方法,将对象转换为本地格式的字符串,本地格式为系统设置 |
valueOf | 方法,当对象参与运算的时候,会默认的调用valueOf方法获取对象的值,如果可以参与运算,则直接使用,如果不能,则调用toString方法 |
proto | 属性,指向对象的原型 |
Function eval
Function
可以使用Function来创建函数,他可以将字符串转换成代码
//如果不传参数,则创建出来的是空函数
var func = new Function();
//如果传一个参数,则这个参数为函数体
var func1 = new Function(methodBody);
//如果传多个参数,则最后一个参数为函数体,之前所有参数为函数的形参名
var func2 = new Function(arg1, arg2, arg3, arg4, argN, methodBody);
eval
可以将字符串转换成代码并且运行
当使用eval
处理JSON格式字符串的时候,会将JSON字符串内的{}
当做代码段来处理,会报错
解决方案:
* 在JSON格式的字符串前后拼接()
* 将变量的声明(var obj
)和=
拼接在JSON格式的字符串之前
eval和Function的区别和联系
相同点
- 都可以将字符串转换成代码
- 都存在安全性问题
- 都存在性能问题
不同点
- Funciton创建出来的是函数,需要手动的去调用
- eval直接会将字符串转成代码进行执行
静态成员和实例成员
静态成员:通过构造函数访问的成员是静态成员
实例成员:通过对象访问的成员是实例成员