一、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继承方式
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()是浅拷贝,只解决了第一层.
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)
结果与上面是一样的:
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拷贝遇到的问题与对象一致
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]
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))
但是该方法有以下几个问题:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用问题
- 不能正确处理new Date()
- 不能处理正则
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微任务和宏任务
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是异步执行