Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function.prototype.bind()实现 #5

Open
lynnic26 opened this issue Sep 3, 2017 · 0 comments
Open

Function.prototype.bind()实现 #5

lynnic26 opened this issue Sep 3, 2017 · 0 comments
Assignees

Comments

@lynnic26
Copy link
Owner

lynnic26 commented Sep 3, 2017

定义

MDN上对bind()函数的定义

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

这个定义充分的体现了翻译的苍白无力,再看英文

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

似乎也很迷
没关系,我来翻译成人话

  • bind()方法创建一个新函数,并返回,这个新函数也能在后续被调用
  • 当这个函数被调用时,它会有一些外界提供的设置项(即参数)传进来
  • 这些设置项有两部分功能
    1. 把原来的this关键字设置成想要的值
    2. 新函数被调用时,另一部分设置项会预先作为它的参数传进来

更直观的Syntax描述

fun.bind(thisArg[, arg1[, arg2[, ...]]])

从中可以看出thisArg是必选项,
arg1, arg2, ...是可选项
它的返回值是A copy of the given function with the specified this value and initial arguments.
这样就很清楚了

举个栗子

var altwrite = document.write;
altwrite("hello");

你会得到一个异常Uncaught TypeError: Illegal invocation
这是因为当函数不作为对象的属性被调用时,此时的this总是指向全局对象
当document.write被赋值给一个变量的时候,它的this就不再是document了
这时大家就会想,我可以call啊,可以apply啊

var altwrite = document.write;
altwrite.call(document, 'hello'); 

是的,这当然很可以,但是我就是不想改参数列表,想单独调用alwrite('hello') 这句呢,所以有

var altwrite = document.write.bind(document);
altwrite("hello");

这时我们就能看出call、apply和bind的差异了:
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

Function.prototype.bind的实现方式

大部分高级浏览器都实现了内置的bind,IE是>=9的版本才支持,在没有原生的Function.prototype.bind的引擎内,我们可以模拟一个。

低配实现

Function.prototype.bind = function(context) {
  var self = this;
  return function() {  // 返回一个新函数
    return self.apply(context, arguments); // 这个arguments是func()时候传入的参数列表
  }
};

var obj = {
  name: 'Lynnic'
}
var func = (function (a, b) {  
  console.log(this.name);
  console.log([a, b]);
}).bind(obj);
func(1, 2);  // 对应上面的arguments

这时的bind函数才只能传一个上下文参数,还没有实现给新函数预先传入参数列表的功能

进化一点

把bind的参数从一个增加到多个,第一个参数为要绑定的上下文,其余为预先填入的新函数的参数
最后会把这部分参数和调用新函数时用户传入的参数组合成一个新的参数列表。

Function.prototype.bind = function() {
  var self = this;
  // 返回第一个参数,既要绑定的上下文,arguments的长度会改变
  var context = [].shift.call(arguments);
  // 将剩余的参数转化为普通数组,slice方法不会修改原对象,只会返回一个子数组
  var args = [].slice.call(arguments);
  return function() {
    // 组合两次分别传入的参数,作为新函数的参数
    // 也可以直接用 args.concat([].slice.call(arguments))
    return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
  }
}

var obj = {
  name: 'Lynnic'
}
var func = (function (a, b, c, d) {
  console.log(this.name);
  // 请注意传入参数的顺序
  console.log([a, b, c, d]);
}).bind(obj, 1, 2);
func(3, 4);

Referrence

JS中的call、apply、bind方法详解
MDN Function.prototype.bind()
Javascript设计模式和开发实践

@lynnic26 lynnic26 changed the title Function.prototype.bind实现 Function.prototype.bind()实现 Sep 3, 2017
@lynnic26 lynnic26 self-assigned this Sep 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant