We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
现在的函数,原型上就有 bind 方法,但是如果我们在不支持该方法的浏览器上,要怎么来实现呢?
bind
简单的bind无非就是使用 call或者apply,然后返回一个函数
call
apply
要点:是绑定后的函数与被绑定的函数在同一条原型链上
// 1 Function.prototype.bind = function (context) { let self = this; return function () { return self.apply(context, arguments) } } // 2 Function.prototype.bind = function (cxt) { var args = Array.prototype.slice.call(arguments, 1), self = this; return function () { let innerArgs = Array.prototype.slice.call(arguments) let finalArgs = args.concat(innerArgs) return self.apply(cxt, finalArgs) } } // 3 Function.prototype.bind = function (cxt) { let args = Array.prototype.slice(arguments, 1), F = function () { }, self = this, bound = function () { let innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs) return self.apply((this instanceof F ? this : cxt), finalArgs) } // // 维护原型关系 F.prototype = this.prototype; bound.prototype = new F(); return bound } // 4 Function.prototype.bind = Function.prototype.bind || function (oThis) { // 偏函数 if (typeof this !== "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"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () { }, //用于原型继承,防止直接引用被修改原型 fBound = function () { /** * this instanceof fNOP 为了防止以下的情况,例如 Bloomer 是调用 bind 函数返回的函数的实例 * 和setTimeout一起使用 * 一般情况下setTimeout()的this指向window或global对象。 * 当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。 * function Bloomer() { * this.petalCount = Math.ceil(Math.random() * 12) + 1; * } * * Bloomer.prototype.bloom = function() { * window.setTimeout(this.declare.bind(this), 1000); * } * * Bloomer.prototype.declare = function() { * console.log('我有 ' + this.petalCount + ' 朵花瓣!'); * } */ return fToBind.apply(this instanceof fNOP // 这里的判断为 true 的情况见下面,这时候的 this 就是 fBound 函数 ? this : oThis || this, // 为false 的情况就是上面所讲的 setTimeout 情况,这时调整 this 指向,没有指定的就直接为 this aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; } function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return this.x + ',' + this.y; }; var p = new Point(1, 2); p.toString(); // '1,2' var emptyObj = {}; var YAxisPoint = Point.bind(emptyObj, 0 /*x*/ ); YAxisPoint.prototype.aaa = 66666666 console.log(YAxisPoint.prototype) console.log(Point.prototype) // this instanceof fNOP var YAxisPoint = Point.bind(null, 0/*x*/); var axisPoint = new YAxisPoint(5); axisPoint.toString(); // '0,5' axisPoint instanceof Point; // true axisPoint instanceof YAxisPoint; // true
那么underscore 里面是怎么实现的呢?
// 决定一个函数是作为构造函数还是接受参数的普通函数 var executeBound = function (sourceFunc, boundFunc, context, callingContext, args) { // 这里的和上文的 this instanceof fNOP == false 一样 if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args) // 下面的几步骤,基本和 new 一个构造函数的过程一样,详见如下: // https://github.com/jyzwf/blog/issues/27 // 获取继承sourceFun原型的一个对象 var self = baseCreate(sourceFunc.prototype) // 类似上文的 fNOP // 执行构造函数,并将里面的 this 指向 self ,返回结果 var result = sourceFunc.apply(self, args) // 如果 返回的结果是一个对象,就直接返回该对象 if (_.isObject(result)) return result // 否则返回之前 new 出来的对象 return self } _.bind = restArgs(function (func, context, args) { /** * startIndex = 2 * 返回如下函数 * function () { var length = Math.max(arguments.length - startIndex, 0), // 防止startIndex 过大 rest = Array(length), index = 0; for (; index < length; index++) { // 将从startIndex开始的后面参数放入数组 rest[index] = arguments[index + startIndex] // 从args 开始收集参数 } switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); // 在_.invoke()使用 case 2: // 调用这个 case return func.call(this, arguments[0], arguments[1], rest); } } */ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function') var bound = restArgs(function (callArgs) { // 收集剩余函数 /** * startIndex = 0 * * function () { var length = Math.max(arguments.length - startIndex, 0), // 防止startIndex 过大 rest = Array(length), index = 0; for (; index < length; index++) { // 将从startIndex开始的后面参数放入数组 rest[index] = arguments[index + startIndex] // 从args 开始收集参数 } switch (startIndex) { case 0: // 调用这个 case return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); // 在_.invoke()使用 case 2: return func.call(this, arguments[0], arguments[1], rest); } } */ return executeBound(func, bound, context, this, args.concat(callArgs)) }) return bound })
这里我们看到出现了两个 restArgs函数,这个函数在 underscore里面很重要,我们看看他长什么样 ?
restArgs
underscore
// 类似es6的剩余参数集合 var restArgs = function (func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex return function () { var length = Math.max(arguments.length - startIndex, 0), // 防止startIndex 过大 rest = Array(length), index = 0; for (; index < length; index++) { // 将从startIndex开始的后面参数放入数组 rest[index] = arguments[index + startIndex] } // switch 干嘛的????????????? switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); // 在_.invoke()使用 case 2: return func.call(this, arguments[0], arguments[1], rest); } // 收集startIndex 之前的参数,并将 startIndex 开始的后面的参数包装为一个数组使用 var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index] } // 将args的最后一个参数设置为之前的 rest args[startIndex] = rest; return func.apply(this, args) } }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
现在的函数,原型上就有
bind
方法,但是如果我们在不支持该方法的浏览器上,要怎么来实现呢?简单的
bind
无非就是使用call
或者apply
,然后返回一个函数那么underscore 里面是怎么实现的呢?
这里我们看到出现了两个
restArgs
函数,这个函数在underscore
里面很重要,我们看看他长什么样 ?The text was updated successfully, but these errors were encountered: