深拷贝和浅拷贝
- 浅拷贝:浅拷贝是创建一个新对象,这个对象有着原始对象的一份精准拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用属性,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址的值,就会影响到另一个对象。
- 深拷贝:深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响到原对象。
- 浅拷贝的实现方法
- Object.assign():可把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
- 函数库lodash的_.clone方法
- 展开运算符
- Array.prototype.concat();
- Array.prototype.slice();
- 深拷贝的实现方法
- JSON.parse(JSON.stringify()):可以实现数组或对象深拷贝,但不能处理函数和正则。
- 函数库lodash的_.cloneDeep方法
- jQuery.extend()方法:
$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝 - 手写递归方法。
function deepClone(obj, hash = new WeakMap()) { if(obj === null) return obj; // 如果是null或者undefined就不进行拷贝操作 if(obj instanceof Data) return new Date(obj); if(obj instanceof RegExp) return new RegExp(obj); // 可能是对象或者普通的值,如果是函数的是不需要深拷贝 if(typeof obj !== 'object') return obj; // 是对象的话就要进行深拷贝 if(hash.get(obj)) return hash.get(obj); let cloneObj = new obj.constructor(); // 找到的是所属类型上的constructor,而原型上的constructor指向的是当前类本身 hash.set(obj, cloneObj); for(let key in obj) { if(obj.hasOwnProperty(key)) { // 实现一个递归拷贝 cloneObj[key] = deepClone(obj[key], hash) } } return cloneObj; }
Map 和 WeakMap的区别
- Map的键可以是任意类型,WeakMap只接受对象作为键(null除外),不接受其他类型的值作为键。
- Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键; WeakMap的键是弱引用。默认创建一个对象,就默认创建一个强引用的对象,我们只有手动将obj = null,它才会被垃圾回收机制进行回收,如果是弱引用对象,垃圾回收机制才会自动进行回收。
- Map可以被遍历,WeakMap不能被遍历。
new一个函数是发生了什么
- 创建了一个空对象
let obj = {} - 设置新对象的__proto__属性指向构造函数的原型对象
obj.__proto__ = Person.prototype - 让构造函数中的this指向新对象,并执行构造函数的函数体
let res = Person.call(obj) - 判断构造函数的返回值类型,如果是值类型,则返回新对象。如果是引用类型,就返回这个引用类型的对象。
if(typeof(res) == "object") { p = res; }else { p = obj; }
普通函数和箭头函数的区别
- 箭头函数的定义要比普通函数定义简洁,清晰。
- 箭头函数没有prototype,所以箭头函数本身没有this
- 箭头函数没有自己的this,箭头函数的this指向在定义的时候继承外层第一个普通函数的this,所以,箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变。
- call、apply,bind无法改变箭头函数中this的指向。
- 箭头函数不能作为构造器使用,用new调用时会报错。
- 箭头函数不绑定arguments,取而代之用rest参数...代替arguments,来访问箭头函数的参数列表。
- 箭头函数不能用作Generator函数,不能使用yield关键字。
this指向问题
- js的this总是指向一个对象,而具体指向那个对象是在运行时基于函数的执行环境动态绑定的,而不是函数被声明时的环境。
- 确定js中的this的指向问题,一般从6个方面进行考虑:
- 箭头函数:箭头函数中的this是在创建它时的外层this的指向。创建箭头函数时,就已经确定了它的this指向,箭头函数内的this指向外层的this,如果我们要确定箭头的this,只需要确定外层的this就可以了。
- new:当使用new关键字调用函数时,函数中的this一定指向js创建的新对象。箭头函数不能当做构造函数,所有不能和new一起执行。
- bind:bind不会执行函数,只是修改this后返回一个新的函数。
- apply和call:apply()和call()第一个参数都是this,区别在于通过apply调用的实参是放在数组中的,而通过call调用时实参是逗号分隔开来的。
- obj. :
- 直接调用:在函数不满足前面的场景,被直接调用时,this将指向全局对象。
call,bind, apply的区别
- 三个函数的作用都是将函数绑定到上下文中,用来改变函数中this的指向;三者的不同点在于语法上的不同:
- apply和call的区别是参数的传递方式不一样,call是多个参数以逗号分割直接传递,apply是以一个数组的形式传递的。
- bind:直接改变this的指向,指定一个新的函数,并返回这个新的函数。
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。简单理解就是内嵌函数,即函数中嵌套函数
由于在JS中,变量的作用域属于函数作用域,在函数执行后作用域就会被清理、内存也随之回收,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数——也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后作用域内的值也不会被销毁。
- 应用场景
- 构造函数的私有属性
- 计算缓存
- 函数节流,防抖。
{},new Object(), Object.create({})的区别
- 字面量和new Object()创建的是Object的实例,原型就是Object.prototype,继承内置对象Object;
- Object.create()中第一个参数指的就是新对象的原型对象,是个必填参数,这个参数可以为null,那新对象就彻底是一个空对象,没有继承Object.prototype中任何属性和方法。(propertiesObject:可选参数,指的要添加到新对象的可枚举属性的描述符及相应的属性名称。
防抖和节流
- 防抖:就是指触发事件后n秒后才执行函数,如果在n秒内有触发了事件,则会重新计算函数执行时间。
- 节流:就是指连续触发事件但是在n秒中只执行一次。
- 应用场景
- 防抖:
- 搜索框,用户在不断输入值是,用防抖来节约请求资源。
- window触发resize的时候,不断的调整浏览器窗口会不断的触发这个事件,用防抖来让其只触发一次。
- 节流:
- 鼠标不断点击触发,mousedow(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用节流来判断。
- 防抖:
// 防抖
const debounce = (fn, time) => {
let timeout = null;
return function() {
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, time);
}
};
// 节流
const throttle = (fn, time) => {
let flag = true;
return function() {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, time);
}
}
数据类型判断的方法?
- typeof:简单类型用typeOf,复杂类型用instanceOf。用typeof判断简单类型,除了null会返回object,其他都会返回正确的结果,如果判断复杂类型,除了function,一律返回object。
- instanceOf:用instanceof可以检测某个实例是否是某个对象类型,如果用它来检测简单数据类型则始终返回false,因为基本类型不是对象。instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
- Object.prototype.toString()
- 所有对象都会从它的原型上继承一个 constructor 属性,这个属性指向它的构造函数,可以利用这个属性判断数据类型。所有对象都会从它的原型上继承一个 constructor 属性,这个属性指向它的构造函数,可以利用这个属性判断数据类型。
function getType(obj) {
return Object.prototype.toSring.call(obj).slice(8, -1)
}
懒加载的原理和实现
懒加载的原理和实现: juejin.cn/post/698948…