var context = {id: 12}; function fun (name, age) { console.log(this.id, name, age) }
bind
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
原生 bind
1. var a = fun.bind(context, 'bind'); 2. a(1); // 12, 'bind', 1 :作为普通函数 3. new a('1'); // undefined, 'bind', '1' :作为构造函数
以上例子可以看出
bind作为普通函数使用时,改变的this的指向,指向context对象;fun函数执行了;传入参数不确定,可以在第一步bind传值,也可以在第二步执行函数fun传值。
bind最为构造函数使用时,不同点是this指向实例的对象。
bind的实现代码
Function.prototype.mybind = function (context) { if (this && this.constructor !== Function) // 抛错 throw new Error("Function.prototype.mybind - what is trying to be bound is not callable"); // this =>绑定函数 fun var self = this;
// 获取mybind函数从第二个参数到最后一个参数 var arg = Array.prototype.slice.call(arguments, 1); function fbound() { // 普通函数: this => window 构造函数: this => 实例对象
// 获取mybind返回函数传入的函数 var args = Array.prototype.slice.call(arguments); self.apply(this instanceof self ? this : context, arg.concat(args)); } var FNOP = function () {}; FNOP.prototype = this.prototype; fbound.prototype = FNOP.prototype; return fbound; } var a = fun.mybind(context, 'mybind'); a('12') // a: 普通函数 var b = new a(12) // a: 构造函数 b: 实例对象
兼容写法
Function.prototype.bind = Function.prototype.bind || function () {
……
};
call
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
原生 call
1. fun.call(context, 'call', 2) // 12, call, 2
call 的实现代码
Function.prototype.mycall = function (context) { if (this && this.constructor !== Function) throw new Error(this.name + ".mycall is not a function"); context = context || window; context.fn = this; var arg = []; for (var i =1; i< arguments.length; i++) { arg.push('arguments[' + i + ']'); } eval('context.fn(' + arg + ')'); delete context.fn; } fun.mycall(context, 'mycall', 3); // 12, 'mycall', 3
apply
apply与call的实现原理差不多,只是apply第二个参数是数组;
1. fun.apply(context, ['call', '4']) // 12, call, '4'
Function.prototype.myapply = function (context, arr) { if (this && this.constructor !== Function) throw new Error(this.name + ".myapply is not a function"); context = context || window; context.fn = this; var arrs = []; if (arr && arr.constructor === Array) { // 判断第二个参数是数组类型 for (var i =0; i <arr.length; i++) { arrs.push('arr[' + i + ']'); } eval('context.fn(' + arrs + ')'); } else if (!arr) { // 第二个参数不传 delete context.fn(); } else { // 第二个参数不是数组类型 throw new Error ("CreateListFromArrayLike called on non-object"); } delete context.fn; } fun.myapply(context, ['myapply', 4]);