单例设计模式的理论模型
/*
* 单例设计模式(singleton pattern)
* 1.表现形式
* var obj = {
* xxx:xxx,
* ...
* };
* 在单例设计模型中,OBJ不仅仅是对象名,它被称为“命名空间[NameSpace]”,把描述事务的属性存放到命名空间中,多个命名空间是独立分开的,互不冲突
*
* 2.作用
* =>把描述同一件事务的属性和特征进行“分组、归类”(存储在同一个堆内存空间中),因此避免了全局变量之间的冲突和污染
* var pattern1={name:'xxx'}
* var pattern2={name:'xxx'}
*
* 3.单例设计模式命名的由来
* =>每一个命名空间都是JS中Object这个内置基类的实例,而实例之间是相互独立互不干扰的,所以我们称它为“单例:单独的实例”
*/
var name = "李清";
var age = 18;
var sex = "girl";
var name = "张三";
var age = 20;
var sex = "boy";
/*
* 高级单例模式
* 1.在给命名空间赋值的时候,不是直接赋值一个对象,而是先执行匿名函数,形成一个私有作用域AA(不销毁的栈内存),在AA中创建一个堆内存,把堆内存地址赋值给命名空间
*
* 2.这种模式的好处:我们完全可以在AA中创造很多内容(变量OR函数),哪些需要供外面调取使用的,我们暴露到返回的对象中(模块化实现的一种思想)
*/
var nameSpace = (function () {
var n = 12;
function fn() {
//...
}
function sum() {
}
return {
fn: fn,
sum: sum
}
})();
THIS
1.给当前元素的某个事件绑定方法, 当事件触发方法执行的时候,方法中的THIS是当前操作的元素对象
oBox.onclick=function(){
//=>this:oBox
}
2.普通函数执行,函数中的THIS取决于执行的主体,谁执行的,THIS就是谁(执行主体:方法执行,看方法名前面是否有“点”,有的话,点前面是谁this就是谁,没有this是window)
function fn(){//=>AAAFFF000
console.log(1);
}
var obj={
fn:fn //=>fn:AAAFFF000
};
//=>执行的是相同的方法(不同地方在于函数执行方法中的this是不一样的)
obj.fn();//=>this:obj
fn();//=>this:window
//=>自执行函数执行,方法中的this是window
~function(){
//=>this:window
}();
var n = 2;
var obj={
n:3,
fn:(function (n) {
n*=2;
this.n+=2;
var n=5;
return function (m) {
this.n*=2;
console.log(m + (++n));
}
})(n)//=>obj.n会报错
};
var fn = obj.fn;
fn(3);//=>9
obj.fn(3);//=>10
console.log(n, obj.n);//=>8 6
实战项目中的模块化
/*
* 模块化开发
* 1.团队协作开发的时候,会把产品按照功能板块进行划分,每一个功能板块有专人负责开发
* 2.把各个版块之间公用的部门进行提取封装,后期在想实现这些功能,直接的调取引用即可(模块封装)
*/
var utils=(function () {
return {
aa:function () {
}
}
})();
//=>成员1
var skipRender = (function () {
var fn = function () {
//...
};
//...
return {
init: function () {
},
fn:fn
}
})();
skipRender.init();
//=>成员2
var weatherRender = (function () {
var fn = function () {
};
return {
init: function () {
fn();//=>调取自己模块中的方法直接调取使用即可
skipRender.fn();//=>调取别人模块中的方法
}
}
})();
weatherRender.init();
工厂模式
/*
* 工厂模式(Factory Pattern)
* 1.把实现相同功能的代码进行“封装”,以此来实现“批量生产”(后期想要实现这个功能,我们只需要执行函数即可)
*
* 2.“低耦合高内聚”:减少页面中的冗余代码,提高代码的重复使用率
*/
function createPerson(name, age) {
var obj = {};
obj.name = name;
obj.age = age;
return obj;
}
var p1 = createPerson('xxx', 25);
var p2 = createPerson('xxx', 25);
面向对象编程(Object Oriented Programming)
/*
* JS是一门编程语言(具备编程思想)
* [面向对象]
* JS\JAVA\PHP\C#\Ruby\Python\C++...
*
* [面向过程]
* C
*
* 面向对象编程,需要我们掌握:“对象、类、实例” 的概念
* 对象:万物皆对象
* 类:对象的具体细分(按照功能特点进行分类:大类、小类)
* 实例:类中具体的一个事物(拿出类别中的具体一个实例进行研究,那么当前类别下的其它实例也具备这些特点和特征)
*
* 整个JS就是基于面向对象设计和开发出来的语言,我们学习和实战的时候也要按照面向对象的思想去体会和理解
*/
创建值的两种方式以及区别
/*
* 基于构造函数创建自定义类(constructor)
* 1.在普通函数执行的基础上“new xxx()”,这样就不是普通函数执行了,而是构造函数执行,当前的函数名称之为“类名”,接收的返回结果是当前类的一个实例
*
* 2.自己创建的类名,最好第一个单词首字母大写
*
* 3.这种构造函数设计模式执行,主要用于组件、类库、插件、框架等的封装,平时编写业务逻辑一般不这样处理
*/
function Fn() {
}
Fn();//=>普通函数执行
var f = new Fn();//=>Fn是类 f是类的一个实例
var f2 = new Fn();//=>f2也是Fn的一个实例,f2和f是独立分开的,互不影响*/
/*
* JS中创建值有两种方式
* 1.字面量表达式
* 2.构造函数模式
*/
// var obj = {};//=>字面量方式
// var obj = new Object();//=>构造函数模式
// //=>不管是哪一种方式创造出来的都是Object类的实例,而实例之间是独立分开的,所以 var xxx={} 这种模式就是JS中的单例模式
//=>基本数据类型基于两种不同的模式创建出来的值是不一样的
//> 基于字面量方式创建出来的值是基本类型值
//> 基于构造函数创建出来的值是引用类型
//->NUM2是数字类的实例,NUM1也是数字类的实例,它只是JS表达数字的方式之一,都可以使用数字类提供的属性和方法
var num1 = 12;
var num2 = new Number(12);
console.log(typeof num1);//=>"number"
console.log(typeof num2);//=>"object"
构造函数执行的机制
//=>普通函数执行
//1.形成一个私有的作用域
//2.形参赋值
//3.变量提升
//4.代码执行
//5.栈内存释放问题
function Fn(name, age) {
var n = 10;
this.name = name;
this.age = age + n;
}
//=>普通函数执行
Fn();
//=>构造函数执行
var f1 = new Fn('xxx', 20);
var f2 = new Fn('aaa', 20);
console.log(f1 === f2);//=>false:两个不同的实例(两个不同的堆内存地址)
console.log(f1.age);//=>30
console.log(f2.name);//=>'aaa'
console.log("name" in f1);//=>true name&age在两个不同的实例都有存储,但是都是每个实例自己私有的属性
console.log(f1.n);//=>undefined 只有this.xxx=xxx的才和实例有关系,n是私有作用域中的一个私有变量而已(this是当前类的实例)
* 构造函数执行,不写RETURN,浏览器会默认返回创建的实例,但是如果我们自己写了RETURN?
* 1.return是的一个基本值,返回的结果依然是类的实例,没有受到影响
* 2.如果返回的是引用值,则会把默认返回的实例覆盖,此时接收到的结果就不在是当前类的实例了
*
* =>构造函数执行的时候,尽量减少RETURN的使用,防止覆盖实例
function Fn() {
var n = 10;
this.m = n;
// return;//=>这样RETURN是结束代码执行的作用,并且不会覆盖返回的实例
// console.log(1);
}
var f = new Fn();//=>new Fn; 在构造函数执行的时候,如果Fn不需要传递实参,我们可以省略小括号,意思还是创建实例(和加小括号没有区别)
console.log(f);
检测方法扩展
//=>instanceof:检测某一个实例是否隶属于这个类
// console.log(f instanceof Fn);//=>TRUE
// console.log(f instanceof Array);//=>FALSE
// console.log(f instanceof Object);//=>TRUE (万物皆对象:所有的对象,包含创建的实例都是Object的实例)
//=>in:检测当前对象是否存在某个属性(不管当前这个属性是对象的私有属性还是公有属性,只要有结果就是TRUE)
// console.log('m' in f);//=>TRUE
// console.log('n' in f);//=>FALSE
// console.log('toString' in f);//=>TRUE toString是它的公有属性
//=>hasOwnProperty:检测当前属性是否为对象的私有属性(不仅要有这个属性,而且必须还是私有的才可以)
// console.log(f.hasOwnProperty('m'));//=>TRUE
// console.log(f.hasOwnProperty('n'));//=>FALSE 连这个属性都没有
// console.log(f.hasOwnProperty('toString'));//=>FALSE 虽然有这个属性但是不是私有的属性
//=>思考题:编写一个方法hasPubProperty,检测当前属性是否为对象的公有属性,和hasOwnProperty对应
function hasPubProperty(obj, attr) {
//=>OBJ:要检测的对象
//=>ATTR:要检测的属性
//...
}
hasPubProperty(f, 'm');//=>FALSE
hasPubProperty(f, 'n');//=>FALSE
hasPubProperty(f, 'toString');//=>TRUE
原型链和原型链的查找机制
* 原型(prototype)、原型链(__proto__)
*
* [函数]
* 普通函数、类(所有的类:内置类、自己创建的类)
*
* [对象]
* 普通对象、数组、正则、Math、arguments...
* 实例是对象类型的(除了基本类型的字面量创建的值)
* prototype的值也是对象类型的
* 函数也是对象类型的
* ...
* 1.所有的函数数据类型都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给它开辟一个堆内存
* 2.在浏览器给prototype开辟的堆内存中有一个天生自带的属性:constructor,这个属性存储的值是当前函数本身
* 3.每一个对象都有一个__proto__的属性,这个属性指向当前实例所属类的prototype(如果不能确定它是谁的实例,都是Object的实例)
function Fn() {
var n = 100;
this.AA = function () {
console.log(`AA[私]`);
};
this.BB = function () {
console.log(`BB[私]`);
};
}
Fn.prototype.AA = function () {
console.log(`AA[公]`);
};
var f1 = new Fn;
var f2 = new Fn;
console.log(f1.n);//=>undefined