OOP(Object Oriented Programming):面向对象程序设计
POP(Procedure /prəˈsiːdʒər/ Oriented):面向过程程序设计
编程语言:JAVA PYTHON PHP C# C C++ RUBY GO NODE…
编程语言:面向对象/面向过程(除了C是面向过程的,其它都是面向对象的。)
标记语言:HTML/CSS
LESS/SASS/STYLUS…CSS预编译器:用面向对象的思想去编写CSS,最后把其生成浏览器可以识别的普通的CSS。
之所以将标记语言CSS用面向对象的预处理器来处理是因为面向对象的编程思想好处多多。
先来大概讲述下对象/类/实例:
对象–所有要研究和学习操作的都是研究对象;
类–我们根据需要把各种事物按照其特点划分成不同的类别,类别也不是具体的东西,只是一个范围,是对对象这个抽象的东西的具体化一些。
实例–类别中的具体事物。
什么是面向对象?
对象、类、实例
JS本身就是基于面向对象研发出来的编程语言:内置类、数据类型、DOM元素……
内置类:
【数据类型】
每一种数据类型都有一个自己对应的类别
- Number数字类,每一个数字(包括NaN\Infinity)都是它的一个实例
- String、Boolean、(Null、Undefined)、(Symbol)、BigInt、Object(Array、RegExp、Date…)、Function
(Null、Undefined):特殊性,有这两个类但是被浏览器保护起来,不能被操作
(Symbol):这个内置类能用,但是不能被new
对象的学习:先研究这个对象的一个具体实例,掌握这个具体实例后,对象的其他实例也是一样的,下面以数组为例来讲解如何学习JS中的内置类。
数组的学习
1.准备Array类的一个实例(一个具体的数组)
2.开始研究数组的结构、研究它的方法(一般都在Array.prototype原型上)
let arr = [10, 20, 30]
数组是对象,是一个堆,这个堆内存中属性名是从0开始依次递增的数字,有个length属性代表当前数组长度,当前这个数组实例的结构是这样的,其它的数组实例结构也是这样的。
JS创建的时候是先有类再有类的实例这样来创建的,所以学习的时候是先学习类的一个实例。
学习对象的时候先在控制台输出一下,这样就可以看到这个类所有的方法了。
真实开发的时候,光这些内置类是不够的,我们还需要自己构建一些类“自定义类”来完成功能的开发
构造函数和普通函数执行时的区别
创建函数并且把函数执行(每一个类都是函数数据类型的,包含内置类和自定义类)
typeof Array;//=>"function"
typeof HTMLDivElement;//=>"function"
//函数创建和执行
function func() {}
func();
自定义类(类名的第一个首字母一般都要大写)
function Func() {
}
Func();//把它当做普通函数执行(形成私有的上下文、作用域链、THIS[window]、ARGUMENTS、形参赋值、变量提升、代码执行...)
let f = Func(); //f获取的是函数的返回结果(函数没有RETURN,所以f=undefined)
let f = new Func(); //这是构造函数执行(当做类来执行,而不是当做普通函数了),此时Func被称为“类”,返回的结果(f)被称为“当前类的一个实例”,它是一个实例对象
console.log(f);
构造函数执行(类) 和 普通函数执行 的区别?
function Func(x, y) {
let num = x + y;
this.x = x;
this.y = y;
}
let f1 = Func(10, 20);
console.log(f1, window.x); //=>undefined 10
let f2 = new Func(10, 20);
console.log(f2); //=>{x:10,y:20} 当前类的实例对象
下面用图来讲解一下普通函数和构造函数执行的区别
构造函数在执行的时候在进行完初始化作用域链后会比普通函数执行多做一些事情:1.创建一个对象;2.让this指向这个对象;3.返回当前实例对象
构造函数执行的时候和普通函数一样也会形成私有的上下文和私有变量,也会初始化作用域链、初始化this···一步步执行:
1.与普通函数执行的区别是会在初始化作用域链后默认创建一个对象,这个对象就是当前创建的实例;
2.在初始化this的时候会让this指向当前这个实例对象,然后是正常的【形参赋值、变量提升、代码执行】,在代码执行过程中遇到的let\var\function跟普通的函数调用一样都是给当前这个私有上下文创建私有变量,只要this.xxx=xxx才是给当前这个实例对象创建私有的属性和方法,因为this就是当前的实例对象,所以this.xxx=xxx就是给当前的实例对象设置属性和方法。 像let这样创建的私有变量默认和当前这个实例对象没有必然的联系。
3.在函数没有返回值的情况下默认会把创建的实例对象返回,如果函数中自己写了return:如果返回基本类型值则还是返回实例为主;如果返回的是引用类型的值,自己编写的返回值会替换默认返回的实例。
回到代码示例中,f2是new的Func,所以在这个Func中有私有的变量x=10、y=20、num=30,这些变量与实例对象f2无关,只有this.xxx才与当前实例对象f2有关,【this.x = x;this.y = y;】给当前实例f2添加【x=10、y=20】这两个属性。构造函数Func没有写返回值,默认会把当前这个实例对象f2返回。
- 返回基本类型值,f2依然是创建的实例对象
function Func(x, y) {
// num只是当做普通函数执行的时候,给私有上下文中设置的私有变量,和实例对象没有关系,只有THIS是实例对象,所以THIS.XXX=XXX才和实例有关系
let num = x + y;
this.x = x;
this.y = y;
return num;
}
let f2 = new Func(10, 20);
console.log(f2);//Func {x: 10, y: 20}
console.log(f2 instanceof Func);//true
2.如果自己返回的就是一个引用值,一切以自己返回的为主,此时的f2={name:‘xxx’}而不再是当前类的实例了
function Func(x, y) {
let num = x + y;
this.x = x;
this.y = y;
return {
name: 'xxx'
};
}
let f2 = new Func(10, 20);
console.log(f2);//{name: "xxx"}
console.log(f2 instanceof Func);//false
因为有以上第2种情况,所以如何判断当前对象是否为某个类的实例?
基于 instanceof 可以检测当前对象是否为 某个类 的实例
语法:实例 instanceof 类,是的话返回TRUE,反之FALSE
每一个对象(包含实例对象)都有很多属性和方法,在自己堆内存中存储的都是私有的属性方法,基于__proto__原型链查找类prototype原型上的都是共有的属性方法
检测属性是当前对象私有的还是公有的:
- 对象.hasOwnProperty(属性) 检测是否为私有的属性(只有是私有属性才是TRUE,哪怕有这个属性,但是为公有属性结果也是FALSE)
- 属性 in 对象:检测是否为他的属性(不论是公有还是私有都可以)
console.log('x' in f2); //=>TRUE x是f2的属性
console.log(f2.hasOwnProperty('x')); //=>TRUE x是f2的私有属性
//属性的双引号必须带
f2.hasOwnProperty(x);//=>Uncaught ReferenceError: x is not defined
console.log('toString' in f2); //=>TRUE toString是f2的属性
console.log(f2.hasOwnProperty('toString')); //=>FALSE toString是他的公有属性
总结下构造函数和普通函数的区别:
如果一个函数通过new来执行那么这个函数就是类,返回的结果就是这个类的实例,构造函数的执行和普通函数一样都会形成执行上下文和自己的私有变量,也会初始化作用域链、初始化this、形参赋值、变量提升、代码执行···除此之外的区别是:1.在初始化作用域链后会创建一个默认的对象;2.在初始化this的时候将指向当前这个实例对象 3.构造函数中的this.xxx=xxx就是给当前这个实例对象添加私有的属性和方法,构造函数中用let/var/function创建的变量存储在构造函数的私有变量AO中,与实例对象没有直接的联系。4.构造函数没有返回值时默认返回的是当前创建的这个实例对象,如果有返回值,当返回值是基本数据类时返回的依然是当前这个实例对象,当返回值是引用数据类型时,返回的是这个引用数据类型,不再是当前这个实例了。