五. call_apply_bind 和 arguments
来实现一下apply、call、bind函数, 不会过度考虑一些边界情况
这里只是模拟,v8引擎大部分都是c++代码写的
call apply的使用的区别 见4.5this的显示绑定
5.1. JS模拟实现call函数
// 如果args没有传参数,args就是一个空数组
Function.prototype.hycall = function (thi sArg, ...args) {
// 获取执行调用的函数
var fn = this
// 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
thisArg = thisArg ? Object(thisArg) : window
// 将传进来的对象绑定到this(隐式绑定)
thisArg.fn = fn
// 调用被执行的函数
var result = thisArg.fn(...args)
delete thisArg.fn
// 返回最终结果
return result
}
function foo() {
console.log("foo", this);
}
function sum(num1, num2) {
console.log("sum", this);
return num1 + num2
}
foo.hycall(null)
var sums = sum.hycall("abc", 10, 20)
console.log(sums);
5.2. JS模拟实现apply函数
Function.prototype.hyapply = function (thisArg, argArray) {
// 获取执行调用的函数
var fn = this
// 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
// 如果传入的thisArg=0,则转为Number对象
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
// 将传进来的对象绑定到this
thisArg.fn = fn
// 调用被执行的函数
var result
// 执行前判断是否有数组传进来,不然解构时会报错
argArray = argArray || []
result = thisArg.fn(...argArray)
delete thisArg.fn
// 返回最终结果
return result
}
function sum(num1, num2) {
console.log("sum被调用", this, num1, num2);
return num1 + num2
}
var result = sum.hyapply("abc", [10, 20])
console.log(result);
function foo() {
console.log("foo", this);
}
var bar = foo.hyapply("aaa")
console.log(bar);
var bar1 = foo.hyapply(0)
console.log(bar1);
5.3. JS模拟实现bind函数
- 系统bind()的调用
function sum(num1, num2, num3, num4) {
console.log("sum被调用", this, num1, num2, num3, num4);
return num1 + num2
}
var result = sum.bind("aaa", 10, 20, 30, 40)
result()
var result = sum.bind("aaa")
result(10, 20, 30, 40)
var result = sum.bind("aaa", 10, 20)
result(30, 40)
JS模拟实现bind函数
Function.prototype.hybind = function (thisArg, ...argArray) {
// 获取执行调用的函数
var fn = this
// 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
// 如果传入的thisArg=0,则转为Number对象
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
// 返回新函数
return function proxy(...args) {
// 将传进来的对象绑定到this
thisArg.fn = fn
// 合并两次传入的参数
var fanilyArr=[...argArray,...args]
var result = thisArg.fn(...fanilyArr)
delete thisArg.fn
return result
}
}
function foo() {
console.log(this);
return 1
}
var bar = foo.hybind("xxx")
console.log(bar());
function sum(num1, num2, num3, num4) {
console.log("sum被调用", this, num1, num2, num3, num4);
return num1 + num2 + num3 + num4
}
var newSum = sum.bind("aaa", 10, 20)
console.log(newSum(30, 40));
5.4. 认识arguments
arguments 是一个 对应于 传递给函数的参数 的 类数组(array-like)对象。
- 有数组的一些特性,比如说length,比如可以通过index索引来访问
- 没有数组的一些方法,比如forEach、map等;
常见的对arguments的操作有三个
- 获取参数的长度: arguments.length
- 根据索引值获取某一个参数: arguments[2]
- 获取当前arguments所在的函数: arguments.callee
5.5. arguments转成数组
-
for遍历arguments中所有的元素
function foo() { var newArr = [] for (let i = 0; i < arguments.length; i++) { newArr.push(arguments[i]) } return newArr } foo(1, 2, 3, 4, 5) -
用slice()将arguments转为数组
function foo() { // this最初是原型,后面被显示绑定为arguments var newArr = Array.prototype.slice.call(arguments) // var newArr= [].slice.call(arguments) 等同上面 console.log(newArr); } foo(1, 2, 3, 4, 5)疑惑_为什么slice可以实现该需求?下面是模拟Array中的slice函数
Array.prototype.hyslice = function (start, end) { let _this = this let newArr = [] start= start || 0 end= end || _this.length for (let i = start; i < end; i++) { newArr.push(_this[i]) } return newArr } function foo() { var newArr = Array.prototype.hyslice.call(arguments,1,3) console.log(newArr); } foo(1, 2, 3, 4, 5) -
Array.from()
function foo() { var newArr= Array.from(arguments) console.log(newArr); } foo(1, 2, 3, 4, 5) -
解构...
function foo() { var newArr= [...arguments] console.log(newArr); } foo(1, 2, 3, 4, 5)
5.6. 箭头函数里没有arguments
箭头函数是不绑定arguments的,所以我们在箭头函数中使用arguments会去上层作用域查找:
注意:window的全局作用域是没有arguments;node的全局是有arguments的
function bar(m,n){
return (a,b,c)=>{
console.log(arguments); // [10,20]
}
}
var fn=bar(10,20)
fn(1,2,3)
- 箭头函数没有arguments,但我们有需要拿到这些额外参数,可以用剩余运算符
var foo=(num1, num2, ...args)=>{
console.log(...args);
}
foo(1,2,3,4,5,6)