function_new (fn, ...args){ const obj = Object.create(fn.prototype) const res = fn.call(obj, ...args); returntypeof res === "object" ? res : obj; } functionPerson(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也可以 returntypeof 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
// 剩余参数的形式来接受实参 functionmax(...args){ returnMath.max(...args) } // 可以不给形参,在函数中arguments关键字用来储存实际传过来的参数列表 functionmin(){ returnMath.min(...arguments) } //实际参数的个数可以比形式参数个数多 functionsum(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; functionfoo(a, b){ console.log(a, b); console.log(this.age); } foo(1, 2);
函数直接调用
1 2 3 4 5 6
var age = 19; functionfoo(a, b){ console.log(a, b); console.log(this.age); } foo.call({age:20},1,2)
函数通过_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 instanceofArray){ res = context.fn(...arrArg) } delete fn; return res; }
1 2 3 4 5 6
var age = 19; functionfoo(a, b){ console.log(a, b); console.log(this.age); } foo._apply({age:20},[1,2])
函数通过_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"){ thrownewError(`${that} must be function`) } let args = [...arguments].slice(1); returnfunction (){ 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:"张三" } functionfoo(a,b){ console.log(a,b) console.log(this.name) return { name:this.name, a, b } } foo._bind(obj,1,2)(3);