关于bind的兼容性处理,书中列出了如下代码:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== 'function') {
// 与 ECMAScript 5 最接近的
// 内部 IsCallable 函数
throw new TypeError(
'Function.prototype.bind - what is trying ' +
'to be bound is not callable'
);
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments))
);
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
代码的方法都认识,为什么组合到一起就搞不清是什么意思了呢?
我们先来看看MDN中对bind方法的解释:
Function实例的 bind()
方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this
关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
具体解释清参考MDN:Function.prototype.bind() - JavaScript | MDN
既然看不明白,那就打断点来看看吧:
首先,这里面的this很好理解,指向的是调用bind方法的方法,比如a.bind(b),指向的为a
让我迷惑的地方出现了
fBound = function () {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments))
);
};
起初不能理解,this instanceof fNOP && oThis ? this : oThis 这段三元运算是在干什么,后来通过资料了解(上文分享的MDN网址),在使用构造方法也就是new操作符去调用bind时,需要忽略this硬性绑定,也就是说指定的this无效,那再来看这段代码,如果this(注意,这里的this,不是上文例子中的a或者b,而是全局对象windows,请继续看下面例子)是fNOP的实例(通过new调用)并且有oThis(传进来需要绑定this的方法)时,apply绑定的this为windows,否则apply绑定bind传入的方法。
解决了一个语句,终于可以继续了,但是,下面这个语句简直是在迷惑人!
var aArgs = Array.prototype.slice.call(arguments, 1);
aArgs.concat(Array.prototype.slice.call(arguments))
什么?这是什么神操作呢,这两句代码如果单独运行是这样的
var arguments = ['this', 1, 2, 3];
var aArgs = Array.prototype.slice.call(arguments, 1); // 1,2,3
console.log('aArgs =', aArgs);
var bArgs = aArgs.concat(Array.prototype.slice.call(arguments)); // 1,2,3,'this',1,2,3
console.log('bArgs =', bArgs);
我的本能告诉我,这不对,肯定哪里有问题,我又想到,不能脱离环境而单独运行代码,这次我在调用的地方打断点,准备搞懂为什么要这样写
Function.prototype.bind = function (oThis) {
if (typeof this !== 'function') {
// 与 ECMAScript 5 最接近的
// 内部 IsCallable 函数
throw new TypeError(
'Function.prototype.bind - what is trying ' +
'to be bound is not callable'
);
}
console.log('this1 =', this);
console.log('arguments1 =', arguments);
var aArgs = Array.prototype.slice.call(arguments, 1);
var fToBind = this,
fNOP = function () {},
fBound = function () {
console.log('arguments2 =', arguments);
console.log('this2 =', this);
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments))
);
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
const module = {
x: 88,
getX: function (...data) {
console.log('data =', data);
return this.x;
},
};
const unboundGetX = module.getX;
const boundGetX = unboundGetX.bind(module, 1, 2, 3);
console.log(boundGetX('a', 'b'));
豁然开朗!代码中的两个arguments不一样,aArgs.concat(Array.prototype.slice.call(arguments))执行之后,是将两次传入的参数拼接在了一起,也就是代码中第一次传参为module, 1, 2, 3,第二次为'a', 'b',拼接之后为1, 2, 3, 'a', 'b' 又去查了下资料,这种将多入传参变为单入传参的方法叫函数柯里化。
到此我的疑惑基本都解决了,bind兼容性核心功能也搞明白了,最后两段代码模拟了Object.creat方法
fNOP = function () {},
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();