闲来无事总结一下学过的知识
js
原型链
但访问js对象时,如果对象本身不存在这个属性,它将会通过__proto__去父级对象找,直到null;
proto:对象的私有属性,对象指向对象;
prototype:构造函数的属性,函数指向对象;
constructor: 对象的属性,指向实例化对象的构造函数
深拷贝浅拷贝
基本数据类型:String、Number、Boolean、Null、Undefined、Symbol (es6引入的一种类型) ,
特点:直接存储在栈中;
引用数据类型:Object、Array、Function
特点:真实值存在堆中,名字和指针存在栈中。
深浅拷贝只是针对Array与Object这样的引用数据类型。
浅拷贝只是拷贝了它在栈中存储的指针,它们指向的都是同一个堆内存地址,所以浅拷贝在某些情况会造成改变数据后导致别的另一份数据也同步被改变的情况;
而深拷贝是直接将堆内存中存储的数据直接复制一份,不会有浅拷贝互相影响的问题。
浅拷贝的方法:Object.assign()、Array.prototype.concat()、Array.prototype.slice()、=直接复制、for循环
深拷贝方法:JSON.parse(JSON.stringify())、迭代递归浅拷贝、jQuery中extend( )、lodash.cloneDeep
堆、栈
- List item
- 栈(stack):简单地数据段,存放在栈中,有固定大小的内存(自动分配),自动释放
- 基本数据类型:number,string,boolean,undefined,null
- 栈存储了什么:变量名,基本数据类型值,地址
- 全局作用域,私有作用域,都属是栈内存,理论上,存储的越少,运行速度越快
- 先进后出
- 栈内存回收:作用域销毁(立即销毁,不销毁,不立即销毁)
- 全局作用域销毁:一般情况不销毁,页面关闭,整个作用域销毁
- 私有作用域销毁 :
- 不销毁
- 销毁
- 不立即销毁
- 堆(heap):动态分配内存,大小不定也不会自动释放
- 引用类型:栈中存放地址,指向堆中的对象,当我们要获取(函数,数组,对象等)的时候,先从栈中获取地址,然后从堆中获取数据
- 先进先出
- 堆存储了什么:引用数据类型的值
- 内存回收:GC垃圾回收机制
- chrome:标记法,每隔一段时间对所有的空间地址检测,如果没有被占用,立即回收
- ie和火狐:计数法,空间地址被占用一次+1,空闲一次空间地址-1,如果为0 被回收
闭包
闭包是指有权访问另一个函数作用域中的变量的函数;
如下 b函数可以访问a函数作用域的变量:
function a(){
var name = "xxx"
return function b(){
alert(name);
}
}
闭包的特性
jQuery也采用了类似的封装方式,外部的方法和变量就不会污染闭包内部的东西,同时,闭包内的变量也会有效的保存下来
缺点:
1.引用的变量可能发生变化
function outer() {
var result = [];
for (var i = 0; i<10; i++){
result.[i] = function () {
console.info(i)
}
}
return result
}
2.this指向问题
var object = {
name: ''object",
getName: function() {
return function() {
console.info(this.name)
}
}
}
object.getName()() // underfined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
3.内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
el = null // 主动释放el
}
优点
1.用闭包解决递归调用问题
function factorial(num) {
if(num<= 1) {
return 1;
} else {
return num * factorial(num-1)
}
}
var anotherFactorial = factorial
factorial = null
anotherFactorial(4) // 报错 ,因为最好是return num* arguments.callee(num-1),arguments.callee指向当前执行函数,但是在严格模式下不能使用该属性也会报错,所以借助闭包来实现
// 使用闭包实现递归
function newFactorial = (function f(num){
if(num<1) {return 1}
else {
return num* f(num-1)
}
}) //这样就没有问题了,实际上起作用的是闭包函数f,而不是外面的函数newFactorial
2:用闭包模仿块级作用域
for(var i=0; i<10; i++){
console.info(i)
}
alert(i) // 变量提升,弹出10
//为了避免i的提升可以这样做
(function () {
for(var i=0; i<10; i++){
console.info(i)
}
})()
alert(i) // underfined 因为i随着闭包函数的退出,执行环境销毁,变量回收
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
柯里化函数
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
- 原理:当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数以后永远不变) 然后返回一个新的函数接收剩余的参数,返回结果
function getSum(a, b, c) {
return a + b + c;
}
function curry(func){
return function curriedFn(...args){
if (args.length < func.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)))
}
}
return func(...args)
}
}
let curried = curry(getSum)
console.log(curried(1, 2)(3))
学习参考
https://blog.csdn.net/weixin_42003850/article/details/108651427
https://www.cnblogs.com/sandaizi/p/11582488.html
https://www.jianshu.com/p/2975c25e4d71