call,apply,bind
# apply,call,bind 原理
call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数
都可以改变函数 func 的 this 指向
call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参
bind 虽然改变了 func 的 this 指向,但不是马上执行,而这两个(call、apply)是在改变了函数的 this 指向之后立马执行
let a = {
name: "jack",
getName: function(msg) {
return msg + this.name;
},
};
let b = {
name: "lily",
};
console.log(a.getName("hello~")); // hello~jack
console.log(a.getName.call(b, "hi~")); // hi~lily
console.log(a.getName.apply(b, ["hi~"])); // hi~lily
let name = a.getName.bind(b, "hello~");
console.log(name()); // hello~lily
# 判断数据类型
function getType(obj) {
let type = typeof obj;
if (type !== "object") {
return type;
}
return Object.prototype.toString.call(obj).replace(/^$/, "$1");
}
# 类数组借用方法
arguments
这种类数组
var arrayLike = {
0: "java",
1: "script",
length: 2,
};
Array.prototype.push.call(arrayLike, "jack", "lily");
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
# 获取数组的最大值/最小值
apply 直接传递数组作为调用方法的参数,也可以减少一步展开数组
let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6
# 继承
function Parent() {
this.name = "parent";
}
Parent.prototype.getName = function() {
return this.name;
};
function Child() {
Parent.call(this);
this.type = "child";
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
# 手写 new
new 被调用后大致做了哪几件事情:
- 让实例可以访问到私有属性
- 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
- 构造函数返回的最后结果是引用数据类型
function MyNew(ctor, ...args) {
if (typeof ctor !== "function") throw " ctor must be a function";
let obj = Object.create(ctor.prototype);
// let obj = new Object();
// obj.prototype = Object.create(ctor.prototype);
let res = ctor.call(obj, ...args);
let isObject = typeof res === "object" && res !== null;
let isFunction = typeof res === "function";
return isObject || isFunction ? res : obj;
}
# 手写 call 和 apply
实现 call 和 apply 的关键就在 eval 这行代码。其中显示了用 context 这个临时变量来指定上下文,然后还是通过执行 eval 来执行 context.fn 这个函数,最后返回 result
Function.prototype.call = function(context, ...args) {
let context = context || window;
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
};
Function.prototype.apply = function(context, args) {
let context = context || window;
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
};
# 手写 bind
要注意这两个方法(call 和 apply)和 bind 的区别就在于,这两个方法是直接返回执行结果,而 bind 方法是返回一个函数,因此这里直接用 eval 执行得到结果
实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用 Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound
Function.prototype.bind = function(context, ...args) {
if (typeof this !== "function") {
throw new Error("this must be a function");
}
var that = this;
var fbound = function() {
that.call(context, ...args, arguments);
};
if (this.prototype) {
fbound.prototype = Object.create(this.prototype);
}
return fbound;
};
# 手写 instanceof
function myInstanceof(left, right) {
//基本数据类型直接返回false
if (typeof left !== "object" || left === null) return false;
//getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
while (true) {
//查找到尽头,还没找到
if (proto == null) return false;
//找到相同的原型对象
if (proto == right.prototype) return true;
proto = Object.getPrototypeof(proto);
}
}
# 手写 debounce
https://blog.shenzjd.com/pages/3c209d1a362c4/ (opens new window)
function debounce(cb, delay) {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
cb(...arguments);
}, delay);
};
}
# 手写 throllte
function throttle(cb, delay) {
var timer;
return function() {
if (timer) return;
timer = setTimeout(() => {
cb();
timer = null;
// clearTimeout(timer)
}, delay);
};
}
编辑 (opens new window)
上次更新: 2024/11/05, 06:40:27