Author: Gorit
Date:2022年1月6日
这三个玩意是干啥的,apply, call, bind?
用来修改 this 指向的,如果默认值为 null 或者 undefined 的,那么 this 的值就会指向 window(游览器环境下)
调用对象的方法,将另一个对象替换为当前对象。
最实用的 call 的用法,简单来说,我们有个函数,一般都是通过函数名直接调用执行,另一种方式就是通过函数名.call() 来调用
这样做就是改变了函数的上下文,即改变了 this 的指向
function add(a, b) {
console.log(this); // Window
return a + b;
}
add(1,2);
// 使用 call
function add1(a, b) {
console.log(this); // obj
return a + b + this.c;
}
const obj = {
c: 20,
};
add1.call(obj, 10, 20); // 50,但是在实际的开发当中,我们会传 undefined 的
需求:
function add1(a, b) {
console.log(this); // obj
return a + b + this.c;
}
const obj = {
c: 20,
};
/**
* 如何实现 call 函数?
* 对象.函数() this 指向这个对象
* @param {*} fn 接收的函数,实际上为回调函数
* @param {*} obj 改变 this 为 obj
* @param {...any} args 传参
*/
function call1(fn, obj, ...args) {
// 可能存在为 null 或 undefined 的 this
if (obj === undefined || obj === null) {
// 绑定 Node.js 的全局对象
obj = globalThis;
}
// console.log(obj);
// 临时绑定方法 为 obj
obj.temp = fn;
const result = obj.temp(...args);
// 删除对象的属性,方法
delete obj.temp;
return result;
}
globalThis.c = 10;
let result = call1(add1, obj, 20, 20);
console.log(result); // 60
let result1 = call1(add1, undefined, 20, 20);
console.log(result1); // 50
apply 方法和 call 方法类似,唯一不同的点就是传参的方式
apply 一次性接收一个数组,而 call 是可以接收多个参数
let arr = ['a', 'b'];
let elements = [1,2,3];
arr.push.apply(arr, elements);
console.log(arr); // ['a', 'b', 1, 2, 3]
减少循环操作
const test = [10,20,30,40];
console.log("test max val is =>", Math.max.apply(undefined, test));
console.log("test min val is =>", Math.min.apply(undefined, test));
/**
*
* @param {*} fn
* @param {*} obj this 指向
* @param {*} args 数组[]
* @returns
*/
function apply(fn, obj, args) {
if (obj === undefined || obj === null) {
obj = globalThis;
}
// 为 obj 添加临时方法
obj.temp = fn;
// 执行临时方法,传参
const result = obj.temp(...args);
// 删除临时方法
delete obj.temp;
return result;
}
function add(a , b) {
return a + b + this.c;
}
const obj = {
c: 10
}
// 如果没有绑定这个,则会打印 NaN
globalThis.c = 5;
console.log(apply(add, undefined, [10, 20])); // 35
console.log(apply(add, obj, [10, 20])); // 40
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 指定为 bind() 的第一个参数,而其他参数则作为新函数的参数,供调用使用
globalThis.x = 9; // Node.js 环境 globalThis
var module = {
x: 81,
getX: function() {
return this.x;
}
}
console.log(module.getX()); // 81
let retireve = module.getX;
console.log(retireve()); // 9 拿到的是全局的
/**
* 绑定函数
* 创建一个新函数,把 'this' 绑定到 module 对象上
*/
let boundGetX = retireve.bind(module);
console.log(boundGetX()); // 81
学习 callback 之前,我们先提出如下几个问题
什么是 callback?从名称上来看,它在 JavaScript 中叫做 “回调函数”?那么什 么又是“回调函数” 呢?“回调函数” 又要怎么触发呢?它有返回值吗?
不绕圈子了,不然就进入回调陷阱了
我们先来看一个生活 中回调函数的一个例子(我在学习 callback,在知乎翻到的)
这里面出现了几个概念,我的理解如下
换成 JavaScript 的语言来说,我们注册了一个异步函数,但是不知道什么时候生效(收到回调)。当收到某一特定事件(货物到了),并且店员打电话告诉我们,通知我们去拿货物(通知回调)
我们日常在编程的过程中,我们的代码一般都是从上往下按顺序执行的。很少会出现程序不按照顺序执行的情况。【仅限 JavaScript 环境,因为 JavaScript 是单线程语言,是不存在并发这一说的】
但是,有时候确实需要回调函数处理一些 非同步 问题
但是在有些情况,确实需要异步执行的!!!
举个最简单的例子,网络请求,大家都熟悉吧,有时候网页内容加载不出来,我们就会按下键盘上的 F5 键,这个时候游览器就会把当前网页重新加载一般。 — 这种必须要等待内容加载完毕的,就叫 “同步处理”
我们再来看看 “异步” 的案例, 翻译都用过吧,但是我们输入完待翻译的内容之后,整个网页并没有刷新,只有翻译的框框显示了翻译结果。这种局部刷新的情况就叫做 “异步处理”
<button id="submit">提交</button>
...
// 处理 DOM 事件
let btnSubmit = document.getElementById("submit");
// 方式一:监听点击事件 (ES5 的写法,在 ES6 中我们可以使用箭头函数来简化)
btnSubmit.addEventListener('click', function() {
// 逻辑处理
});
// 方式二:
btnSubmit.onclick(function () {
// 业务逻辑
});
// 发起 ajax 网络请求,这里我用到了自己编写的真实的网络接口
let res = fetch("http://api.xxx.xxxx/api/v1/rest/info")
.then(res => res.json())
.then(res => {
console.log(res);
});
// 返回的是一个 JSON 对象
{
"code": 20000,
"msg": "操作成功",
"data": {
"method": "get",
"remoteIpAddress": "xx.xx.xx.xx",
"requestPath": "/api/v1/rest/info"
}
}
这里怎么有两个 then?这里实际上是简写了,使用了 ES6 的箭头函数语法,直接把网络请求拿到的值,当成函数的参数传递给下游处理
// 第一个参数就是要处理的函数,第二个是延迟的时间
setTimeout(() => {
// ...
}, 2000);
let arr = [1,2,3];
// 其实 function 也可以省略掉
arr.forEach(function (item, index) => {
console.log(item, index);
})
再说一个,我们在 JavaScript 中经常会用到的数组的方法 —— forEach
在用 vscode,输入 forEach 的时候,就会弹出如下信息。这不就告诉了我们这个是怎么用的嘛
翻译过来,简单的说 forEach 函数,会接收三个参数,并且会 告诉 callbackfn 对数组中的每一个元素执行一次回调操作
所以这个 forEach 的案例告诉了我们什么?
callback 实际上也是一个函数,它也可以接收参数,并有返回值。只不过它的使用方式有点特殊。它一般在函数中使用,写出来就是下面这个样子
// 伪代码 => 一个函数接收另一个函数
fn(callbackFn());
通过上面,我们知道了 callback 本身就是一个函数,调用方法的时候,使用函数接收。就能拿到回调结果
// ES6
const handleSomeEvent = (param1, param2, callback) {
let res = param1 + param2;
// callback 就是在函数里面调用了另一个函数
if (callback) {
callback(res);
}
}
handleSomeEvent(1,2, (num) => {
console.log(num); // 3
})