new,blind,call,apply实现

new实现

  • 创建一个对象
  • 将构造函数的作用域赋给新对象
  • 给新对象添加属性
  • 返回新对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 1.创建一个空对象
* 2.改变this的指向
* 3.给新对象新增属性
* 4.返回新对象
*/
function MyNew() {
//创建一个空对象
let newObj = {},
//获取传入的构造函数
targetConstructor = [].shift.call(arguments);
//实例继承 通过create 可以避免newObj和targetConstructor共享引用
newObj = Object.create(targetConstructor.prototype);
//借用外部传入的构造器给obj设置属性
const ret = targetConstructor.apply(newObj, arguments);
//避免null的情况返回
return typeof ret === "object" ? ret || newObj : newObj;
}

call

  • 将函数设为对象的属性
  • 执行该函数
  • 从对象上删除该函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 1. 将函数设置为对象的属性
* 2. 执行该对象上新增的函数
* 3. 删除该属性
*/
Function.prototype.myCall = function (context = window) {
context.fn = this;
const args = [];
for (let i = 1, len = arguments.length; i < len; i++) {
//执行后 args为 ["arguments[1]", "arguments[2]", "arguments[3]"]
args.push("arguments[" + i + "]");
}
//eval()中的参数必须为字符串,才会计算成JS代码
const result = eval("context.fn(" + args + ")");
delete context.fn;
return result;
};

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*
* apply和call区别在于第二个参数格式不同
*/
Function.prototype.myApply = function (context = window, arr) {
context.fn = this;
let result;
if (!arr) {
result = context.fn();
} else {
for (let i = 0, len = arr.length; i < len; i++) {
args.push("arr[" + i + "]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
};

bind

  • bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
  • 传入一个绑定的对象和参数
  • 返回一个函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 1. 传入一个对象和参数
* 2. 更改this的指向到调用的函数上,如果是new绑定的,则绑定到实例化的对象上
*/
Function.prototype.myBind = function (context) {
//调用bind的函数
const self = this;
//调用bind时传入的参数
const argument = [].slice.call(arguments, 1);

let fBind = function () {
//调用函数时或实例化时传入的参数
const args = [].slice.call(arguments);
// 作为构造函数时,this不能再指向context 此处的this指向实例
// 如果不是当做构造函数,那么将调用bind的函数指向context
self.apply(this instanceof fBind ? this : context, argument.concat(args));
};

// 将函数fBind的prototype指向调用bind的函数,这样就可以继承到绑定函数原型中的值
const fNOP = function () {};
fNOP.prototype = this.prototype;
fBind.prototype = new fNOP();

return fBind;
};

var foo = {
value: 1,
};

function bar(name, age) {
this.habit = "shopping";
console.log(this.value, "实例化的时候,无法获取到该值");
console.log(name);
console.log(age);
}
// 此处为原型上的属性,如果没有更改绑定函数的prototype的话,无法获取到该值
bar.prototype.friend = "kevin";

var bindFoo = bar.myBind(foo, "daisy");
const obj = new bindFoo("18");
console.log(obj.friend);
console.log(obj.habit);
//undefined 实例化的时候,无法获取到该值
//daisy
//18
//kevin
//shopping

参考资料

再此感谢冴羽大佬的深入系列