招式与内功并修,根本与潮流兼顾!!!
扎实的内功通俗易懂的讲法就是最牛逼的讲法(不认同这个观点的其实就没必要往下看了, 哈哈哈。。。)
1.变量类型与内存的关系
-
基本数据类型: Sting Number Boolean null undefined Symbol
基本数据类型保存在栈内存中 闭包中的基本数据类型变量不保存在栈内存中,而是保存在堆内存中。
-
引用数据类型 Array,Function,Object.. 除了上文提到的基本数据类型以外,所有类型都是引用数据类型
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
为了更好的搞懂变量对象与堆内存,我们结合以下例子与图解进行理解。
// 基本数据类型-栈内存
let a1 = 0;
// 基本数据类型-栈内存
let a2 = 'this is string';
// 基本数据类型-栈内存
let a3 = null;
// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let b = { m: 20 };
// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
let c = [1, 2, 3];
image.png
因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量中获取了该对象的地址指针, 然后再从堆内存中取得我们需要的数据。
2.深拷贝和浅拷贝的区别是什么?实现一个深拷贝
浅拷贝只拷贝一层,而深拷贝是层层拷贝。
-
深拷贝
深拷贝复制变量值,对于引用类型的变量,递归至基本类型后,再复制,复制后与源对象完全隔离互不影响。
-
浅拷贝
浅拷贝是将对象的每个属性进行复制,当对象的属性是引用类型时,实质复制的是其引用堆里面的指向路径,当引用指向的值发生变化时也会发生变化。
-
实现
1.深拷贝最简单的实现是JSON.parse(JSON.stringify(obj))
缺点
1.对象的属性值是函数时,无法拷贝。
2.原型链上的属性无法拷贝
3.会忽略 undefined
4.不能正确的处理 Date 类型的数据
5.不能处理 RegExp
6.会忽略 symbol
2.使用 deepClone方法递归实现
//使用递归的方式实现数组、对象的深拷贝
function deepClone(obj, hash = new WeakMap()) {
if (obj instanceof RegExp) return new RegExp(obj)
if (obj instanceof Date) return new Date(obj)
if (obj === null || typeof obj !== 'object') {
return obj
}
if (hash.has(obj)) {
return hash.get(obj)
}
/**
* 如果obj 是数组, 那么obj.constructor 是 [Function: Array]
* 如果obj 是对象, 那么obj.constructor 是 [Function: Object]
*/
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
console.log(key)
//递归
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
3.如何正确判断this的指向?
谁调用它,this 就指向谁。
-
全局环境中的 this
浏览器环境: this 都指向全局对象 window;
node 环境:this 都是空对象 {};
-
是否是 new 绑定
构造函数返回值不是 function 或 object。new Super() 返回的是 this 对象。
构造函数返回值是 function 或 object,new Super()是返回的是Super种返回的对象。
-
箭头函数的情况
箭头函数没有自己的this,继承外层上下文绑定的this。
4.柯里化函数
概念
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
作用
-
参数复用
-
提前返回 – 返回接受余下的参数且返回结果的新函数
-
延迟执行 – 返回新函数,等待执行
应用场景
重复调用一个函数参数很多重复的
const curry = (fn, ...args) => args.length < fn.length
? (...arguments) => curry(fn, ...args, ...arguments) : fn(...args)
function sumFn(a, b, c) {
return a + b + c
}
var sum = curry(sumFn)
console.log(sum(2, 3, 4)) // 10
console.log(sum(2)(3)(4)) // 10
console.log(sum(2, 3)(4)) // 10
5.let为啥变量提升了以及let const var 区别
一个变量或者方法是需要经过「创建」「初始化」「赋值」到使用的过程
created (创建)
initialized (初始化)
assigned (赋值)
|
created x | created fn
initialized x | initialized fn
var x = 1 <= assigned | assigned fn
| funtion fn() {}
______________________________|_____________________________________
|
created x | created x
let x = 1 <= initialized | const x = 1 <=initialized x
x = 2 | x = 2 Error: no assign ment
|
结论补充
-
let变量也会提升其实是创建过程被提升了,初始化过程没有被提升
-
var 的「创建」和「初始化」都被提升了
-
function 的「创建」「初始化」和「赋值」都被提升了
最后看 const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
所谓暂时死区,就是不能在初始化之前,使用变量。
-
区别
- let/const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
- 相同作用域中,let 和 const 不允许重复声明,var 允许重复声明。
- const 声明变量时必须设置初始值
- const 声明一个只读的常量,这个常量不可改变。
6.什么是闭包,什么是内存销毁
请移步 通俗易懂的解释就是最牛逼的讲法
网友评论