#bind特征#
由第一章总结了bind函数有以下几点特征:
- bind返回的是一个改变了this指向的函数
- bind传入的也是参数列表,但是可以多次传入
- 当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
- 原函数的原型链上的属性,new出来的结果也可以找到
#代码示例#
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
const bindFun = bar.bind(null, 'zhangguan');
// bind 返回的函数 作为普通函数调用
// 输入
let a = bindFun('29');
console.log(a)
// 输出
{
value: 2,
name: 'zhangguan',
age: '29'
}
// bind 返回的函数 作为构造函数调用,绑定的 this 值obj会失效,this指向实例对象a
// 输入
let a = new bindFun('29');
console.log(a)
// 输出
{
value: undefined,
name: 'zhangguan',
age: '29'
}
#bind实现步骤#
1、定义一个bind函数,并return 返回一个函数
// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
// bind返回的函数
return function (...innerArgs) {
// bind 返回的函数 作为普通函数被执行
}
}
2、bind传入的也是参数列表,但是可以多次传入
// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
let ctx = context || window;
let fn = Symbol();
ctx[fn] = this;
// bind返回的函数
return function (...innerArgs) {
// 对外层参数和后续追加参数进行拼接,外层参数在前
return ctx[fn](...[...args,...innerArgs]);
}
}
3、 此时基本上快满足bind的实现,但是还有最后一点特征:当 bind 返回的函数,使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效,目前第二步代码执行后,通过以下两个示例调用发现this的指向并没有失效,value值不等于undefined:
const bindFun = bar.bind(null, 'zhangguan');
// 函数调用
let a = bindFun('29');
// 输出
{
value: 2,
name: 'zhangguan',
age: '29'
}
// 使用new作为构造函数
let a = new bindFun('29');
// 输出
{
value: 2,
name: 'zhangguan',
age: '29'
}
如何判断是new作为构造函数的场景,可以通过如下代码:
// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
let cxt = context || window;
let fn = Symbol();
cxt[fn] = this; // this:Bar
// bind返回的函数
var fbound = function (...innerArgs) {
if(this instanceof fbound) { // this:a (new出来的实例对象)
// 为实例对象a添加Person方法
this.fn = cxt[fn];
// 实例对象a执行Person方法
return this.fn(...[...args,...innerArgs]);
} else {
// 对外层参数和后续追加参数进行拼接,外层参数在前
return cxt[fn](...[...args,...innerArgs]);
}
};
return fbound;
}
4、 原函数的原型链上的属性,new出来的结果也可以找到,最终代码实现如下:
// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
let cxt = context || window;
let fn = Symbol();
cxt[fn] = this; // this:Bar
// Object.create()原理,创建一个空白函数来做中间件
function Fn() {};
// bind返回的函数
var fbound = function (...innerArgs) {
if(this instanceof fbound) { // this:a (new出来的实例对象)
// 为实例对象a添加Person方法
this.fn = cxt[fn];
// 实例对象a执行Person方法
return this.fn(...[...args,...innerArgs]);
} else {
// 对外层参数和后续追加参数进行拼接,外层参数在前
return cxt[fn](...[...args,...innerArgs]);
}
};
Fn.prototype = this.prototype;
// 通过原型链找到原函数中的属性
fBound.prototype = new Fn();
return fbound;
}