# 15、OOP(面向对象编程思想)
# 单例模式
我们把对象数据类型实现把描述同一件事务的属性或者特征归纳汇总在一起,以此避免全局变量冲突问题的方式和思想叫做:单例设计模式;
把描述同一件事务的属性或者方法存放在某一个命名空间下,多个命名空间中的属性和方法是互不干扰的
// 单例模式
// 1、singleton不仅仅是对象名了,在单例模式中,singleton称之为 “命名空间(nameSpace)”
var singleton={
xxx:xxx,
...
};
var singleton={
nameSpace1:{
xxx:xxx,
...
},
nameSpace2:{
xxx:xxx,
...
}
...
};
# 使用单例模式实现模块化开发
模块化开发:把一个复杂页面按照具体功能划分成几大块,然后由不同的人分别去开发,这种模块划分的思想就是模块化开发功能。
//= 项目主管(开发人员):公共模块
var utils={
trim:function(){}
};
//=>陆永勇:搜索模块
var searchModel={
submit:function(){
utils.trim();
},
...
};
//=>唐元帅:天气模块
var weatherModel={
setWeather:function(){},
...
};
//=>陈金广:频道模块
var channelModel={
show:function(){
//=>在当前的命名空间下调取其它命名空间的方法:指定好对应的命名空间名字即可,使用 [NameSpace].[property] 就可以操作了
searchModel.submit();
//=>调取本模块中的一些方法,可以直接使用THIS处理即可:此方法中的THIS一般都是当前模块的命名空间
this.setChannel();
},
setChannel:function(){},
...
};
channelModel.show();
# 高级单例模式
基于JS高阶编程技巧惰性思想,来实现的单例模式,并且可以把一些常用的设计模式(如:命令模式、发布订阅模式、promise设计模式等)融合进来,最后清晰的规划我们的业务逻辑代码,方便后期二次开发和维护,这种设计思想综合体就是高级单例模式,也是最常用的。
var serchModel=(function(){
function submit(){
}
return {
init:function(){
this.submit();
}
}
})();
searchModel.init();
# 对象、类、实例
TIP
对象:万物皆对象 类:对象的具体细分(按照属性或特性细分的一些类别) 实例:某一个类中的具体事物
# 对象
# 对象的创建
两种方式
1.字面形式
var obj = {}
;
2.实例创建方式(构造函数方式)
var obj = new Array()
;
1、对于引用数据类型来说,两种创建方式是大致相同的,只不过,两种方法创建的语法不同。
// 两种创建方式在核心意义上没有差别,都是创建Array这个类的一个实例,但是在语法上是有区别的
// 1、字面量创建方式传递进来什么,都是给数组每一项加入的内容
// 2、构造函数创建方式
new Array(10) // 创建一个长度为10的数组,数组中的每一项都是空
new Array('10') // 如果只传递一个实参,并且实参不是数字,相当于把当前值作为数组的第一项存储进来
new Array(10,20,30) // 如果传递多个实参,不是设置长度,而是把传递的内容当做数组中的每一项存储起来
2、对于基本数据类型来说,字面量方式创建出来的结果和实例方式创建出来的结果是有一定区别的,从严格意义上来讲,只有实例创建出来的结果才是标准的对象,数据类型值也是标准的基本数据类型,也是标准的内置类的实例;对于字面量方式创建出来的结果是基本数据类型的值,不是严格的实例,但是由于JS的松散特点,导致了可以使用 内置类.prototype
上提供的方法;
# 对象的属性描述符
属性描述符
writable
(是否可写): 决定是否可以修改属性的值
enumerable
(是否可枚举): 表示是否可以在for in
中遍历到属性
configurable
(是否可配置): 为true
时代表可以通过Object.defineProperty
方法修改属性描述符
# A instanceof B
检测某一个实例是否属于这个类, 判断A实例是否属于B类
# attr in object
表示
attr
属性是否在对象object
中,不管是私有属性还是公有属性只要存在,用in
来检测都是true
# a.hasOwnProperty(attr)
检测
attr
是否是A
对象的私有属性,而不是原型上的属性。
# 对象数据类型的
普通对象
- 数组
- 正则
Math
数学函数- 一般类的实例
- 函数的
prototype
属性(Function.prototype
除外,它是函数数据类型的) - 实例的
__proto__
属性
# 函数数据类型的
普通函数
所有的类(内置类和自定义类都是)
# JS常用的内置类
# 数据类型的类
内置类
Number
:每个数字或者NaN是它的一个实例
String
:字符串类
Boolean
:布尔类
Null
和 Undefined
:浏览器屏蔽了我们操作null和udnefined这个类
Object
:对象类,每个对象数据类型都是它的实例
Array
:数组类RegExp
:正则类Date
:日期类
Function
:函数类,每个函数都是它的一个实例
# 元素对象或者元素集合的类
TIP
HTMLCollection:
元素集合类
getElementsByTagName()
getElementsByClassName()
querySelectorAll
NodeList
:节点集合类
childNodes
getElementsByName()
HTMLDivElement
HTMLElement
Element
(标签类)
Node
(节点类,Element只是其中的一个元素节点)
# 为什么getElementById的上下文只能是document?(即getElementById为什么只能通过document来调用)?
因为只有在Document这个类上才有
getElementById
这个方法,其他类上(如:HTMLDivElement类)没有getElementById
这个方法,而document是HTMLDocument这个类的一个实例,能通过document.__proto__.__proto__
找到Document这个类的原型上公有的getElementById方法。
# 构造函数设计模式
使用构造函数方式,主要是为了创建类和实例的,也就是基于面向对象思想来实现一些需求
在JS中,当我们使用new xxx()执行函数的时候,此时的函数就不是普通的函数了,而是变为一个类,返回的结果叫做当前类的实例,我们这种new xxx执行的方式称之为
构造函数设计模式
function fn(){
}
new fn();
# 构造函数执行时new都干了些什么?
在new Fn(),执行的时候,是先把函数执行了,也就是后面的Fn()先执行,形成一个私有作用域,形参赋值变量提升,在变量提升完了之后,new操作符才起了作用,这个时候,浏览器开始创建一个新的对象,让Fn中的this指向这个新创建的对象,然后让这个对象的__proto__指向Fn.prototype,然后JS代码才开始继续往下执行,开始往新创建的对象当中添加每个实例私有的属性和方法。JS代码执行完成后,会默认返回当前创建的这个对象。
# 普通函数执行与构造函数执行的区别
区别
构造函数执行的时候,也是先形成一个私有作用域,形参赋值,变量提升,在代码从上而下执行之前,构造函数有特殊的操作:浏览器会在当前的作用域中默认创建一个对象数据类型的值,并且会让当前函数中的this指向创建的这个对象。 然后JS代码再执行,代码执行完成后,即使函数中没有写return,在构造函数模式中:浏览器会默认的把创建的对象返回到函数外面
总结:
- 构造函数执行期间,既具备函数执行的一面,也同时具备自己独有的操作:在构造函数执行期间,浏览器会默认创建一个对象,这个对象就是当前这个构造函数(类)实例,函数执行完成后,浏览器会默认的把这个实例返回。所以new Fn()执行,Fn是一个类,返回的结果 就是Fn这个类的一个实例
# 构造函数执行后面的‘()’问题
TIP
构造函数执行如果不需要传递参数,函数后面的()可省略,如new Fn()
可写为new Fn
;
注意:
- 如果要在new Fn之后直接调用实例的方法,则必须要加小括号,即必须写成
new Fn().方法名
# 构造函数模式的返回值问题
构造函数返回值
构造函数模式中默认返回值是当前的实例,如果有return,返回分2种情况: 1、return 后面是一个基本数据类型的值,当前实例是不变的,例如return 100;我们的返回值还是当前类的实例; 2、return 后面是一个引用数据类型的值,当前实例会被返回的值给替换掉例如return {name:"世界"}我们的返回值就不再是当前类的实例了,而是对象 {name:"世界"};
# 原型链模式
TIP
基于构造函数模式的原型链模式解决了方法或者属性公有的问题,把实例之间公有的属性和方法写在当前类的prototype
属性上;
1、每一个函数数据类型都有一个天生自带的属性:prototype(原型),并且这个属性的属性值是一个对象数据类型的值(Function.prototype
是函数数据类型的,但是它没有prototype
属性(Function.prototype.prototype
是undefined
)。Array.prototype
是一个数组),浏览器默认为其开辟一个堆内存;
2、在浏览器给prototype
开辟的这个堆内存中,浏览器天生给它加了一个constructor
属性(构造函数),属性值是当前函数(类)本身;
3、每一个对象数据类型(普通对象、数组、正则、实例、protoype..)也天生自带一个属性:__proto__
,属性值指向当前实例所属类的原型(prototype
);
(IE中屏蔽了对象的__proto__
属性,但是确实有,只是不让我们使用而已)
4、Object是JS中所有对象数据类型的基类(最顶层的类);
# 原型链模式中的this
原型模式中的this分两种情况
1、 在类中this.xxx=xxx;this->当前类的实例 2、 原型链中提供的私有(公有)方法中的this问题: 总结: 看执行的时候"."前面是谁this就是谁。具体操作步骤如下
- 1、需要先确定this的指向(this)
- 2、把this替换成对应的的代码
- 3、按照原型链查找的机制,一步步的查找结果
# 如何往原型中批量添加属性和方法
重构原型
让某个构造函数的原型指向我们自己开辟的堆内存,但是自己开辟的堆内存当中是没有constructor
属性的,所以要往自己开辟的堆内存中添加constructor
属性,属性值为当前构造函数本身;
缺点: 重构原型后,会导致之前添加的属性和方法都没有了,只能使用重构之后添加的属性和方法;
注意:
- 不要忘了重构之后要添加
constructor
属性指向当前构造函数; - 内置类的原型不能重构,浏览器不允许我们这么做;
- 重构原型之前生成的实例的
__proto__
属性值依然指向重构之前的原型,而不是重构之后的原型,只有在重构原型之后生成的实例的__proto__
属性值才指向新的原型;
← 14、正则 16、面向对象的理解与继承 →