bind的思考以及实现

思考

  • 需要理解bind是位于Function.prototype上的
  • polyfill:polyfill 在英文中有垫片的意思,意为兜底的东西。在计算机科学中,指的是对未能实现的客户端上进行的"兜底"操作。打补丁在前端er是件习以为常的事情,结合笔者日常工作经验,总结出3种打补丁方式
  1. 手动打补丁:在石器时代,我们是手动导入所需的补丁,以 ES6 的 object#assign 为例 ,即使在 IE 11上,仍会报错。所以我们需要打上相应的补丁。可以用第三方成熟的package ,也可以使用 MDN 提供的模板进行打补丁。问题是解决了,但优势和劣势也相当明显:优势是保持最小化引入,不会有额外的冗余代码开销,保证了应用的性能。劣势是手动导入不易管理和维护,对于多样化的 polyfill 和变化多端的 Web 应用维护成本比较大
  2. 根据覆盖率自动打补丁
  3. 根据浏览器特性,动态打补丁:以上两种方法都有一个弊端——补丁的冗余。以 Object#assign 来说,在支持这个特性的浏览器来说,就没必要引入这个补丁,势必造成了一定的补丁冗余,这就有了根据浏览器特性动态打补丁的方案。
    Polyfill.io 就是实现这个方案的服务,它会根据浏览器的UA不同,返回不一样的补丁。如想要 Promise 补丁,在页面引入
  • 要实现的api:
  1. fn.bind(asThis)
  2. fn.bind(asThis,param1,param2)
  3. fn.bind(asThis)()
  4. fn.bind(asThis,param1,param2)()
  5. fn.bind(asThis)(param1)
  6. fn.bind(asThis,param1,param2)(p3,p4)

进阶思考

  • 新语法的写法,不支持旧的api
  • 旧版的写法需要支持new
  • 还需要了解原型链
  • new的实际转换过程
new Fn()
//第一步
var tmp = {};
//第二步
tmp._proto_=fn.prototype
//第三步
fn.call(tmp,'x');
//第四步
return this 

代码实现

//老语法实现的bind
var slice = Array.prototype.slice;
function bind(asThis){
  //this就是函数
  var args = slice.call(arguments,1);
  var fn = this;
  if(typeof fn !== 'function'){
    throw new Error('bind 必须调用在函数身上');
  }
  function resultFn(){
    var args2 = slice.call(arguments,0);
    return fn.apply(
      resultFn.prototype.isPrototypeOf(this) ? this : asThis,
      args.concat(args2)
    );
  }
  resultFn.prototype = fn.prototype;
  return resultFn;
}

//用新语法实现的bind,简洁明了
function _bind(asThis,...args){
  //this 就是函数
  const fn = this;
  function resultFn(...args2){
    return fn.call(this instanceof resultFn? this : asThis,...args,...args2);
  }
  resultFn.prototype = fn.prototype;
  return resultFn;
}
if(!Function.prototype.bind){
  Function.prototype.bind = _bind;
}
module.exports = _bind;

测试案例

const bind = require("../src/index");

test1("fn.bind 能用");
test2("this 绑定成功");
test3("this, p1, p2 绑定成功");
test4("this, p1 绑定成功,后传 p2 调用成功");
test5("new 的时候绑定了 p1, p2");
test6("new 的时候绑定了 p1, p2,并且 fn 有 prototype.sayHi");
test7("不用 new 但是用类似的对象");

function test1(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  console.assert(Function.prototype.bind2 !== undefined);
}

function test2(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn1 = function() {
    return this;
  };

  const newFn1 = fn1.bind2({ name: "frank" });
  console.assert(newFn1().name === "frank");
}

function test3(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn2 = function(p1, p2) {
    return [this, p1, p2];
  };

  const newFn2 = fn2.bind2({ name: "frank" }, 124, 456);
  console.assert(newFn2()[0].name === "frank", "this");
  console.assert(newFn2()[1] === 124, "p1");
  console.assert(newFn2()[2] === 456, "p2");
}

function test4(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn2 = function(p1, p2) {
    return [this, p1, p2];
  };
  const anotherFn2 = fn2.bind2({ name: "frank" }, 123);
  console.assert(anotherFn2(245)[0].name === "frank", "this");
  console.assert(anotherFn2(245)[1] === 123, "p1");
  console.assert(anotherFn2(245)[2] === 245, "p22");
}

function test5(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn = function(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
  };
  const fn2 = fn.bind2(undefined, "x", "y");
  const object = new fn2();
  console.assert(object.p1 === "x", "x");
  console.assert(object.p2 === "y", "y");
}

function test6(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn = function(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
  };
  fn.prototype.sayHi = function() {};
  const fn2 = fn.bind2(undefined, "x", "y");
  const object = new fn2();
  console.assert(object.p1 === "x", "x");
  console.assert(object.p2 === "y", "y");
  // console.assert(object.__proto__ === fn.prototype);
  console.assert(fn.prototype.isPrototypeOf(object));
  console.assert(typeof object.sayHi === "function");
}

function test7(message) {
  console.log(message);
  Function.prototype.bind2 = bind;
  const fn = function(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
  };
  fn.prototype.sayHi = function() {};
  const object1 = new fn("a", "b");
  const fn2 = fn.bind2(object1, "x", "y");
  const object = fn2(); // 没有new
  console.assert(object === undefined, "object 为空");
  console.assert(object1.p1 === "x", "x");
  console.assert(object1.p2 === "y", "y");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaobangsky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值