模拟实现call、apply
1. 知识点补充:
首先在模拟实现前,先Mark一些我之前不知道的知识:
a. eval(string)函数:可计算某个字符串,并执行其中的JavaScript代码
其中,string是必需传入的待计算或待执行的语句,并且必须是原始字符串的形式!
eval(string)相当于<script> string </script>
b. 类数组对象(Array-like Object)
类数组对象是一个对象,比如:arguments、DOM API返回的NodeList对象都属于类数组对象,具有指向对象元素的数组index下标和length属性,但是它们不能使用push/pop/shift/unshift等数组方法!
但是如何能将类数组转换为真正的数组呢?有如下方法:
- Array.prototype.slice.call( arguments ) // 在低版本IE下不支持
- [].slice.call( arguments ) // 等同于1
- let arr = Array.from( arguments ) // ES6,可将类数组对象和可遍历对象转为真正的数组
- let arr = [ ...arguments ]
以下例为例演示:
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name)
console.log(age)
}
bar.call(foo, 'ning', 20);
这里可以考虑将bar这个函数作为foo的一个方法,然后在外层执行这个函数,然后再删掉该函数即可!
2. call的模拟实现
Function.prototype.call2 = function (context) {
context.fn = this; // context是foo,this是bar也就是调用call的那个函数
context.fn();
delete context.fn;
}
// 使用下例来验证call2是否可行
var foo = {
value: 1
}
function bar() {
console.log(this.value);
}
bar.call2(foo);
content.fn = this;这句首先获取到了调用call的函数,本例这里也就是bar;
context.fn();即执行bar这个函数;
delete删掉该函数。
但是现在的模拟中有几个问题:
- 不能传入参数,因此我们将利用arguments,从Arguments对象中从第二个参数(因为第一个参数是this)开始取值,放到一个数组里,再把这个数组放到要执行的函数的参数里
- this参数传入null或者undefined时,我们需要将this指向window
- 当call2()内传的不是一个对象,而是一个基本数据类型时,如何处理?(在call实现时会自动调用Object()转换)
- 函数可以有返回值
所以我们得到以下call2()代码:
Function.prototype.call2 = function (context) {
context = context ? Object(context) : window;
context.fn = this;
var arr = [];
for (var i = 1, len = arguments.length; i < len; i++) {
arr.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + arr + ')');
delete context.fn;
return result;
}
下面我们测试一下:
var value = 'global';
var foo = {
value: 1
}
function bar(name, age) {
console.log(this.value)
return {
value: this.value,
name: name,
age: age
}
} bar.call2(null) // global console.log(bar.call2(foo, 'ning', 20))
//
// {value: 1, name: "ning", age: 20}
说明两点:
- arr.push('arguments['+ i +']');这句得到的是(2) ["arguments[1]", "arguments[2]"]一个新数组,是我们想要的
- eval('context.fn('+ arr +')');这句中arr会自动调用arr.toString()得到一个字符串:arguments[1],arguments[2],然后进行字符串拼接
下面给出ES6版本的:
Function.prototype.call2 = function (context) {
context = context ? Object(context) : window;
context.fn = this;
let arr = [...arguments].slice(1);
let result = context.fn(' + arr + ');
delete context.fn;
return result;
}
3. apply的模拟实现:
Function.prototype.apply2 = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
var result = [];
// 没有arr参数直接执行
if (!arr) {
result = context.fn();
// 有arr参数则将参数拼接后执行
} else {
var args = [];
for (var i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']')
}
result = eval('context.fn(' + args + ')')
}
delete context.fn;
return result;
}
下面给出ES6版本的:
Function.prototype.apply2 = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
let result = [];
if (!arr) {
result = context.fn();
} else {
// ...arr的使用
result = context.fn(...arr)
}
delete context.fn;
return result;
}
模拟实现call、apply的更多相关文章
- js 模拟call、apply、bind实现
1.模拟call实现 Function.prototype.myCall = function (context) { var context = context || window // 给 con ...
- JavaScript模拟call和apply的实现
参考: call和apply的模拟实现 1. call:调用一个对象的一个方法,用另一个对象替换当前对象.例如:B.call(A, args1,args2);即A对象调用B对象的方法. /*call( ...
- call、apply、bind的区别,模拟call、apply和bind的实现
bind:bind绑定完this的指向后会返回一个新的函数体,不会被立即调用 call&apply:绑定完this的指向后会立即调用 call与apply的区别: call:第 ...
- call, apply, bind 区别
#call, apply, bind 区别及模拟实现call apply bind 三者都可以用来改变this的指向,但是在用法上略有不同 首先说一下call和apply的区别 call和apply ...
- Vue.js组件之间的通信
导语:组件之间的关系不外乎两种, 父子组件和非父子组件,本文将对两类组件之间的通信方式进行详细阐述. 父子组件间的通信 通信方式1(单向绑定): Props down, Events up (建议使用 ...
- vue以及js的一些坑或常用技巧
判断空object Object.getOwnPropertyNames(obj).length === 0 模拟range Array.apply(null, Array(5)).map(funct ...
- 22 道高频 JavaScript 手写面试题及答案
实现防抖函数(debounce) 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时. 那么与节流函数的区别直接看这个动画实现即可. 手写简化版: // 防抖函数 cons ...
- JS中this的指向问题&使用call或apply模拟new
this的指向由调用时决定而不是定义时决定,定义的方式: //直接定义在函数里 var a="window中的a"; var name="window"; fu ...
- JavaScript深入之call和apply的模拟实现
call 一句话介绍 call: call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法. 举个例子: var foo = { value: 1 }; func ...
- call和apply的模拟实现
call 一句话介绍 call: call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法. 举个例子: var foo = { value: 1 }; func ...
随机推荐
- (转)Java安全通信:HTTPS与SSL
转:http://www.cnblogs.com/devinzhang/archive/2012/02/28/2371631.html 1. HTTPS概念 1)简介 HTTPS(全称:Hyperte ...
- 配置基于Devstack的嵌套KVM虚拟化
本文为minxihou的翻译文章,转载请注明出处Bob Hou: http://blog.csdn.net/minxihou JmilkFan:minxihou的技术博文方向是 算法&Open ...
- Metasploit 使用MSFconsole接口
什么是MSFconsole? 该msfconsole可能是最常用的接口使用Metasploit框架(MSF).它提供了一个“一体化”集中控制台,并允许您高效访问MSF中可用的所有选项.MSFconso ...
- 获取从天气预报接口返回回来的json数据
搬迁到了我的新博客中 ==> http://www.suanliutudousi.com/2017/08/26/%E8%8E%B7%E5%8F%96%E4%BB%8E%E5%A4%A9%E6%B ...
- jq 出现 $.cookie is not a function
出现这种问题有3中可能 1是没有导入cookie相关的库文件 2是cookie的库文件和jquery库文件的先后顺序问题, 必须先导入jquery库文件后到其他库文件 3一个页面由于二次或多次引入了j ...
- POJ 3237 /// 树链剖分 线段树区间修改(*-1)
题目大意: 给定树的N个结点 编号为1到N 给定N-1条边的边权. 三种操作: CHANGE k w:将第 k 条边的权值改成 w. NEGATE x y:将x到y的路径上所有边的权值乘 -1. QU ...
- fastjson转jackson
使用fastjson有个内存oom的问题,我们应该尽量使用jackjson,为什么呢?因为fastjson会引发一个oom,很潜在的危险,虽然jackjson的api真的非常好用,对于解析json串来 ...
- Tomcat相关知识总结
有关Tomcat的杂货店 一.修改端口号,并以IP访问 1.确保80端口没有程序占用.例如nginx等. 2.vi /tomcat/conf/server.xml 3.找到<Connector ...
- SpringBoot Redis 订阅发布
一 配置application.yml spring: redis: jedis: pool: max-active: 10 min-idle: 5 max-idle: 10 max-wait: 2 ...
- leetcode-157周赛-5216-统计元音字母序列的数目
题目描述: 方法:倒推 class Solution(object): def countVowelPermutation(self, n): MOD = 10 ** 9 + 7 a=e=i=o=u= ...