new call apply bind方法的实现原理

new的实现

es6的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function _new (fn, ...args){
const obj = Object.create(fn.prototype)
const res = fn.call(obj, ...args);
return typeof res === "object" ? res : obj;
}
function Person(name ,age){
this.age = age;
this.name = name;
}
Person.prototype.say = function(){
console.log("my name is " + this.name + ",我" + this.age + "岁了。");
}
const p = _new(Person, "tom", 19)
console.log(p)
p.say()

es5的写法

1
2
3
4
5
6
7
8
9
10
11
12

function __new(){
const fn = Array.prototype.shift.call(arguments);
const obj = Object.create(fn.prototype)
const res = fn.call(obj, ...arguments);
// const res = fn.apply(obj, arguments);//用apply也可以
return typeof res === "object" ? res : obj;
}
const p2 = __new(Person, "job", 59)
console.log(p2)
p2.say()

call的实现

先看JavaScript中函数传参的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 剩余参数的形式来接受实参
function max(...args){
return Math.max(...args)
}
// 可以不给形参,在函数中arguments关键字用来储存实际传过来的参数列表
function min(){
return Math.min(...arguments)
}
//实际参数的个数可以比形式参数个数多
function sum(a, b){
// js中函数的实际参数是可以比形参个数多的,此时只会接受配到的前几个参数
return a + b;
}
let res = max(1,2,3,5)
let res2 = min(1,2,3,5)
let res3 = sum(1,2,3,5)

在Function的原型上添加_call方法来模拟原生的call方法:

1
2
3
4
5
6
7
// 这里的_call方法只提供了一个默认的形参,用来存储第一个参数也就是需要把目标函数中this指向的那个对象
Function.prototype._call = function(context=window){
context.fn = this;//this指向调用_call方法的函数体
let res = context.fn(...[...arguments].slice(1));
delete context.fn;
return res;
}

测试:

1
2
3
4
5
6
var age = 19;
function foo(a, b){
console.log(a, b);
console.log(this.age);
}
foo(1, 2);
函数直接调用
函数直接调用
1
2
3
4
5
6
var age = 19;
function foo(a, b){
console.log(a, b);
console.log(this.age);
}
foo.call({age:20},1,2)
函数通过_call方法进行调用
函数通过_call方法进行调用

结论

显然call方法能够改变函数中this的指向,而且call方法不会改变原来函数的返回值。

apply的实现

apply实现原理和call和很相似,只是参数的形式不同。

1
2
3
4
5
6
7
8
9
10
11
Function.prototype._apply = function(context=window){
console.log(arguments)
context.fn = this;
const arrArg = arguments[1]
let res = undefined;
if(arrArg && arrArg instanceof Array){
res = context.fn(...arrArg)
}
delete fn;
return res;
}
1
2
3
4
5
6
var age = 19;
function foo(a, b){
console.log(a, b);
console.log(this.age);
}
foo._apply({age:20},[1,2])
函数通过_apply方法进行调用
函数通过_apply方法进行调用

bind的实现

1
2
3
4
5
6
7
8
9
10
11
Function.prototype._bind = function(context=window){
var that = this;
if(typeof that !== "function"){
throw new Error(`${that} must be function`)
}
let args = [...arguments].slice(1);
return function (){
let bindArgs = [...arguments]
return that.apply(context,args.concat(bindArgs));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
name:"张三"
}
function foo(a,b){
console.log(a,b)
console.log(this.name)
return {
name:this.name,
a,
b
}
}
foo._bind(obj,1,2)(3);
函数通过_bind方法进行调用
函数通过_bind方法进行调用

new call apply bind方法的实现原理
https://zbdev.online/2023/03/09/new-call-apply-bind方法的实现原理/
作者
zzb
发布于
2023年3月9日
许可协议