call apply bind new的手写实现,带有测试用例和注释
不熟悉this指向的问题,可以先看:this绑定的四种方式:new,显式,隐式,默认
call
...
<script src="./fn.js"></script>
<script>
// 普通函数
function test(age) {
console.log(this.name + " " + age);
}
// 自定义对象
var obj1 = {
name: '小明'
}
var obj2 = {
name: '小李'
}
// 调用函数的 _Call 方法
test._Call(obj1, 11)
test._Call(obj2, 22)
</script>
...
// this 为调用的函数
// context 是参数对象
Function.prototype._Call = function (context) {
context = context || window;
/** 1.将this作为context的一个属性,由于隐式绑定,this会指向context,
所以能够f访问到context对象内的属性 **/
context.f = this;
args = Array.from(arguments).slice(1); //除context的其它参数
//2.把参数传入调用对象
let result = context.f(...args);
//3.删除context.f
delete context.f;
//4.返回执行结果
return result;
};
apply
<script src="./fn.js"></script>
<script>
// 普通函数
function test(age) {
console.log(this.name + " " + age);
}
// 自定义对象
var obj = {
name: '小明'
}
test._Apply(obj, [22])
</script>
// this 为调用的函数
// context 是参数对象
Function.prototype._Apply = function (context,args) {
context = context || window
/** 1.将this作为context的一个属性,由于隐式绑定,this会指向context,
所以能够f访问到context对象内的属性 **/
context.f = this
//2.把参数传入调用对象
let result = context.f(...args)
//3.删除context.f
delete context.f
//4.返回执行结果
return result
};
bind
<script src="./fn.js"></script>
<script>
// 普通函数
function test() {
//这里的bind接收函数,如果是直接调用,this指向了obj,打印出小明;
//如果是new出来的函数调用,this指向test(),打印undefined
console.log(this.name,'this.name');
console.log([...arguments]);
}
// 自定义对象
var obj = {
name: '小明'
}
let F = test._Bind(obj, 1, 2, 3);
console.log(F,'F');
//直接调用方式,this指向obj
F(5, 6); //这里有柯里化,相当于test._Bind(obj, 1, 2, 3)(5, 6)
// new方式
// new出来的bind接收函数,不会改变this指向,只会带上bind()里除第一个参数外的参数;这里依旧指向test()
let newObj = new F(7, 8);
// console.log(newObj,'newObj');
</script>
// this 为调用的函数
// context 是参数对象
Function.prototype._Bind = function (context) {
// 判断调用者是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 截取传递的参数
const args = Array.from(arguments).slice(1);
// 用_this保存this,因为调用bind接收函数时,执行环境变了
const _this = this;
console.log(_this, "_this"); // 调用bind的函数
// 返回一个函数等待接收
return function F() {
// 返回了一个函数,可以直接调用F(),也可以 new F(),所以需要判断
// 对于 new 的情况来说,不会被任何方式改变 this
if (this instanceof F) {
console.log(this, "this"); // 这里的this指向F
return new _this(...args, ...arguments);
} else {
console.log(this, "this"); // 这里的this指向window
//直接调用,则把this指向context
//这里的arguments是接收bind的函数所传的参数(柯里化)
return _this.apply(context, args.concat(...arguments));
}
};
};
new
<script src="./fn.js"></script>
<script>
function Foo() {
this.name = '小明'
this.arg = arguments[0]
}
Foo.prototype.callName = function () {
console.log(this.name)
}
// 测试
let test = _New(Foo, '学生', '男')
test.callName()
console.log(test)
</script>
function _New(Ctor) {
//==>1.创建一个新的对象(未来的实例对象)
let obj = {};
//==>2.将构造函数的prototype和实例对象的__proto__建立联系
obj = Object.create(Ctor.prototype); //给实例对象指定__proto__为Ctor.prototype
// 相当于obj.__proto__ = Ctor.prototype
let args = Array.prototype.slice.call(arguments, 1); // 获取除去Ctor之外的参数
// 相当于 let args = Array.from(arguments).slice(1);
//==>3.改变构造函数的this指向,指向实例对象;因此,实例对象obj就可以访问到构造函数内的属性和方法了
let result = Ctor.call(obj, ...args);
console.log(result, "result");
//==>4.返回实例对象
// 判断构造函数是否存在返回值,且返回值为引用类型(基本类型无效),
// 如果不存在则返回创建的新对象obj
return typeof result === "object" || result instanceof Function
? result
: obj;
}