call() :在使用一个指定this值 和 若干个指定参数值 的前提下,调用某个函数或方法。
//例1
let foo = {
value: 1
}
function bar() {
console.log(this.value)
}
bar.call(foo)//1
/* 注意
1.call改变了this的指向,指向了foo;
2.bar函数执行了
*/
例1改写如下
//例2
let foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar()//1
/*
这个时候 this 就指向foo,但这样却给 foo 对象本身添加了一个属性,所以们用 delete 再删除它即可。
所以模拟call的步骤为:
1.将函数设为对象属性;
2.执行该函数;
3.删除该函数;
所以代码为:
第一步-------fn是对象的属性名,起什么都可以,后面会删除它
foo.fn = bar
第二步
foo.fn()
第三步
delete foo.fn
*/
手写call 第一版--指定this
/* 写在原型上目的是为了让所有实例对象都可以调用 */
Function.prototype.call2 = function(context) {
//第一步
context.fn = this;
//第二步
context.fn();
//第三步
delete context.fn;
}
//测试一下
let foo = {
value:1
}
function bar() {
console.log(this.value)
}
bar.call2(foo);//1
手写call 第二版--指定参数
//例1
var foo={
value:1
}
function bar(name, age){
console.log(name);
console.log(age);
console.log(this.value)
}
bar.foo('jinjin', 12);//'jinjin',12,1
//改造call2
Function.prototype.call2 = function(context) {
context.fn = this;
let arg = [...arguments].slice(1);//截取除了第一个参数的后面参数
context.fn(...arg);//展开数组
delete context.fn;
}
//测试一下
let foo = {
value: 1
}
function bar(name, age){
console.log(name);
console.log(age);
console.log(this.value);
}
bar.call(foo, 'jinjin', 12)//'jinjin',12,1
bar.call2(foo, 'jinjin', 12)//'jinjin',12,1
手写call 第三版 this为null 和 实现返回值
//例1 传入的参数this(函数)可以是null。 当为null时,视为指向window
var value = 1;
function bar(){
console.log(this.value);
}
bar.foo(null);//1
//例2 针对函数 可以实现返回值
var obj={
value:1
}
function bar(name, age){
return {
value:this.value,
name:name,
age:age,
}
}
console.log(bar.call(obj, 'jinjin', 12))//Object{value:1,name:'jintig',age:12}
//改造call2
Function.prototype.call2 = function(context){
var context = context||window;//1.this为null 也可以写为=> context ?? window 或者 context = context ?? window
context.fn =this;
let args = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn;
return result;//2.有返回值
}
//测试一下
var obj={
value:1
}
function bar(name, age){
return {
value:this.value,
name:name,
age:age,
}
}
console.log(bar.call(obj, 'jinjin', 12))
console.log(bar.call2(obj, 'jinjin', 12))//Object{value:1,name:'jinjin',age:12}
手写call 最终简化版--确保唯一函数名称
Function.prototype.call2 = function(context, ...args){
// console.log(...args)
// console.log(...arguments)
if(typeof context==='undefind' || context==null){//等同于 var context = context||window;
context= window
}
let fnSymbol = Symbol();//每个从 Symbol() 返回的 symbol 值都是唯一的
context[fnSymbol] = this;
let result = context[fnSymbol](...args);//入参...args === [...arguments].slice(1);
delete context[fnSymbol];//删除目的是不污染原来数据
return result;
}
//测试一下
var obj={
value:1
}
function bar(name, age){
return {
value:this.value,
name:name,
age:age,
}
}
console.log(bar.call(obj, 'jinjin', 12))
console.log(bar.call2(obj, 'jinjin', 12))
手写apply
apply的实现与call类似,只是入参不一样,apply为数组
Function.prototype.apply2 = function(context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if(!arr) {
result= context.fn();
} else {
result =context.fn(...arr);
}
delete context.fn;
return result;
}
简化版
Function.prototype.apply2 = function(context, args) {
if(typeof context ==='undefind' || context === null) {
context = window
}
let fnSymbol = Symbol();
let fn = context[fnSymbol](...args);
delete context[fnSymbol];
return fn;
}