1、实现call
call()方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。
通过 call 方法,你可以在一个对象上借用另一个对象上的方法
语法fun.call(thisArg[,arg1[,arg2,…]]);
举个例子:
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); // 1
当调用 call 的时候,把 foo 对象改造成如下
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
};
foo.bar(); // 1
- 模拟实现第一步
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
代码实现如下:
function myCall(context){
// 首先要获取调用call的函数,用this可以获取(this代表bar函数)
context.fn = this;
context.fn();
delete context.fn;
}
- 模拟实现第二步
call 函数还能给定参数执行函数,我们可以从 Arguments 对象中取值,取出第二个到最后一个参数,然后放到一个数组里。
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo); // 1
function myCall(context){
// 首先要获取调用call的函数,用this可以获取(this代表bar函数)
context.fn = this;
var args = [];
// es 方法获取参数并执行
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
// // es6 方法获取参数并执行
// for(var i = 1, len = arguments.length; i < len; i++) {
// args.push(arguments[i]);
// }
// context.fn(...args)
delete context.fn;
}
- 模拟实现第三步
- this 参数可以传 null,当为 null 的时候,视为指向 window。
- 函数是可以有返回值的
- 避免fn重名
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo); // 1
// 生成UUID 通用唯一识别码
function generateUUID(){
var i, random;
var uuid = '';
for (i = 0; i < 32; i++) {
random = Math.random() * 16 | 0;
if (i === 8 || i === 12 || i === 16 || i === 20) {
uuid += '-';
}
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
.toString(16);
}
return uuid;
}
//简单模拟Symbol属性
function mySymbol(obj) {
var unique_proper = "00" + Math.random();
if (obj.hasOwnProperty(unique_proper)) {
arguments.callee(obj)//如果obj已经有了这个属性,递归调用,直到没有这个属性
} else {
return unique_proper;
}
}
// call 函数实现
function myCall(context){
var context = context || widow
// es3 方法 可以写个uuid 或者 fn + new Date().getTime();
var fn = generateUUID()
// // 自己实现Symbol方法
// var fn = mySymbol(context)
// // e6 方法
// var fn = Symbol()
// 首先要获取调用call的函数,用this可以获取(this代表bar函数)
context[fn] = this;
var args = [];
var result
// es 方法获取参数并执行
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
result = eval('context[fn](' + args +')');
// // es6 方法获取参数并执行
// for(var i = 1, len = arguments.length; i < len; i++) {
// args.push(arguments[i]);
// }
// result = context[fn](...args)
delete context.fn;
return result
}
2、实现apply
语法与 call() 方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要,
语法:fun.apply(thisArg[, argsArray])
可以根据上面call函数实现来完善以下代码。
function myApply(context, arr) {
var context = context || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
3、实现bind
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
语法:fun.bind(thisArg[, arg1[, arg2[, ...]]])
bind会创建一个新函数(称之为绑定函数),原函数的一个拷贝,也就是说不会像call和apply那样立即执行。
当这个绑定函数被调用时,它的this值传递给bind的一个参数,执行的参数是传入bind的其它参数和执行绑定函数时传入的参数。
- bind是ES5新增的一个方法
- 传参和call或apply类似
- 不会执行对应的函数,call或apply会自动执行对应的函数
- 返回对函数的引用
兼容写法
if (!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, // this在这里指向的是目标函数
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this //此时的this就是new出的obj
: oThis || this,//如果传递的oThis无效,就将fBound的调用者作为this
//将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
//将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
fBound.prototype = new fNOP();
//返回fBond的引用,由外部按需调用
return fBound;
};
}