Node-JS专精08手写bind01

手写bind

确定API

  • bind 位于 Function.prototype 上
  • 需要做 polyfill

什么是polyfill

  • polyfill为何物
  • 聚填充(生活中)
    • 抹平
    • 补全
    • 你的桌子一个腿短,于是底下垫了一个纸片
    • 你的墙面不够平整,有坑,于是用白浆填平它
  • 对于前端
    • 你的JS代码运行在坑人的IE6下
    • 我们知道方法一般挂在Function.prototype上 也就是Function.prototype.bind而在 IE6没有,此时你就需要 polyfill

API

  • fn.bind(asThis)
  • fn.bind(asThis, param1, param2)
  • fn.bind(asThis)()
  • fn.bind(asThis, param1, param2)()
  • fn.bind(asThis)(param1)
  • fn.bind(asThis, param1, param2)(p3, p4)

bind API例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function aa(){
console.log(this);
console.log(arguments)
}

// fn.bind(asThis)
// fn.bind(asThis)()
var newFn = aa.bind({a:1})
newFn()
/*
{a:1}
arguments[]
*/

// fn.bind(asThis, param1, param2)
// fn.bind(asThis, param1, param2)()
var newFn2 = aa.bind({a:1},1,2)
newFn2()
/*
{a:1}
arguments[1,2]
*/


// fn.bind(asThis)(param1)
var newFn3 = aa.bind({a:1})
newFn3(1)
/*
{a:1}
arguments[1]
*/


// fn.bind(asThis,p1,p2)(p3,p4)
var newFn4 = aa.bind({a:1},1,2)
newFn4(3,4)
/*
{a:1}
arguments[1,2,3,4]
*/

001bind(asThis) 实现

  • 新建项目目录 bind_demo
  • 新建 bind_demo/src/index.js
1
2
3
4
5
6
7
8
9
10
11
function bind(asThis){
const fn = this
return function(){
return fn.call(asThis)
}
}
module.exports = bind;

if(!Function.prototype.bind){
Function.prototype.bind = bind;
}
  • 新建 bind_demo/test/index.js
1
2
3
4
5
6
7
8
9
10
const bind = require('../src/index');
Function.prototype.bind2 = bind;

console.assert(Function.prototype.bind2 !== undefined);

const fn1 = function(){
return this;
}
const newFn = fn1.bind2({name:'hjx'});
console.assert(newFn().name === 'hjx');

002bind(asThis,p1,p2) 实现

  • 通过扩展运算符

src/index.js

1
2
3
4
5
6
7
8
9
10
11
function bind(asThis, ...args){
const fn = this
return function(){
return fn.call(asThis, ...args)
}
}
module.exports = bind;

if(!Function.prototype.bind){
Function.prototype.bind = bind;
}

test/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const bind = require('../src/index');
Function.prototype.bind2 = bind;

console.assert(Function.prototype.bind2 !== undefined);

const fn1 = function(){
return this;
}
const newFn = fn1.bind2({name:'hjx'});
console.assert(newFn().name === 'hjx');

const fn2 = function(p1,p2){
return [this,p1,p2]
}

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

003bind(asThis,p1)(p2) 实现

  • step1通过newFn = fn.bind({name:'hjx'},1) 绑定this 传递参数
  • step2newFn(2) 在此传递参数调用
1
2
3
4
5
6
7
8
9
10
11
function bind(asThis, ...args){
const fn = this
return function(...args2){
return fn.call(asThis, ...args, ...args2)
}
}
module.exports = bind;

if(!Function.prototype.bind){
Function.prototype.bind = bind;
}

test/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const bind = require('../src/index');
Function.prototype.bind2 = bind;

console.assert(Function.prototype.bind2 !== undefined);

const fn1 = function(){
return this;
}
const newFn = fn1.bind2({name:'hjx'});
console.assert(newFn().name === 'hjx');

const fn2 = function(p1,p2){
return [this,p1,p2]
}

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


const anotherFn2 = fn2.bind2({name:'hjx'},123);
console.assert(anotherFn2(456)[0].name === 'hjx');
console.assert(anotherFn2(456)[1] === 123);
console.assert(anotherFn2(456)[2] === 456);

MDN bind的Polyfill实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (!Function.prototype.bind) (function(){
var slice = Array.prototype.slice;
Function.prototype.bind = function() {
var thatFunc = this, thatArg = arguments[0];
var args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - ' +
'what is trying to be bound is not callable');
}
return function(){
var funcArgs = args.concat(slice.call(arguments))
return thatFunc.apply(thatArg, funcArgs);
};
};
})();

改写我们的 bind

  • 为什么MDN 不用扩展运算符,因为它不能用和 bind 同时期的API

src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
var slice = Array.prototype.slice;
function bind(asThis){
var args = slice.call(arguments,1);
const fn = this
if(typeof fn !== 'function'){
throw new Error("bind 必须调用在函数身上");
}
return function(){
var args2 = slice.call(arguments,0);
return fn.apply(asThis, args.concat(args2))
}
}

ES5和ES6版 bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var slice = Array.prototype.slice;
// ES5版
function bind(asThis){
var args = slice.call(arguments,1);
const fn = this
if(typeof fn !== 'function'){
throw new Error("bind 必须调用在函数身上");
}
return function(){
var args2 = slice.call(arguments,0);
return fn.apply(asThis, args.concat(args2))
}
}

// ES6版
function _bind(asThis, ...args){
const fn = this
return function(...args2){
return fn.call(asThis, ...args, ...args2)
}
}