-
执行上下文、作用域链、闭包
js有三种执行上下文类型,分别为全局执行上下文、函数执行上下文、Eval函数执行上下文 全局执行上下文:这是默认或者说基础的上下文,任何不在函数中的代码都在全局在上下文中。它会执行两件事,一是创建一个全局的window对象(浏览器情况下),二是设置this等于这个全局对象,一个程序中只会有一个全局执行上下文。 函数执行上下文:每当一个函数执行的时候,都会为这个该函数创建一个新的上下文。每个函数都有它自己的执行上下文,且是在函数被调用的时候创建的。函数上下文可以有任意多个,每当一个新的执行上下文被创建,它都会按定义顺序执行。 Eval函数执行上下文:执行在eval函数内部的代码也会有它自己的执行上下文,但由于不常用,因为不怎么了解。
执行栈:也就是其他语言所说的调用栈,是一种LIFO(后进先出)的数据结构的栈,被用来存储代码运行时创建的所有执行上下文。 js引擎第一次碰到某段代码时,首选创建一个全局上下文执行并压入当前执行栈,每当后续遇到一个函数,为该函数创建一个新的执行上下文并压入执行栈顶部,引擎执行位于顶部的函数,执行结束时,从栈中弹出,然后控制流程到当前栈的下一个上下文,直到所有的上下文执行完。
作用域链 首先理解作用域,作用域是运行代码中某些特定部分中变量、函数和对象的可访问性。 Es6之前没有块级作用域,只有全局作用域和函数作用域,Es6提供了块级作用域,可通过let const来体现。 全局作用域,在代码的任何地方都能访问到的对象拥有全局作用域, 函数作用域,只在固定代码片段可访问的对象或变量拥有的作用域。 块级作用域,声明的变量在指定块的作用域外无法访问。
var a = 100 function F1() { var b = 200 function F2() { var c = 300 console.log(a) // 自由变量,顺作用域链向父作用域找 console.log(b) // 自由变量,顺作用域链向父作用域找 console.log(c) // 本作用域的变量 } F2() } F1()
寻找变量从当前作用域寻找,如果没找到,在父级作用域寻找,如果还是没找到,在全局作用域寻找,这一层一层的关系就是作用域链。
闭包
能够访问其他函数内部变量的函数,被称为 闭包 ,内部的函数存在外部作用域的引用就会导致闭包
// 闭包是指那些能够访问自由变量的函数。这里的自由变量是外部函数作用域中的变量。
function add() { let num = 100 function con(){ console.log(num) } con() } add() 使用场景1: return回一个函数 var n = 10 function fn(){ var n =20 function f() { n++; console.log(n) } return f } var x = fn() x() // 节流 function throttle(fn, timeout) { let timer = null return function (...arg) { if(timer) return timer = setTimeout(() => { fn.apply(this, arg) timer = null }, timeout) } } // 防抖 function debounce(fn, timeout){ let timer = null return function(...arg){ clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arg) }, timeout) } }
-
call,apply,bind
fun.call(thisArg,param1,param2)
fun.apply(thisArg,[param1,param2])
fun.bind(thisArg,param1,param2)
call/apply:
fun
执行的结果 bind:返回fun
的拷贝,并拥有指定的this
值和初始参数fun
的
this指向
thisArg`对象**非严格模式下:thisArg指定为null,undefined,fun中的this指向window对象.
严格模式下:
fun
的this
为undefined
值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象,如 String、Number、Boolean
-
浅拷贝 深拷贝(一般来说,js数据分为基本数据类型和引用数据类型,我们说的深浅拷贝都是相对于引用数据类型而言的)
浅拷贝:只复制了引用,未复制真正的值;
let a = [1,2,3] let cloneA = a cloneA.push(4) console.log(a,cloneA) // [1,2,3,4] , [1,2,3,4] 可以看到a和clone都改变了 这就浅拷贝
深拷贝:引用和值都复制了。只要进行了深拷贝,他们就是完全独立的两个个体了,其中一个数据的更改不会引起另一个数据的变化
1.深拷贝最简单的方式:JSON.stringfy JSON.parse let a = [1,2,3] let cloneA = JSON.parse(JSON.stringfy(a)) cloneA.push(4) conosle.log(a,cloneA) // [1,2,3] , [1,2,3,4] 但这这种方式不能适配所有的情况 const obj = { name:'tom', sayHi:function(){ console.log('say hi') } } const cloneObj = JSON.parse(JSON.stringfy(obj)) console.log(cloneObj) // {nam:'tom'} // 可以看到在进行之前的深浅拷贝后,sayHi方法丢了,原因就是此种方式在转换时遇到undefined,function,symbol类型的数据回忽略。 2. 递归 function deepClone(val){ // 判断一下复制的目标是对象还是数组 const targetObj = val.constructor === 'Array'?[]:{} for(let keys in val){ if(val[keys]&&typeof val[keys] === 'Object'){ tragetObj[keys] = val[keys].constructor === 'Array'?[]:{} targetObj[keys] = deepClone[val[keys]] }else{ targetObj[keys] = val[keys] } } return targetObj } deepClone(): source => { let clone = Object.assign({}, source); Object.keys(clone).forEach( key => (clone[key] = typeof source[key] === 'object' ? deepClone(source[key]) : source[key]) ); return clone; } const cloneObj = deepClone(obj) conosle.log(cloneObj) // {name:'tom',sayHi:function(){console.log('say hi')}}
-
reduce
reduce是一个数组的迭代方法,和map、filter不同,reduce方法可以缓存一个变量,迭代时我们可以操作这个变量然后返回它。
使用场景1:数组累加。
let arr = [1,2,3,4] arr.reduce((a,i) =>{ return a + i})
使用场景2:处理不规则数组
let data = [ ["红色","128g", "苹果手机"], ["南北","两室一厅","128㎡","洋房住宅"], ["小米","白色","智能运动手表","心率血压血氧","来电信息提醒"], ["官方发售","2020年秋季","篮球","球鞋","品牌直邮"] ] data.map((item) => {return item.reduce((a.i) =>{ return `${a} {i}` })})
使用场景3:数组去重
let arr = [1,2,3,'a','b','c',2,'a'] arr.reduce((val,item) => { if(val.indexOf(item) === -1){ val.push(item) } return val },[])
使用场景4:按属性分组
let obj = [ {name: '张三', job: '数据分析师', country: '中国'}, {name: '李四', job: '科学家', country: '中国'}, {name: '雷尔', job: '科学家', country: '美国'}, ] let obj1 = obj.reduce((val,item) => { let newKey = item.country if(!val[newKey]){ val[newKey]=[] } val[newKey].push(item) return val },[]) console.log(obj1);
使用场景5:数组扁平化
let arr = [[1, 2, 3], ['a', ['b', 'c'], 'd'], [5]] function flatArr(val) { return val.reduce((newArr, item) => newArr.concat(Array.isArray(item)? flatArr(item): item ) ,[]) } console.log(flatArr(arr))
使用场景6:字符串反转
let str = 'hello,world' console.log([...str].reduce(val,item => item+val))