函数创建与定义的过程
- 函数定义
- 在堆内存中开辟一段空间。
- 把函数体内的代码原封不动的存储在这段空间内。
- 把这段空间赋值给栈。
- 函数调用
- 按照变量名内的存储地址找到堆内存中对应的存储空间
- 在调用栈中开辟一个新的函数执行空间
- 在执行空间中进行形参赋值
- 在执行空间中进行预解析
- 在执行空间中完整执行一遍函数内的代码
- 销毁栈创建的执行空间
- 不会销毁的函数执行空间
- 当函数内返回一个引用数据类型时
- 并且函数外部有变量接收着这个引用数据类型
- 函数执行完毕 执行空间不会销毁
- 如果后续不需要这个变量空间了,让变量指向别的位置即可
function fn(){
const obj = {
a:1,
b:2
}
return obj
}
const data = fn();
console.log(data);
//如果后续不需要这个变量空间了,让变量指向别的位置即可
res = 100;
沙箱模式
- 沙箱模式是一种通过隔离代码环境来保证代码安全的模式。它将Js运行时放在一个安全的沙箱环境中,以避免外部代码的污染和攻击。
- 利用了函数内间接返回了一个函数
- 外部函数返回一个对象,这个对象内部写了多个函数
function fn(){
let a = 100;
let b = 200;
return {
getA : function (){
return a
},
getB : function (){
return b
},
setA : function (val){
a = val
}
}
}
//得到一个沙箱
const res1 = fn();
console.log(res1.getA()) // 100
console.log(res1.getB()) // 200
res1.setA(300);
console.log(res1.getA()) // 300
//重新得到一个沙箱
const res2 = fn();
console.log(res2.getA()) // 100
闭包
- 需要一个不会被销毁的执行空间
- 需要直接或者间接的返回一个函数
- 内部函数使用外部函数的私有变量
- 概念:函数里的函数
- 优点:
- 可以在函数外访问函数内的变量
- 延长了变量的生命周期
- 缺点:闭包函数不会销毁空间,大量使用会造成内存溢出。
function outer(){
let a = 100;
let b = 200;
// inner是outer的闭包函数
function inner (){
// 我们使用了一个a变量,但是inner自己没有;
// 所以我用的是 外部函数outer内部的变量a;
return a;
}
return inner
}
let res = outer();
let outerA = res()
console.log(outerA)
防抖和节流
- 防抖:一段时间内连续触发事件,确保事件只执行一次,而不是每次触发都执行。
const debounce = (func,wait)=>{
let timeout;
return (...args)=>{
clearTimeout(timeout);
timeout = setTimeout(()=>{func.apply(this,args)},wait)
}
}
const debounceFn = debounce((val)=>{
console.log(val)
},500)
const onInput = debounceFn(111)
- 节流:短时间内频繁调用,那么只允许在一定时间间隔内最多只执行一次。
const throttle = (func,limit)=>{
let inThrottle;
return (...args)=>{
if(!inThrottle){
func.apply(this,args);
inThrottle = true;
setTimeout(()=>{ inThrottle = false},limit)
}
}
}
const throttleFn = throttle((val)=>{
console.log(val)
},1000)
const onBtn = throttleFn(123)
- 总结:
- 防抖(debounce)更注重结束后的动作,即使事件触发n秒后,如果事件没有再次触发,则执行函数。如果在这n秒内有被重复触发,则重新计算执行时间。
- 节流(throttle)更注重过程中的动作,每隔一段时间就触发,不管事件触发得多频繁。
柯里化函数
- 定义:多个参数的函数转化为一系列只接收一个参数的函数。
function curry(fn){
if(typeof fn !== 'function'){
throw new Error('curry requires a function');
}
return function curried(...args){
if(args.length >= fn.length){
return fn.apply(this,args)
}else{
return function(...args2){
return curried.apply(this,args.concat(args2))
}
}
}
}
// 使用示例
function sum (a,b,c ) {
return a+b+c
}
const curriedSum = curry(sum);
const sumWithFirst = curriedSum(1)
const sumWithFirstAndSecond = sumWithFirst(2)
const result = sumWithFirstAndSecond(3)
console.log(result);
数据劫持(代理)
框架中我们通常数据驱动视图 也就是说数据修改完后视图自动更新。
- 数据劫持:以原始数据为基础,对原始数据进行复制。
- 复制出来的数据是不允许被修改的,值是从原始数据中获取的。
- 语法:
Object.defineProperty(哪一个对象,属性名,{配置项}) - 配置项:
- value:改属性对应的值。
- writable:该属性确定是否允许被重写,默认值是false.
- emunerable:该属性是否可别枚举(遍历), 默认值是false。
- get:是一个函数,叫做getter获取器,可以用来决定改属性的属性值,get属性的返回值就是当前属性的属性值。
- set:是一个函数,叫做setter设置器,当修改属性值的时候会触发函数。
- set和get 不能和其他三个属性一起用
<div>
<h1>姓名:<span class="name">默认值</span></h1>
<h1>年龄:<span class="age">默认值</span></h1>
<h1>性别:<span class="sex">默认值</span></h1>
<br>请输入姓名:<input type="text" name="" id="name">
<br>请输入年龄: <input type="text" name="" id="age">
<br>请输入性别: <input type="text" name="" id="sex">
</div>
<script>
const nameE1 = document.querySelector('.name')
const ageE1 = document.querySelector('.age')
const sexE1 = document.querySelector('.sex')
const inp1 = document.querySelector('#name')
const inp2 = document.querySelector('#age')
const inp3 = document.querySelector('#sex')
function observer(origin, callBack) {
const target = {}
for (let key in origin) {
Object.defineProperty(target, key, {
get() {
return origin[key]
},
set(val) {
origin[key] = val;
callBack(target)
}
})
}
// 首次调用
callBack(target)
return target
}
const app = observer(
{
name: '张三',
age: '18',
sex: '男'
}
,
function bindHtml(res) {
nameE1.innerHTML = res.name
ageE1.innerHTML = res.age
sexE1.innerHTML = res.sex
}
)
inp1.oninput = function () {
app.name = this.value
}
inp2.oninput = function () {
app.age = this.value
}
inp3.oninput = function () {
app.sex = this.value
}
</script>
数据代理(ES6)
- 通过内置构造函数代理
- 语法 :
new Proxy(想要代理的对象) - 数据代理完成后,在向对象中添加属性,也可以自动完成代理。
const obj = {
name: 'zz',
age: 1
}
const res = new Proxy(obj, {
get(target, property) {
return target[property]
},
set(target, property, val) {
target[property] = val
}
})
res.age = 20;
console.log(res.age)
console.log(res.name)
// 数据代理后添加的数据也可以被代理
res.abc = 123
console.log('@', res)