JS中的面向对象编程

JS中的面向对象编程

单利设计模式(Singleton Pattern)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
单利设计模式:
1. 表现形式
var obj = {
xxx: xxx,
...
}
在单利设计模型中,obj 不仅仅是对象名,他被成为“命名空间”,把描述事物的属性存放到命名空间中,多个命名空间是独立分开的,互不冲突。
2. 作用
把描述同一件事物的属性和特征进行分组、归类(存储在同一个堆内存空间中),因此避免了全局变量之间的冲突和污染
*/
var person1 = {
name: "zhangsan",
age: 18
}
var person2 = {
name: "lisi",
age: 20
}

/* 高级单例模式:
1. 在给命名空间复制的时候,不是直接赋值一个对象,而是先执行匿名函数,形成一个私有作用域(不销毁的栈内存);在这个私有作用域中创建一个堆内存,把堆内存的地址赋值给命名空间
2. 这种模式的好处:我们完全可以在这个私有作用域中创造很多内容(变量。函数……),那些需要供外面调取使用的,我们暴露到返回的对象中(模块化实现的一种思想)
*/
var nameSpace = (function (){
var n = 110;
function fn(){
// ...
}
return {
fn: fn
}
})();

高级单例模式的一道练习题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)
};
var fn = obj.fn;
fn(3); // 9
obj.fn(3); // 10
console.log(n, obj.n); // 8 6
// 代码执行过程见下图

面向对象编程

基于单例模式实现模块化开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
模块化开发:
1、团队协作开发的时候,会把产品按照功能板块进行划分,每一个功能板块有专人负责开发
2、把各个板块之间公用的部分进行提取封装,后期再想实现这些功能,直接调取引用即可(模块封装)
*/

// 例如使用模块化开发百度首页
// 公共方法
var utils = (function(){
return {
aa: function(){}
}
})();
// 小明负责开发换肤模块
var skipRender = (function(){
var fn = function(){
// ...
}
return {
init: function(){},
fn: fn
}
})();
skipRender.init();
// 小红负责开发天气模块
var weatherRender = (function(){
var fn = function(){
// ...
}
return {
init: function(){
fn(); // 调取自己模块中的方法直接调取使用即可
skipRender.fn(); // 调取别人模块中的方法(该方法需要暴露出来)
}
}
})();
weatherRender.init();

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
工厂模式(Factory Pattern)
1、把视线相同功能的代码进行“封装”,以此来实现“批量生产”(后期想要实现这个功能,我们只需要执行函数即可)
2、“低耦合高内聚”:减少页面中的冗余代码,提高代码的重复使用率
*/
function createPerson(name, age){
var obj = {};
obj.name = name;
obj.age = age;
return obj;
}

var p1 = createPerson('小明', 18);
var p2 = createPerson('小红', 20);

oop

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
JS是一门编程语言(具备编程思想)
[面向对象]
JS/JAVA/PHP/C#/C++/Ruby/Python...
[面向过程]
C
*/
/*
面向对象编程,需要我们掌握:“对象、类、实例” 的概念
对象:万物皆对象
类:对象的具体细分 (按照功能特点进行分类:大类、小类)
实例:类中具体的一个事物 (拿出类别中的一个具体实例进行研究,那么当前类别下的其他实例也具备这些特点和特征)
*/

1551869605301

创建值的两种方式以及区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
基于构造函数创建自定义类(constructor)
1、在普通函数执行的基础上 “new xxx()”,这样就不是普通函数执行了,而是构造函数执行,当前的函数名称之为 “类名”,接收的返回结果是当前类的一个实例
2、自己创建的类名最好第一个单词首字母大写
3、这种构造函数设计模式执行,主要用于组件、类库。插件、框架等的封装,平时编写业务逻辑一般不这样处理
*/
function Fn(){
// ...
}
var f = new Fn(); // Fn 是类, f 是类的一个实例


/*
JS中创建值有两种方式
1、字面量表达式
2、构造函数模式
*/
var obj = {}; // 字面量方式
var obj = new Object(); // 构造函数模式
// 不管是哪一种方式创造出来的都是 Object 类的实例,而实例之间是独立分开的,所以 var xxx = {} 这种模式就是 JS 中的单例模式。

// 基本数据类型基于两种不同的模式创建出来的值是不一样的
// 基于字面量方式创建出来的值是基本类型值; 基于构造函数创建出来的值是引用类型
var num1 = 120; // typeof num1: number
var num2 = new Number(120); // typeof num2: object

构造函数执行的机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Fn(name, age){
var n = 10;
this.name = name;
this.age = age + n;
}
// 普通函数执行
// 1、形成一个私有作用域 2、形参赋值 3、变量提升 4、代码执行 5、栈内存释放问题
Fn();

// 构造函数执行
// 1、像普通函数执行一样,形成一个私有的作用域(栈内存),-形参赋值 -变量提升
// 2、【构造函数执行独有】:在 JS 代码自上而下执行之前,首先在当前形成的私有栈中创建一个对象(创建一个堆内存:暂时不存储任何东西),并且让函数的执行主体(this)指向这个新的堆内存(this === 创建的对象)
// 3、代码自上而下执行
// 4、【构造函数执行独有】:代码执行完成,把之前创建的堆内存地址返回(浏览器默认返回)
// 也就是第二步创建的对象其实就是当前 Fn 这个类的一个实例,this 指向这个实例,代码执行中的 this.xxx = xxx 都是给实例设置“私有属性”,最后浏览器会把默认创建的实例返回,供外面接收。
var f = new Fn('xxx', 20);

1551869671975

构造函数中的一些细节问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
构造函数执行,不写 return,浏览器会默认返回创建类的实例,但是如果写了 return,有下面两种情况:
1、return 是一个基本类型值,返回的结果依然是类的实例,没有受到影响
2、如果返回的是引用值,则会把默认返回的结果覆盖,此时接收到的结果就不再是当前类的实例了

构造函数执行的时候,尽量减少 return 的使用,防止覆盖实例
*/
function Fn(){
var n = 10;
this.m = n;

// return '小明';
return {name: "小明"};
}
var f = new Fn();
// new Fn; 在构造函数执行的时候,如果 Fn 不需要传递实参,我们可以省略小括号,意思还是创建实例(和加小括号没有区别)

// instanceof:检测某一个实例是否隶属于这个类
console.log(f instanceof Fn); // ture
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

// hasOwnProperty:检测当前属性是否为对象的私有属性(不仅要有这个属性,而且必须还是私有的才行)
console.log(f.hasOwnProperty('m')); // true
console.log(f.hasOwnProperty('n')); // false 没有这个属性
console.log(f.hasOwnProperty('toString')); // false 有这个属性,但不是私有的

原型链和原型链的查找机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
原型(prototype)、原型链(__proto__)
【函数】:
普通函数、类(所有的类:内置类、自己创建的类)
【对象】:
普通对象、数组、正则、Math、arguments……
实例是对象类型的(除了基本类型的字面量创建的值);prototype 的值也是对象类型的;函数也是对象类型的
……


1、所有的函数数据类型都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给它开辟一个堆内存
2、在浏览器给 prototype 开辟的堆内存中有一个天生自带的属性:constructor,这个属性存储的值是当前函数本身
3、每一个对象都有一个 __proto__ 的属性,这个属性指向当前实例所属类的 prototype (如果不能确定它是谁的实例,都是 Object 的实例)
*/

1551865322692

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
原型链:
它是一种基于 __proto__ 向上查找的机制,当我们操作实例的某个属性或者方法的时候,首先找自己空间中的私有的属性或者方法
1、找到了,则结束查找,使用自己私有的即可
2、没有找到,则基于 __proto__ 找所属类的 prototype,如果找到了就用这个公有的,如果还没找到,基于原型的 __proto__ 继续向上查找,一直找到 Object.prototype 的原型为止,如果还没找到,操作的属性或者方法不存在
*/
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;

// 求下列输出结果
f1.n; // undefined
f1.AA === f2.AA; // false
f1.__proto__.AA === f2.__proto__.AA; // true
f1.__proto__.AA === Fn.prototype.AA; // true
f1.hasOwnProperty === Fn.prototype.hasOwnProperty; // true

f1.name = 'xxx'; // 给自己设置私有属性
f1.__proto__.name = 'xxx'; // 给原型上设置公有属性(每个实例都可以用这个属性) === Fn.prototype.name = 'xxx';

1551869717880

打赏功能
-------------本文结束感谢您的阅读-------------
0%