发表于: 2020-01-14 22:27:57
0 1344
今日完成的事
面向对象编程(oop)
什么叫做面向对象
对象 类 实例
在js中万物皆对象,类是对象的细分,实例:类中具体的事物。
以自然界来比喻,大自然中所有的一切存在的东西都可以叫做对象,js中存在的所有东西也都是对象,我们要学习研究的事物统称泛指都是对象,在大自然中按照功能特点会进行分类,比如分为动物类,植物类,动物类又分为猫科动物,犬科动物,爬行动物,哺乳动物,人类,人类有可以分成男人,女人。Js里面的类也是一样,按照特点划分。实例是某一类的具体东西。
比如说你,我,他就是男人或者女人这个类的具体东西,我家养的猫是猫科,动物园的狮子是猫科动物的具体东西。Js中的实例也是如此,他是某个类的具体表现。
Js中每一个数据值都是都是某一个类的实例,比如1是number这个类的实例,
这里注意一下null是Null这个类的实例,undefine是Undefined这个类的实例,正则是RegExp这个类的实例,object是整个js的基类。
每一个元素标签都有一个自己所属的大类,div所属的大类就是HTMLDivElement
HTMLDivElement这个类的父类是HTMLElement
,HTMLElement的父类是Element
,节点分为元素节点,文本节点,注释节点等,Element就是元素节点,Element的父类是Node,
Node是一个大类,Node下面又分为元素子类,文本子类,注释子类。Node的父类是EventTarget事件目标类
EventTarget的父类是object基类
。所有类最后又回到了object上,object是对象类,所以说万物皆对象。
每一个实例都可以调用所属类以及所属类的父类一直到基类的所有方法,就像你我他都有男人或者女人的一切特征,又有人类的一切特征,又有哺乳动物的一切特征。
Js当中把我们要学习研究的东西都分类给提供好了。而所谓的面向对象就是学习一个方法,首先想这个的方法是哪一个类的实例,在研究这个类,在研究他的父类一直研究到基类,然后在回归到实例本身上。
内置类不能满足我们所有的开发需求,所以还需要我们自己创建自定义类。
一个函数直接执行就是普通函数,但是用new执行就是创建自定义类,用new执行函数,此时函数不仅仅只是function的实例了,他自己也是一个类了。虽然是用new执行,但是函数也是执行,所以函数直接执行的步骤一个不能少,
New一个函数执行
想成一个上下文,
创建自己的AO,
创建ARGUMENTS,
形参赋值,
声明this指向,
初始化作用域链,
代码从上到下执行
但是用new执行多了几个步骤。
1. 在代码从上到下执行之前他会默认创建一个对象,而这个对象就是当前类的实例。
2. 会把this指向改变,让其指向新创建的实例。
3. 不论写不写return都会把新创建的实例返回,但是有一个特殊点,如果函数写了return,return的值如果是基本数据类型,那么返回的是创建的实例,如果返回的是引用类型值,那么则会把默认返回的实例给覆盖掉(此时返回的值就不在是类的实例了)。
New执行的时候既有普通函数的一面,也有类执行的一面,要把普通函数和类执行的结果分开。
内置New的实现原理
new fn与new fn()都是创建实例,区别在于第二种可以传参
构造函数
基于构造函数创建自定义类,创建自定义类的函数就是构造函数。
在普通函数执行的基础上以new xxx(),这样就不是普通函数执行,而是构造函数执行,当前的函数名称之为类名,接收的返回结果是当前的一个实例。
自己创建的类名最好第一个首字母大写
这种构造函数的设计模式执行,主要用于组件,类库,插件,框架等封装,平时写业务逻辑一般用不上
基本数据类型基于两种不同的模式创建出来的值是不一样的,基于字面量方式创建出来的值是基本数据类型。基于构造函数创建出来的是引用类型,创建值的2种方法,字面量和构造函数
构造函数执行
1形成私有作用域
2形参赋值,变量提升
1. 在当前形成的私有栈中形成的私有栈中创建一个对象(创建一个堆内存:暂时不存储任何东西),并且让函数中的执行主体this指向这个新的堆内存(this===创建的对象)。
2. 代码自上而下执行
3. 代码执行完成把之前创建的堆内存地址返回(浏览器默认返回)
开始创建的对象就是当前的一个实例,我们让this指向这个实例,代码执行的this.xxx=xxx都是给实例设置私有属性,最后浏览器会默认把创建的实例返回,供外面接收。
在执行一次构造函数会把上面的操作克隆一份,会形成新的实例(新的内存空间),所以说实例是分开的。
构造函数执行不写return浏览器会默认返回创建的实例,如果我们写了return,return是一个基本值,返回的结果依然是一个类的实例,没有受影响。
如果返回的是引用值,则会把默认的返回的实例覆盖,此时接收到的结果就不是当前的实例了,构造函数执行的时候,尽量减少return的使用,防止实例覆盖。
Newfn在构造函数执行的时候,如果fn不需要传递实参,我们可以省略小括号,意思还是创建实例
实例可以使用类的一些方法
原型和原型链
函数:普通函数,类(所有的类:内置类,自己创建的类)
对象:普通对象,数组,正则,math,实例是对象类型的,prototype的值也是对象类型的,函数也是对象类型的,万物皆对象。
1. 所有的函数数据类型(类)都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给他开辟一个堆内存。
2. 在浏览器给prototype开辟的堆内存当中有一个天生自带的属性:constructor(类的构造器),这个属性存储的值是当前函数(类)本身。
3. 每一个对象都有一个_poto_的属性,这个属性指向当前实例所属类的prototype。
4. 每一个类都把供实例调取的公共属性方法,存储到自己的原型上,(原型的prototype的作用就是存储一些公共的属性和方法,供他的实例调取使用)。
原型链:他是一种基于_proto_向上查找的机制,当我们操作实例的某个属性或方法的时候首先会在自己空间中私有的属性或者方法,没有找到则基于_proto_找所属类的原型,如果找到就用这个共有的,如果没找到,基于原型上的_proto_继续向上查找,一直找到Object.prototype的原型为止,如果没有,操作的属性或者方法不存在。函数的代码字符串跟函数的属性存在一个堆内存中。
Function这个类的原型是一个匿名函数
原型重定向:这里的构造函数原型重新定向了一个对象,这样原先原型对象里的方法都不能用了,只能用新定向的方法。
内置类的扩展方法和方法借用
假如一个数组调用push方法,
这个过程是arr基于原型链,找到Array.prototype上的push方法,在把找到的push方法执行,push中的this:arr,push的作用是给this末尾增加新值。每一个内置类有很多方法,但是类上面的方法不一定满足开发需求,比如数组的原型上就没有去重的方法我们就自己在类的原型上写一个方法,这个方法可以供所有此类的实例使用,这就是内置类的扩展。比如
在Array的prototype上写一个去重的方法,这样所有的数组都可以使用这个方法了
链式写法,上一个方法执行返回的结果依然是当前类的实例,所以一眼可以用当前类的方法,
比如
\
方法的借用,假如一个字符串想要调用一个数组的方法,秩序改变这个数组方法的this指向就行了
借用数组原型上的slice方法,实现把字符串(或者类数组)转化为数组,这就是方法的借用
评论