ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JS 手写bind 我写bug的能给你看生代码段子?

2021-11-12 23:02:17  阅读:155  来源: 互联网

标签:function bind self fnBound JS var new prototype 看生


今天看完手写bind真的是寄了。
callapply说实话还好,这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的实现。
  • 如果我们不做任何处理,上面这行的thisself不会有任何关系,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,因为FunfnBound.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.prototypefnBound的原型链上了,如果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

手写instanceofnew以及原型链相关的解释都在我的博客里面。

寄了。

然后解释每一段代码:

  • 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) 赋值一个selfprototypefnBound.prototype,把原型链链接起来
    return fnBound 返回可执行函数

标签:function,bind,self,fnBound,JS,var,new,prototype,看生
来源: https://blog.csdn.net/weixin_45543674/article/details/121297084

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有