js知识点

1.常见的值和引用类型

值:

  1. undefine
  2. 字符串
  3. 数值
  4. bool
  5. symbol

引用:

  1. 对象
  2. 数组
  3. null

2.深拷贝与浅拷贝

对于一种问题,比如对象的引用

let obj1 = obj2

obj1.name = “aa”

那么,obj2的name也会因此改变,这就是浅拷贝

这两个对象实例都是指向的同一块内存空间,如果是深拷贝的话,就不会出现这种问题。深拷贝就相当于是把原对象通过递归的方式,将原对象原原本本的复制过去。这样,这个对象实例就变成了一个独立的个体,有着自己的内存空间。

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
function deepClone(obj={}){
if(typeof obj !== 'object' || obj == null){
// 如果传入的不是对象或者为空的话,那么就给他按原值返回
//因为后面需要递归进行调用,如果遇到值,就可以直接进行赋值
//如果是对象的话,还是需要继续递归调用,直到找到值为止
//为了防止出现对象套对象的情况,这样就可以一次性的把对象给遍历完
return obj;

}
let result
if(obj instanceof Array){
result = []
}else{
result = {}
}

for(let key in obj){
// 保证key不是原型的属性
if(obj.hasOwnProperty(key)){

// 不停的递归,直到找到值为止,只要是对象或者数组,就会一直递归
result[key] = deepClone(obj[key])
}
}
}

上述代码为深拷贝,主要的内容就是对对象中的对象进行递归遍历

3.parseInt

字符串转数字:parseInt

如果遇到字符串拼接问题

return 100+"10"返回的就是一个字符串

如果想要返回纯数字,那么就需要return 100+parseInt("10")

4.什么时候用== 和===

大部分情况下都可以用===

只有在判断空的时候可以用==

因为==会做一个强制的类型转换,可能会得到错误的结果

5.truly和falsely变量

image-20210816152733182

1
2
3
4
5
6
!!0 === false
!!NaN === false
!!'' === false
!!null ===false
!!undefined === false
!!false = false

只要两次取反结果为true的变量就是truly变量

==if语句()中判断的就是truely变量和falsely变量==

==逻辑判断同理==

6.instanceof类型判断

从字面意思可以看出,instanceof就是看前者是不是后者的一个实例

instanceof会根据原型链一层一层的往上找,如果找到了就会返回一个true

7.隐式原型和显式原型

image-20210816192506268

__proto__是隐式原型prototype是显式原型

8.原型链

image-20210816194040816

对于原型链,我是这么理解的:

xialuo作为student类的一个实例,他的隐式原型就指向student的显示原型,而Student类继承于父类People,所以student也有一个隐式原型去指向父类people的显示原型

9.手写jQuery

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
class jQuery {
constructor(selector){
const result = document.querySelector(selector)
const length = result.lenth;
for(let i = 0; i < length; i++){
this[i] = result[i]
}
this.length = length;
this.selector = selector
}
get(index){
return this[index]
}
each(fn){
for(let i = 0; i < this.length; i++){
const elem = this[i];
fn(elem)
}
}
on(type,fn){
return this.each(elem =>{
elem.addEventListener(type,fn,false)
})
}
}

其中each()中的fn就是回调函数

10.闭包

image-20210816232637465

对于闭包,对自由变量的查找,是从函数定义的地方开始的,然后向上级作用域一层一层的查找,直到window,而不是在执行的地方

11.property和attribute

image-20210817145854546

尽量使用property,这个引起dom渲染的可能性较小

12.转化为数组

image-20210817150706265

Array.prototype.slice.call(转化对象)

13.dom操作性能优化

image-20210817152344006

通过createDocumentFragment(),创建一个文档片段,插入到文档片段的dom元素并不会立即被渲染,会在后续appendChild中统一渲染

14.事件代理(基于冒泡)

image-20210817160748449

由于事件的冒泡行为,事件会向上进行传递

image-20210817161821832

15.跨域问题

1.jsonp方式

因为script是可以跨域的,所以,可以通过script便签去请求网址,通过这个去传数据

另外也可以通过ajax传递

image-20210817231043221

2.CORS通过服务器进行设置

image-20210817231222494通过服务端设置可信任的端口或者域名

16.手写ajax

image-20210817232633502

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function ajax(url){
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.open("GET",url,true);
xhr.onreadystatechange = function(){
if(xhr.readyState ===4){
if(xhr.status === 200){
resolve(
JSON.parse(xhr.responseText)
)
}else if(xhr.status ===404){
reject(new Error('404 not found'))
}
}
}
xhr.send(null)
})
return p;
}

17.本地存储cookie,localstorage,sessionstorage

不同点:

设置值不同

cookie:通过document.cookie=''设置,并且不会覆盖,会类似于push的方式跟在后面,最大存储4k,每次向服务器发送请求都会跟着进去。

localStorage和sessionStorage:

从字面意思就可以看出,sessionStorage是只存在于本次会话,当浏览器关闭时,就会消失。

而localStorage可以存储在本地

这两个最大可以存储5M,通过setItem设置,getItem获取

并且不会随着http请求发送出去

综上,一般用localStorage比较多

18.手写防抖

防抖就是防止一个事件多次被触发,比如输入事件,按下键,的事件会被不停的触发,所以需要做出防抖,比如延时一段时间后才去执行某个函数

1
2
3
4
5
6
7
8
9
10
11
12
function debounce(fn,delay = 500){
let timer = null;
return function (){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this,arguments)
timer = null
}, delay);
}
}

19.手写节流

对于类似拖拽事件,会触发很多事件,这时候就可以使用节流,每隔一段时间触发一次,而不是每动一下就触发一次,这样体感感觉不到,而且性能也有提升

20.多重解构

1
2
3
4
5
6
7
8
const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
}
}
}
1
const { classes: { stu: { name } }} = school

通过这种方式可以解构更加深层的属性

21.对属性访问的两种方式”. && []”

这两种方式都可以去的属性的值,但某些场景只能使用数组表示法才可以行得通

比如

1
2
3
function foo(portfolio, stockname, shares){
portfolio[stockname] = shares
}

在使用.操作符访问对象的属性的时候,属性名是通过标识符来表示的,标识符必须直接书写在js程序中,他们不是一种数据类型,不能被程序操作

上述文字是犀牛书的原文解释,例如上述的例子,stockname这个属性是对象通过[]也就是数组方式进行调用的,并不能通过.的形式进行调用。同时,如果你尝试用.进行调用,那么再编辑器上书写的时候,就会出现该参数未被使用的提示(vscode)。

通过这个例子可以简单的得出一个结论:通过.进行调用的是标识符,他不属于基本数据类型的一种,所以它不能通过函数进行动态的添加标识符,比如上述的例子中,属性名stockname是用户输入动态传入的,而标识符他是一个静态的,必须通过硬编码到程序中,也就是说需要我们去手动书写,而不是让代码去动态获取

所以最终的结论就是:==如果是需要通过函数或者程序动态进行操作对象属性名的,那么就只能使用[],数组方法去实现====如果是手动进行定义的对象属性名,那么[]数组方法和.调用都可以==

==一句话就是:用手写的用[]和.用代码写的用[]==,结束。

22.数组方法(改不改变原数组)

不改变原数组:map()