1. this 的不同应用场景, 如何取值?
this 的五种情况, 在下面知识点已经解释了
2. 手写 bind 函数
// 模拟 bind
Function.prototype.bind1 = function () {// 将参数拆解为数组 (列表 变成 数组)// arguments 可以直接获取, 传进来的所有参数const args = Array.prototype.slice.call(arguments);// 获取 this(数组第一项)const t = args.shift();// fn1.bind(...) 中的 fn1const self = this;// 返回一个函数return function () {return self.apply(t, args);};
};function fn1(a, b, c) {console.log("this", this);console.log(a, b, c);return "this is fn1";
}const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30);
const res = fn2();
console.log(res);
[扩展] - bind、apply、call 的使用场景
- call 用于继承比较多
- apply 用于获取数组最大最小值
- bind 改变定时器 this 的指向
3. 实际开发中闭包的应用场景, 举例说明
什么是闭包?
函数的内部函数运行访问外部上下文,这就是闭包
应用:
- 防抖、节流
- 柯里化 (电商购物车,不常见)
应用场景: 隐藏数据
// 闭包隐藏数据,只提供 API
function createCache() {const data = {} // 闭包中的数据,被隐藏,不被外界访问return {set: function (key, val) {data[key] = val},get: function (key) {return data[key]}}
}const c = createCache()
c.set('a', 100)
console.log( c.get('a') )
4. 读代码
// 创建 10 个 `<a>` 标签, 点击的时候弹出对应的序号
let i, a
for (i = 0; i < 10; i++) {a = document.createElement('a')a.innerHTML = i + '<br>'a.addEventListener('click', function (e) {e.preventDefault()alert(i)})document.body.appendChild(a)
}
// 输出结果 点击弹出窗 弹出都是 10
// 如何 改写成 点击的时候弹出对应的序号, 如 点击1, 弹出 1
let a
for (let i = 0; i < 10; i++) {a = document.createElement('a')a.innerHTML = i + '<br>'a.addEventListener('click', function (e) {e.preventDefault()alert(i)})document.body.appendChild(a)
}
// 现在点击的时候弹出对应的序号
下面就是相关知识点
知识点:
-
作用域和自由变量
-
闭包
-
this
(一)
作用域
- 全局作用域
- 函数作用域
- 块级作用域 ( ES6新增
let
)
作用域代表了一个变量或者说某个变量的合理使用范围
自由变量
- 一个变量在当前作用域没有定义, 但被使用了
- 向上级作用域, 一层一层依次寻找, 直到找到为止
- 如果全局作用域都没找到, 则报错
xx is not defined
var
是全局作用域, 全局对象window
(二)
闭包
作用域应用的特殊情况, 有两种表现
- 函数作为参数 被传递
- 函数作为返回值 被返回
// 函数作为返回值
function create() {let a = 100return function () {a += 1console.log(a)}
}// const fn = create()
// fn() // 101
// fn() // 102
// fn() // 103// 函数作为参数被传递
function print(fn) {const a = 200fn()
}
const a = 100
function fn() {console.log(a)
}
print(fn) // 100// 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
// 不是在执行的地方!!!
闭包的产生原因:内部的函数存在外部作用域的引用就会导致闭包
JS 闭包经典使用场景和含闭包必刷题
总结: 闭包就是一种特殊的函数, 分两种形式的闭包
- 函数作为参数, 被传递
- 函数作为返回值, 被返回
(三)
this
this 取什么值, 是在执行的时候确定的, 不是在函数定义时确定的, 适用于以下五种
- 作为普通函数 (返回 window)
- 使用 call、apply、bind (传入什么就绑定什么)
- 作为对象方法被调用 (返回对象本身)
- 在 class 方法中调用 (返回实例本身)
- 箭头函数 (由它的上级作用域决定)
call、apply、bind
call、bind 都会改变 this 的指向, 但是 bind 会返回一个函数, 需要重新运行这个函数
// call
function fn1() {console.log(this)
}
fn1() //windowfn1.call({ x: 100 }) // { x: 100 }// bind
const fn2 = fn1.bind({ x: 200 })
fn2() // { x: 200 }// apply
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);console.log(max);
// Expected output: 7
const min = Math.min.apply(null, numbers);
console.log(min);
// Expected output: 2
箭头函数
箭头函数 this 的值取它上级作用域的值
// 使用 普通函数异步中 this 指向 window
const zhangsan = {name: ’张三‘,sayHi() {// this 即当前对象console.log(this)},wait() {setTimeout(function() {// this === windiowconsole.log(this)})}
}
// 使用 箭头函数异步中 this 即当前对象
const zhangsan = {name: '张三',sayHi() {// this 即当前对象console.log(this)},waitAgain() {setTimeout(() => {// this 即当前对象console.log(this)})}
}
class 方法中
class People {constructor(name) {this.name = namethis.age = 20}sayHi() {console.log(this)}
}
const zhangsan = new People('张三')
zhangsan.sayHi() // zhangsan 对象
5. 防抖、节流
防抖
概念
多次触发,在规定时间内,只有最后一次执行
如何实现
应用场景
窗口调整、搜索框实时联想、按钮频繁点击
节流
概念
每隔一段时间,执行一次
如何实现
应用场景
页面滚动、DOM 元素拖拽、播放事件算进度
6. 闭包中变量如何释放?
let fn = function(){let sum = 0return function(){sum++console.log(sum);}
}
fn1 = fn()
fn1() // 1
fn1() // 2
fn1() // 3
fn1 = null // fn1的引用fn被手动释放了
fn1=fn() // num再次归零
fn1() // 1
7. 闭包是什么, 有什么特征? 有什么负面影响?
闭包:
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
特征:
作用域和自由变量
重点:
自由变量的查找, 要在函数定义的地方 (而非执行的地方)
影响:
变量会常驻内存, 得不到释放, 闭包不要乱用
可能会造成内存泄漏, 但是闭包中的数据, 我们不知道什么时候会被释放, 所以不算内存泄漏
内存泄漏是指, 内存中的变量一直没被使用, 常驻在内存中
// 自由变量示例 —— 内存会被释放
let a = 0
function fn1() {let a1 = 100function fn2() {let a2 = 200function fn3() {let a3 = 300return a + a1 + a2 + a3}fn3()}fn2()
}
fn1()// 闭包 函数作为返回值 —— 内存不会被释放
function create() {let a = 100return function () {console.log(a)}
}
let fn = create()
let a = 200
fn() // 100
开发过程中什么时候用到闭包?
- 使用异步请求的时候, 异步请求以函数作为参数进行传递, 这就是一个闭包
- 还有就是 我们写防抖节流函数, 也是需要使用保存局部变量进行计数, 然后返回回调函数, 这也是闭包的使用
- 函数柯里化, 也是一种闭包的使用, 函数作为返回值
任何闭包的使用场景的目的:
- 创建私有变量
- 延长变量的生命周期