前端js常见知识点

158 阅读7分钟

一、js

1.call apply bind区别

(1)相同点:

  • 都是改变this指向的;
  • 第一个参数都是this要指向的对象;
  • 都可以利用后续参数传参;

(2)区别:

  • call和bind的参数是依次传参,一一对应的;
  • 但apply只有两个参数,第二个参数为数组;
  • call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数;
var a ={
    name:'一一',
    age:'22',
    sex:'女',
    hobby:'写代码'
    say:function(sex,hobby) {         
        console.log(this.name,this.age,sex,hobby)      
    }
}
var b = {
    name:'二二',
    age:'23',
}
a.say.call(b,'男','学习');
a.say.apply(b,['男','学习'])
bind可以向cally一样传参:
例如:
a.say.bind(b,'男','学习')();
但由于bind返回的仍然是一个函数,所以我们还可以在调用的时候再进行传参。
例如:
a.say.bind(b)('男','学习');
//结果为:"二二","23","男","学习"

2.原型和原型链及其作用

原型: JS声明构造函数(用来实例化对象的函数)时,会在内存中创建一个对应的对象,这个对象就是原函数的原型。构造函数默认有一个prototype属性,prototype的值指向函数的原型。同时原型中也有一个constructor属性,constructor的值指向函数对象。 通过构造函数实例化出来的对象,并不具有prototype属性,其默认有一个__proto__属性,__proto__的值指向构造函数的原型。在原型对象上添加或修改的属性,在所有实例化出的对象上都可共享。

原型的作用:
1.数据共享 节约内存内存空间
2.实现继承

原型链: 当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。如下图所示好理解(根据代码参考下图)

原型链的作用: 查找对象的属性(方法)

3.js继承方式

js的五种继承方式

4.数组和对象的深拷贝和浅拷贝

4.1对象浅拷贝

重点:对象和数组的的浅拷贝是一样的,如果元素是基本数据类型拷贝的是值,是引用类型拷贝的是地址

4.1.1 Object.assgin()方法

//将所有可以枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象
Object.assgin()
let obj1 = {
  a : 1,
  b : {
       c : 2,
   },
   d: [3,4]
}

let obj = Object.assign({},obj1)

//修改obj里属性值为引用类型的值
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5

console.log("obj",obj) 
console.log("obj1",obj1)

我们发现除了基本数据类型不变之外,引用类型的值都改变了,所以object.assgin()是浅拷贝,只解决了第一层.

image.png

4.1.2 对象展开运算符方法

let obj1 = {
        a : 1,
        b : {
            c : 2,
        },
        d: [3,4]
    }

    let obj = {...obj1}

    //修改obj里属性值为引用类型的值
    obj.a = 2
    obj.b.c = 3
    obj.d[0] = 4
    obj.d[1] = 5

    console.log("obj",obj) 
    console.log("obj1",obj1)

结果与上面是一样的: image.png

4.2 数组的浅拷贝

4.2.1 slice()方法

let arrTest = [1,2,3,{a: 4},[5,6]]
    
//数组的浅拷贝
let arr = arrTest.slice(0) 
//slice()方法 第一个参数是开始的位置 第二个参数是结束的位置
// 不会影响原数组,返回新的截取数组
arr[1] = 3
arr[3].a = 5
arr[4][0] = 6

console.log("arrTest",arrTest)
console.log("arr",arr)

数组的slice拷贝遇到的问题与对象一致

image.png

4.2.2 concat()方法

let arrTest = [1,2,3,{a: 4},[5,6]]
    
//数组的浅拷贝
let arr = [].concat(arrTest)

arr[1] = 3
arr[3].a = 5
arr[4][0] = 6

console.log("arrTest",arrTest)
console.log("arr",arr)

得到的结果与上面一样,这里很多人会问是不是可以用数组的展开运算符,来拷贝数组,这种方法也是可行的.

//数组的展开运算符
let arrTest = [1,2,3,{a: 4},[5,6]]
let arr = [...arrTest]

image.png

4.3 实现浅拷贝

//判断是数组还是对象
function isObjectOrisArray(data) {
    if( Object.prototype.toString.call(data) == '[object Object]' ) {
        return 'object'
    } else if( Object.prototype.toString.call(data) == "[object Array]" ) {
        return 'array'
    }else {
        return false
    }

}

//浅拷贝实现
function cloneShallow(source) {

    //判断数据既不是是数组又不是对象
    if(!isObjectOrisArray(source)) return 

    //判断数据是数组还是对象
    let target = isObjectOrisArray(source) == 'object'? {} : []

    for(let key in source) {
        //避免遍历从原型链到继承下来的可遍历的属性
        if(Object.prototype.hasOwnProperty.call(source,key)) {
            target[key] = source[key]
        }
    }

    return target
}

测试数据:

Object.prototype.method=function(){
   console.log(this);
 }
 let sourceObj = {
     a: 1,
     b: 2,
     c: {
         d: 4
     }
 } 
 sourceObj.__proto__.f = 1
 let targetObj = cloneShallow(sourceObj)

 targetObj.c.d = 5

console.log(sourceObj,targetObj)

4.4 深拷贝

深拷贝会拷贝所有的属性,并指向的动态分配的内存,深拷贝相比于浅拷贝速度较慢并且花销较大,拷贝前后两个对象互不影响.

JSON.parse(JSON.stringfy(Object))

但是该方法有以下几个问题:

  1. 会忽略 undefined
  2. 会忽略 symbol
  3. 不能序列化函数
  4. 不能解决循环引用问题
  5. 不能正确处理new Date()
  6. 不能处理正则

undefined symbol 函数这三种情况,会直接忽略

4.4.1 实现深拷贝

function isObjectOrisArray(data) {
            
 if( Object.prototype.toString.call(data) == '[object Object]' ) {
      return 'object'
  } else if( Object.prototype.toString.call(data) == "[object Array]" ) {
      return 'array'
  }else {
      return false
  }
}

function cloneDeep(source,hash = new WeakMap()) {

    //判断数据既不是是数组又不是对象
    if(!isObjectOrisArray(source)) return 
    if(hash.has(source)) return hash.get(source)

    console.log(hash)

    //判断数据是数组还是对象
    let target = isObjectOrisArray(source) == 'object'? {} : []
    hash.set(source,target)
    
    for(let key in source) {
        //避免遍历从原型链到继承下来的可遍历的属性
        if(Object.prototype.hasOwnProperty.call(source,key)) {
            if(isObjectOrisArray(source[key])) {
                //递归写法
                target[key] = cloneDeep(source[key],hash)
            }else {
                target[key] = source[key]
            }
        }
        
    }

    return target
}

5.localstorage sessionstorage cookie区别

Cookie、SessionStorage、 LocalStorage都是浏览器的本地存储。

5.1共同点

都是存储在浏览器本地的。

5.2不同点

(1)写入方式

    Cookie由服务端写入.

    SessionStorage和LocalStorage由前端写入.

(2)生命周期

    Cookie的生命周期是由服务器端在写入时就设置好的,默认的情况下是关闭浏览器后失效.

    SessionStorage的生命周期是仅保持在当前页面,关闭当前会话或者浏览器后就会失效.

    LocalStorage的生命周期从写入就一直存在,除非手动删除.

(3)存储大小

    Cookie的存储空间比较小,大概是4KB,一般页面最多存储20条左右信息.

    SessionStorage和LocalStorage存储空间比较大,大概是5MB.

(4)数据共享

    Cookie、SessionStorage和LocalStorage数据共享都遵循同源原则,SessionStorage还限制必须是同一个页面.

(5)发送请求时是否携带

    前端给后端发送请求时候会自动携带Cookie中的数据,但是SessionStorage和LocalStorage不会.

(6)应用场景

    Cookie一般用于存储登录验证信息SessionID或者token.

    LocalStorage常用于存储不易变动的数据,减轻服务器的压力.

    SessionStorage可以用来检测用户是否刷新进入页面,如音乐播放恢复进度条功能.

6.rem和em

em是相对于当前元素或父元素进行换算,层级越深,换算越复杂。而rem是相对于根元素计算,避免层级关系。

7.移动端1px怎么实现

.scale-half {
    height: 1px;
    transform: scaleY(0.5);
    transform-origin: 50% 100%;
}

8.js微任务和宏任务

js微任务和宏任务跳转链接

9.get和post的相同点和不同点

POST和GET都是向服务器提交数据,并且都会从服务器获取数据。

9.1区别

1、传送方式:get通过地址栏传输,post通过报文传输。

2、get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。

3、GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

4、GET参数通过URL传递,POST放在Request body中。

5、GET请求只能进行url编码,而POST支持多种编码方式。

10.什么是promise

Promise是异步编程的一种解决方案。

10.1. promise有几个API?

promise.resolve(1)

promise.reject(1)

promise.all([promise1,promise2,promise3])

promise.race([promise1,promise2,promise3])

10.2 promise的三个状态

pending,表示进行中

resolved,表示成功

rejected,表示失败

10.3 promise的状态变化

只能从pending->resolved或者pending->rejected

promise的状态改变后就不会再次改变会一直保持这个状态,而且promise不会受到外界的影响,只会受到异步操作的结果的影响

10.4 promise的基本用法

new Promise((resolve,reject)=>{
                if(true){
                    resolve(value);
                }else{
                    reject(err);
                }
            }).then(
                value=>{};
                reason=>{};
            ).catch(
            )

新建一个promise构造函数,函数接受一个函数作为参数,同时该函数有两个参数,reject和resolve 执行异步操作

成功执行resolve(),状态变成resolved状态

失败执行reject(),状态变成rejected状态

无论成功还是失败都可以通过.then()指定成功和失败的回调函数,catch只能调用失败的回调函数

promise是同步执行,then是异步执行

11.异步编程的解决方案

异步编程的5种解决方案

12.ajax axios fetch之间的关系

ajax axios fetch之间的关系