今天看完手写bind
真的是寄了。call
和 apply
说实话还好,这bind
到底是什么寄吧。
上一段完整代码:
在__proto__
被不推荐使用的情况下,支持检测new
的手写bind
代码如下:
先别看这部分代码,建议先看最下面的几种实现。
Function.prototype.my_bind = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1)
function Fun() { }
var fnBound = function () {
var _this = this instanceof self ? this : thisArg
return self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));
}
Fun.prototype = this.prototype
fnBound.prototype = new Fun()
return fnBound
}
function foo(name) {
this.name = name;
}
var obj = {};
// 解决 上下文
var bar = foo.my_bind(obj);
bar('jack')
console.log(obj.name); // jack
// 解决 参数问题
var tar = foo.my_bind(obj, 'rose')
tar()
console.log(obj.name); // rose
// 解决 new问题
var alice = new bar('alice')
console.log(obj.name); // rose
console.log(alice.name); // alice
为什么中间要加一层原型链关系,不是说加一层,这里其实是加了两层,你不要听我解释,先看最底下的代码实现,先别看这个!!!!:
- 仔细看一下代码,
bar
其实是手写的bind
返回出来的一个闭包fnBound
,然后bar
作为构造函数new
了一个alice
。 - 请回答:
alice
的__proto__
指向哪里?——答案是bar.prototype
,实际上是fnBound.prototype
,然后fnBound.prototype.__proto__
指向哪里?——答案是他的构造函数的原型对象,即Fun.prototype
,好,到这里仔细看上面的这一行代码,Fun.prototype = this.prototype
,请问这么做的目的到底是为什么?——答案是为var _this = this instanceof self ? this : thisArg
这一行做铺垫,重点是this instanceof self
的实现。 - 如果我们不做任何处理,上面这行的
this
和self
不会有任何关系,instanceof
的值一定是false
,就没有效果了对吧。 - 实际支持
var alice = new bar('alice') 以及 var _this = this instanceof self ? this : thisArg
的那一部分代码(instanceof
)到底怎么运作的呢,听我解释: -
new
出来的实例alice
就是this
,不懂为什么的话就去看我那篇《手写new
》,然后这个this
往上找自己的__proto__
,先找到了this.__proto__
,这个的值===fnBound.prototype
,因为fnBound
就是this实例
的构造函数,但是仍不等于self.prototype
,所以继续找下一层__proto__
,找到了fnBound.prototype.__proto__ === self.prototype
,因为Fun
是fnBound.prototype
的构造函数,然后Fun.prototype
不是原生的值,而是被我们手动赋值为了self.prototype
,不知道为什么这样找的话,就去看看instanceof
的实现原理。 - 好了,现在这个
instanceof
的值为true
了,我们需要的_this
指定为了当前实例的this
,这样执行的代码才满足我们的要求 - 加入值为
false
,_this
的值为thisArg
,因为闭包,thisArg
没有被销毁,而是一支存活,每次调用都能取到,就会又给obj
执行相关代码,结果就会是obj.name === 'alice'
我脑子有病才像上面这么写。
我为什么不这么写:
Function.prototype.my_bind = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1)
function Fun() { }
var fnBound = function () {
// !!!!!!!看这里
var _this = this instanceof fnBound ? this : thisArg
return self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));
}
Fun.prototype = this.prototype
fnBound.prototype = new Fun()
return fnBound
}
我都知道self.prototype
在fnBound
的原型链上了,如果alice
实例是fnBound
创建的,那fnBound
一定在alice
的原型链上啊,那self
也就是foo
肯定更在啊,为什么要非得找两层呢,寄。看博客都能看傻。
但是究其本质,看过new
实现原理的应该都懂,这都是对__proto__
这个属性的操作,其直接就影响到原型链。
我们完全可以这么写:
Function.prototype.my_bind = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1)
function Fun() { }
var fnBound = function () {
var _this = this instanceof self ? this : thisArg
return self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));
}
// 看这里
fnBound.prototype.__proto__ = this.prototype
return fnBound
}
self.prototype
既然在fnBound.prototype
的原型链上了,那也就在实例对象的原型链上。
但是现在不推荐直接写__proto__
,所以采用new Fun()
这种不明显的方法来实现。
其实可以不操作__proto__
:
Function.prototype.my_bind = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1)
function Fun() { }
var fnBound = function () {
var _this = this instanceof self ? this : thisArg
return self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));
}
// 看这里 据说这样写有问题
fnBound.prototype = this.prototype
// 而网上的博客会这样写 效果上没有什么区别 这两种写法暂时都没看出问题
fnBound.prototype = Object.create(this.prototype)
return fnBound
}
instanceof
的第一次寻找就可以返回true
了
手写instanceof
和new
以及原型链相关的解释都在我的博客里面。
寄了。
然后解释每一段代码:
-
if (typeof this !== 'function') { throw new TypeError('not a function') }
如果不是函数那就直接抛出错误,不是函数绑定啥绑定 -
var self = this;
将调用的函数保存一下this
,会在下面的闭包里当做常量使用var args = Array.prototype.slice.call(arguments, 1)
将绑定目标以外的参数都取出来,后面要和调用时得到的参数列表进行拼接 -
var fnBound = function () {
var _this = this instanceof self ? this : thisArg
判定new
操作,如果是new
出来的实例,那就直接用当前this
,否则用之前引用的外层函数的变量thisArg
,也就是绑定目标return self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));
返回一个函数,可以在外部手动执行,绑定目标和参数都已经处理好了}
-
function Fun() { }
声明一个空函数,用于链接原型链fnBound.prototype = Object.create(this.prototype)
赋值一个self
的prototype
给fnBound.prototype
,把原型链链接起来return fnBound
返回可执行函数