javascript review
es6的class
定义
通过class
来定义一个类,这个与java
中很相似
1 | class Person { |
继承
通过extends
关键字来继承一个一个类,语法如下
1 | class Person { |
这里实现了Student
类对Person
类的继承,Student
类原型链的父级是Person
,其方法可以在原型链上找到。
类的静态方法与私有属性、方法
- 类的静态方法只能通过类进行调用,而不能通过其实例对象进行调用,在类的内部也是可以调用的。其定义的语法如下
1 | class Person { |
- 类的私有方法,私有属性只能在类当中使用,用
#
定义,例子如下
1 | class Person { |
在实现过程中,是没有办法通过类或者类的实例化对象进行私有方法或者私有属性的调用,私有的只能在类的内部进行调用。
寄生组合式继承
通过构造函数继承父类的属性,通过原型链继承父类的方法
1 | // 定义父类 |
fetch
get请求
fetch
用于发起网络请求,和项目中常用的axios
相比,这个功能相对来说比较少一点,但是比较方便,因为是一个浏览器内置的api
1 |
|
这里定义了一个按钮,并且给它绑定了点击事件,回调函数是一个异步函数
提交formdata类型的数据
tips:提交formdata的时候会默认设置请求头
1 | <input type="file" class="upload"> |
提交json
使用fetch
提交json
数据的时候,得自己指定请求头,告诉服务器请求中body
的数据类型
1 | <h3>提交json类型的数据</h3> |
基本数据类型与引用数据类型
基本数据类型
string
字符串number
数字型:整数浮点数、NANundefined
未定义类型:数据未定义null
空类型Symbol
唯一类型(es6引入):表示数据是唯一的boolean
布尔类型:表示true 或者false
引用数据类型
引用数据类型是一个大的类,Object
,里面包含基本对象、数组、方法、日期、Map
对象、Set
对象等。
基本数据类型与引用数据类型的区别
基本数据类型所占内存空间的大小是确定的,所以简单数据类型直接存储在内存栈当中,这个栈有先进后出的特点,一般的,当这个基本数据不再被使用的时候,会被js的垃圾回收机制自动回收掉。
引用数据类型大小是不固定的,可以联想到数组,可以动态的对数组进行CRUD
,所以引用数据类型一般存储在内存堆当中,而声明的变量仅仅是指向这个实例的一个引用地址,这个声明的变量是存储在内存栈当中的。当访问一个引用数据类型的时候,会先到内存栈中寻找对应的引用地址,再根据这个引用地址找到内存堆中对应的实例。
赋值运算符
当给基本数据类型进行一个复制操作的时候,会直接进行一个数据的拷贝并且存储。会开辟一个新的内存空间,将拷贝的数据放到内存栈当中。
同样的,当将一个对象的引用赋值给另一个变量的时候,也会开辟一个新的内存空间,将存储对象的引用放到内存栈当中,他不会在内存堆中开辟新的内存空间,还是指向原来的对象,因为是同一个引用
代码
1 | const person1 = { |
浅拷贝
理解:
- 直接赋值是直接将内存栈中的该对象的引用地址赋给了新的变量,当使用这个新的引用修改对象的属性的时候,会影响到之前指向这个对象的引用
- 首先,如果对象不存在嵌套属性和方法的情况下,浅拷贝是不会产生影响的,如果存在嵌套属性或者方法,实际上浅拷贝还是拷贝的引用地址。
对于一个对象,也就是引用类型进行拷贝的时候,很容易的想到一个最基本的拷贝方法,赋值运算符,比如说现在有一个对象obj
,我们可以通过es6的展开运算符对其进行拷贝const objCopy = {...obj}
1 | const obj = { |
控制台输出的会是false
,因为这样拷贝相当于创建了一个新的内存空间。
然后也可以使用Object.assign(拷贝对象,被拷贝对象)
这个api
进行浅拷贝
1 | const obj = { |
深拷贝
通过递归来实现深拷贝
- 深拷贝实现对一个对象的拷贝,修改对象的属性而不会对原对象产生影响
- 使用递归的方式进行拷贝
- 先数组、后对象
1 | const obj = { |
lodash/cloneDeep
1 | <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script> |
JSON的API实现
这个应该是最常用的一个方法了,而且也是最简单的方法
1 | const obj = { |
数组原型方法
Array.prototype.concat()
这个方法用于合并多个数组,并且不会改变原数组,而是返回一个新的数组
1 | const arr1 = [1, 2, 3, 4] |
注意:如果不传递任何参数的话,会返回这个数组对象(arr1
)的浅拷贝
Array.prototype.every()
every
方法接收两个参数,一个是回调函数callbackFn
,另一个是thisArg
,当调用every
方法的时候,会对数组中每一个元素使用这个回调函数进行检查,一旦有一个元素不通过检查,也就是回调函数没有返回true
,就会停止执行,返回一个false
,第二个参数thisArg
是可选的,可以用来指定回调函数中this
的指向。
1 | const person = { |
注意:回调函数一定不能是箭头函数,如果是箭头函数这里会指向window
,也就是全局对象
Array.prototype.filter()
filter
方法接收两个参数,一个是回调函数callbackFn
,一个是thisArg
(指定回调函数中this
的指向),当调用filter
方法的时候,会对数组中每一个元素使用这个回调函数进行过滤,如果通过了过滤,则将这个元素的浅拷贝放入新的list
当中,如果都没有通过过滤,filter
方法会返回一个空的list
。
注意浅拷贝这个很重要,在vue3
里面,对一个响应式数据使用filter
方法的时候,操作不当的话响应式的特性会丢失。
1 | const person = { |
Array.prototype.find()
find
方法也是接收两个参数,一个是callbackFn
回调函数,一个thisArg
,指定回调函数中this
的指向,当调用find
方法的时候,会为数组中的元素依次执行回调函数,find方法会匹配第一个符合条件的元素,并且返回一个真值(即所有除 false
、0
、-0
、0n
、""
、null
、undefined
和 NaN
以外的皆为真值),如果没有匹配的,将会返回一个undefined
比如说,要查找一个对象数组中name是’clesbit’的对象
1 | const inventory = [ |
一个值得注意的事情,看下面的代码
1 | const array1 = [5, , , 12, 8, 130, 44] |
最后输出的是12
,不是true
Array.prototype.findIndex()
返回匹配的第一个元素的索引(index
),如果没有匹配的则返回-1
,工作原理和find
是一样的
Array.prototype.flat()
数组解嵌套,并且删除空槽
1 | const arr = [1, 1, , , ['666', 'cles', '', 'bit']] |
Array.prototype.forEach()
forEach
方法是平时最常用的一个遍历数组的方法,接收两个参数,一个是回调函数callbackFn
,另一个还是thisArg
,它总是返回一个undefined
,这说明了forEach
方法不支持链式调用
还有一些比较值得注意的地方:
- 除非抛出异常,没有办法中断forEach循环
forEach
期待的回调函数应该是一个同步函数,它不会等待Promise
的兑现,所以在使用Promise
(或者异步函数)作为forEach
的回调的时候,要格外的注意这一点
1 | const ratings = [5, 4, 5] |
Array.prototype.includes()
includes
方法是用于判断一个数组是否包含一个指定的值,如果包含就返回true
,不包含就返回false
1 | const pets = ['cat', 'dog', 'bat'] |
Array.prototype.indexOf()
学习到这里,我有点犯迷糊,这个和之前findIndex
方法有什么区别,前不久才整理复习的。
首先findIndex
接收的参数和indexOf
不一样,findIndex
接收的是一个回调函数,返回符合条件的元素的下标,indexOf
接收的是一个元素值作为参数,相同的地方是,如果都没有元素满足条件,返回的都是-1
而且indexOf
会跳过数组中的空位,findIndex
不会0.
1 | const arr = [1, 2, 3, , 5] |
Array.prototype.join()
join方法会将数组中每一个元素连接成一个字符串,并且返回这个字符串,可以指定分隔符,默认是’,
‘
可以在join([指定分隔符])
需要注意的是,join会把数组中的空槽视为undefined,并且也会产生一个分隔符
1 | console.log([1, , 3].join()); // '1,,3' |
Array.prototype.map()
map方法也是一个很常用的遍历数组的方法,它可以接受两个参数,一个是回调函数callbackFn
,一个是thisArg
,返回值是一个新的数组,如果数组中存在空槽,循环过程中会忽略空槽,不会触发回调
1 | /** |
Array.prototype.pop()
pop
方法不接受任何参数,它的作用是删除数组的最后一个元素(此方法会改变数组的长度),并且将其返回,如果数组为空则会返回undefined
,值得注意的是,pop方法不会跳过空槽,遇到空槽返回的也是undefined
1 | const arr = [18, false, 1, ,] |
Array.prototype.push()
push
方法接受1
个到n
个参数,并且会将传入的参数添加到原数组末尾(此方法会改变原数组的长度),并且返回数组长度的值。值得注意的是,push
也不会跳过空槽。
1 | const arr = [18, false, , 1, ,] |
Array.prototype.reduce()
reduce方法接受两个参数,第一个参数是回调函数callbackFn,第二个参数是计算的初始值,如果不传,reduce会将数组的第1
个元素作为初始值。
一个比较简单的用法,累加器
1 | const arr = [1, 2, 3, 4, 5, 6] |
它是这么运行的,这里指定了第2个参数的初始值是0
,所以第一次迭代的时候sum是0,current是1,返回值是1,每次迭代的返回值会作为第二次迭代时sum的初始值,所以第二次迭代的时候sum是1,current是2,返回值是3……..
一个比较值得注意的点:回调函数中this
的指向,在非严格模式下,回调函数中的this
是指向全局对象的,在浏览器中也就是window
,严格模式下始终指向undefined
阶段性学习总结
慢慢学习到这里,这篇文档已经有5000字了,突然想起了前些天面试官问我的find
、map
、filter
方法的区别,我想现在已经能说的清清楚楚了。
- 先来说说他们三个相同的地方,三个方法都可以接受两个参数,第一个参数是回调函数,第二个是一个可选参数,thisArg,用于指定回调函数中this的值
- 再说说不同点,他们的返回值是不一样的。find方法会返回数组中第一个符合条件的元素,如果没有符合条件的,则会返回undefined,map方法会对数组中每一个元素都执行一次回调函数,返回的值由回调函数来决定,并且会创建一个新的数组。新的数组长度是和原数组长度是一致的。filter方法也会创建一个新的数组,也会对每一个数组元素执行一次回调函数,符合条件的元素将会(
浅拷贝形式)保存到新的数组当中,如果没有元素符合,则会返回一个空的数组
而且也能回答浅拷贝和深拷贝的区别了
涉及到浅拷贝和深拷贝,就得理解js中的数据类型是怎么存储的,js中有基本数据类型和引用数据类型,其中基本数据类型是因为所占空间是固定的,存储再内存栈当中,引用数据类型因为其所占空间大小是动态变化的,所以存储再内存堆当中。当我们对一个对象进行浅拷贝的时候,可以使用Object.assign([newObject],[oldObject])
这个内置的api,还有展开运算符。浅拷贝的特点是仅仅是拷贝被拷贝对象的第一层属性,如果背拷贝对象的某个属性是引用数据类型的话,实际上还是拷贝的这个属性的引用地址,当我们修改这个属性的时候,旧的对象会被一同影响。所以要想真真正正完全拷贝一个对象有以下三种方式:
- 使用循环递归
- 第三方模块lodash的deepClone方法
- 最后一个就是JSON.parse(JSON.stringify(被拷贝对象)),但是这个解决方案存在一定的局限性,比如说如果该对象存在方法这种引用类型。所以还需要根据实际情况去选择合适的拷贝方法