在javascript角度看面向对象
面向对象OOP理论基础

概述
1.语言基础
(1)标记语言
- html
- css
- ...
(2)编程语言
-
POP(
Procedure Oriented)面向过程程序设计:- C语言
- ...
-
OOP(
object Oriented Programming)面向对象程序设计:- java 编程语言之父
- php
- C# (ASP.NET)
- ASP
- Ruby
- Go
- Python
- C++
- javascript
- ......
2.面向对象的重要性
(1)函数
无法实现变量私有化
(2)闭包
实现变量私有化,但是无法实现公有化
(3)面向对象
同一个程序代码,既有私有的也有公有的方法; 这套代码可以供多个程序使用,而每个程序之间既能使用同一个方法,而且还能保证一些值的私有化
所以面向对象的需求 : 私有化+公有化 的合理管控
3.面向对象程序设计中的三大概念
(1) 对象
泛指,JS中我们操作和研究的事务都是对象 万物皆对象
(2) 类
把抽象的对象,按照功能特点,进行的详细划分,划分成为很多的类别 对'对象'的归类和细分
(3) 实例
每一种类别中具体的事务 类中的具体成员
构造函数中的new xx() 创建的实例,一般来说都是对象 ,有特殊性
一、对象
有句话说的好:面向对象编程的世界里“万物皆对象”;
就好像我们的大自然一样,我们画图了解一下

对象就像是我们上图中的大自然,这里面的东西是在是太太多了...,我们就先了解一下就好;
二、内置类
根据JS中事务的特征,进行类别划分,而默认划分好的类别,就是内置类

(1)常见内置类
- -1).数据类型中的内置类
Number类:每一个数字都是这个类的一个实例String类Boolean类Null类 /Undefined类(浏览器中禁止我们自己访问这两个类)Symbol类Object对象数据类型类:所有的对象数据类型都是这个类的一个实例Object{}普通对象就是它的实例Array数组类 每一个数组是Array类的实例也是Object类的实例RegExp正则类Date日期类- ...
Function类:所有的函数都是这个类的实例
- -2).
DOM元素对象或者DOM元素集合也都有自己的内置类- ...
Node节点类Element元素标签类HTMLElementHTML元素标签类HTMLDivElement每一个DIV元素对象是它的一个实例HTMLAnchorElement每个A元素对象是它的一个实例- 每一个元素标签对象都有一个自己对应的内置类
XMLElement- ...
Text文本节点类Document文档节点类HTMLDocument每一个document都是它的一个实例
- ...
HTMLCollection元素集合类:基于getElementsByTagName等方法获取到的元素集合都是它的一个实例NodeList节点集合类
- ......
JS本身就是按照“面向对象思想(类和实例)”设计出来的语言,所以我们不论是研究和学习JS,还是在工作中进行项目开发(组件封装),也都应该按照面向对象的思想来学习和开发,例如:我想研究数组
(2) 以研究数组为例研究内置类
- 1.每一个数组都是
Array这个类的实例,每个实例都是单独的一个个体(互相是不冲突的,类似于没有一模一样的两个人,在JS中也基本上没有完全一样的两个实例[有特殊情况]) - 2.我们拿出某一个实例去分析其特点和结构,那么
Array的其它实例也具备相同的特点和结构 - 3.数组中有很多浏览器内置的方法,这些方法都是类提供给实例的,每一个实例都能具备和使用这些方法(前提是当前类的实例才能使用,如果不是它的实例,则不能使用)
let arr1=[12,23];
let arr2=[12,23];
console.log(arr1===arr2); //=>false 不同的堆内存地址
// push是Array这个类提供的方法,只要是Array类的实例(数组是它的实例)都可以调用这些方法
let arr = [10, 20];
arr.push(30);
console.log(arr); //=>[10,20,30]
// ARGUMENTS不是Array的实例,不能调它提供的方法
function func() {
// ARGUMENTS实参集合中存储了传递的实参信息,它是一个类数组(类似于数组的结构,但是它不是数组,不是Array类的实例)
arguments.push(30); //=>Uncaught TypeError: arguments.push is not a function
console.log(arguments);
}
func(10, 20);
三、自定义类
构造函数模式创建类
(1)构造函数定义
- “构造”:就是通过某种手段或者方法,创建出来 js中用**new xxx构造函数 **才能创建实例;
- “函数”:就是我们之前学过的函数
构造函数是指 可以通过某种手段或者方法【new xxx构造函数】创造一个实例 的函数
(2)构造函数创建
构造函数也是一个函数,与普通函数的创建没差别
function Fn(x, y) {
let total = x + y,
flag = false;
this.total = total;
this.say = function say() {
console.log(this.total);
};
}
(3)构造函数的执行
// 普通函数执行
let result = Fn(10, 20);
console.log(result);
// “new”:构造函数执行 “标准面向对象的编程->构建自定义类”
// + Fn被称为类「构造函数」
// + result被称为当前类的一个实例
let result1 = new Fn(10, 20);
普通函数执行和构造函数执行区别

- 构造函数会默认创建一个空对象【实例对象】
- 构造函数的this会指向创建的实例对象,函数体中的this.xxx=xxx操作,都是给实例对象设置私有属性/方法 【当前执行上下文中的私有遍历和实例对象没有直接的关系】
- 返回值不同:如果函数不设置返回值或者返回值是原始值类型,则返回的结果默认是创建的实例对象。只有手动返回对象类型,才会改变返回值
四、检测实例的几种方法
1、instanceof:检测当前实例是否属于这个类
数据类型检测的方法
instanceof: 检测当前实例是否属于这个类(或者检测当前值是否为某个类的实例)
-
语法: 值
instanceof类 -
返回值: 是它的实例返回
TRUE,反之返回FALSEfunction Fn() {} let f1 = new Fn; console.log(f1 instanceof Fn); //=>TRUE console.log(f1 instanceof Array); //=>FALSE -
局限:
instanceof不适用于字面量创建的基本数据类型检测,要求检测的实例必须是对象数据类型的console.log(100 instanceof Number); //=>FALSE -
应用场景:区分对象中的特殊数据格式,例如数组或者正则
let arr = [10, 20]; let reg = /^$/; // console.log(typeof arr); //=>"object" typeof不能具体检测对象类型的细分 // console.log(typeof reg); //=>"object" console.log(arr instanceof Array); //=>TRUE console.log(arr instanceof RegExp); //=>FALSE
2、hasOwnProperty:检测当前属性是否为实例私有属性
hasOwnProperty:检测当前的某一个属性是否为实例(或者对象)的私有属性
- 语法:
对象.hasOwnProperty(属性) - 返回值:是私有的属性返回
TRUE,如果不是对象的属性或者不是私有的属性都返回FALSE
3、in检测当前属性是否为对象的属性
in:检测当前属性是否为对象的属性
-
语法:属性
in对象 -
返回值:只要是对象的属性(不管是公有还是私有的属性)结果都是
TRUEfunction Fn() { // 构造函数体中出现的 this.xxx = xxx 都是给当前实例设置的私有属性 this.x = 100; this.y = 200; this.say = function () { console.log(x + y); }; } let f1 = new Fn; let f2 = new Fn; console.log(f1.say === f2.say); //=>false console.log(f1.hasOwnProperty('say')); //=>true console.log(f1.hasOwnProperty('name')); //=>false 因为你连这个属性都没有 console.log(f1.toString()); //=>"[object Object]" toString一定是f1对象的属性,否则f1也不可能调用这个方法 console.log(f1.hasOwnProperty('toString')); //=>false toString不是它的私有属性,是他的公有属性 console.log('say' in f1); //=>true console.log('toString' in f1); //=>true console.log('name' in f1); //=>false
for in内置的缺陷
-
不能迭代Symbol属性
Object.getOwnPropertySymbols(obj) //获取对象所有Symbol的私有属性「返回数组」 Object.keys(obj) || Object.getOwnPropertyNames(obj) //获取对象所有非Symbol的私有属性「返回数组」 -
迭代的时候不一定按照自己编写的键值对顺序迭代「优先迭代数字属性{小->大},再去迭代非数字属性{自己编写顺序}」
Object.prototype.sum = function sum() {}; let obj = { name: 'zhufeng', age: 12, 0: 100, 1: 200, teacher: 'zhouxiaotian', [Symbol('AA')]: 300, }; for (let key in obj) { console.log(key); //'0' '1' 'name' 'age' 'teacher' 'sum' } -
不仅会迭代对象的私有属性,对于一些自己扩展的公有属性也会迭代到「迭代可枚举的{一般设定的都是可枚举的,内置的是不可枚举的}」
//优化for in 【只迭代私有,不迭代共有】 // 只是不想迭代公有的「即使内部机制找到了,我们也不让其做任何的处理」 for (let key in obj) { // 先找所有私有,一但发现这个是公有的,说明私有的都找完了「不含Symbol」 if (!obj.hasOwnProperty(key)) break; console.log(key); }
利用上面两个方法我们可以自己写一个检测某一个属性是否为当前对象的公共属性的方法
需求:检测某一个属性是否为当前对象的公共属性(是他的属性,但是还不是私有的)
function Fn(x, y) {
let total = x + y,
flag = false;
this.total = total;
this.say = function say() {
console.log(this.total);
};
}
let result1 = new Fn(10, 20);
let result2 = new Fn(30, 40);
//方法一:
function hasPubProperty(obj, attr) {
// 思路:基于in检测结果是TRUE「是它的一个属性」 & 基于hasOwnProperty检测结果是false「不是私有的」,那么一定是公有的属性
return (attr in obj) && !obj.hasOwnProperty(attr);
}
console.log(hasPubProperty(result1, 'hasOwnProperty')); //->true
console.log(hasPubProperty(result1, 'say')); //->false
// 弊端:只能是这种情况“某个属性不是私有的而是公有的”,但是如果这个属性 私有中也有&&公有中也有 ,基于我们的这个方法结果是false,但是这个属性确实也是他的公有属性,如何处理?

浙公网安备 33010602011771号