理解javascript中call/apply的使用和模拟实现
Posted 半夜盗贼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解javascript中call/apply的使用和模拟实现相关的知识,希望对你有一定的参考价值。
call/apply 的作用
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数.
注意:该方法的作用和 apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
function sayName(age) { // this.name = "person"; (1) console.log(this.name,age); } const student = { name: "student" }; sayName.call(student,12); //person 12 (1) sayName.call(student,13); //student 13 // call的作用: call 改变了 this 的指向,指向到 student //(1)假如函数内部有相同属性情况,引用函数内部的属性
总结来说,call总共做了三件事,给对象加了一个函数,给函数传参,然后执行函数。下面我们对call进行模拟
下面我们先来模拟实现函数调用的this指向和函数的执行,怎么才能让函数的上下文指向调用对象呢,很简单我们直接把函数当作对象里面的一个方法即可,如
const student = { name: "student”, sayName:function(age){ console.log(this.name,age); } }; student.sayName(12); //person 12 (1)
我们不能让函数一直在对象里面占用内存,所以在调用之后需要删除,这样我们就可以封装一个简化版的call
// 第一版 Function.prototype.callSub = function(context) { context.fn = this; context.fn(); delete context.fn; }; sayName.callSub(student); //student
这里我们基本上完成一个可用的call,但是最重要的一步传参还没有完成。我们可以通过Arguments 对象取值,取出第二个到最后一个参数,然后放到一个数组里,传给函数调用。
// 第二版 Function.prototype.callSub = function(context) { context.fn = this; let args = []; for (var i = 1; i < arguments.length; i++) { // 需要从第二个参数开始 args.push(arguments[i]); } var result = context.fn(...args); delete context.fn; return result; }; function sayName(age, name) { return { age: age, name: name }; } const student = { name: "小红" }; console.log(sayName.callSub(student, 12, "小明")); //{age: 12, name: "小明"}
在参数传值这里我们利用了es6的解构赋值功能实现数组到参数的转化,但是call/apply是在es5中加入的,所以我们不能用es6的语法实现,传参的工作。这里我们利用eval()函数拼接一个可执行的js字符串脚本。
还有一点需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于non-strict mode,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
// 最终版 Function.prototype.callSub = function(context) { var context = context || window; // 当context为null 的时候指向window context.fn = this; let args = []; for (var i = 1; i < arguments.length; i++) { // 需要从第二个参数开始 args.push("arguments[" + i + "]"); } var result = eval("context.fn(" + args + ")"); delete context.fn; return result; }; function sayName(age, name) { return { age: age, name: name, gender:this.gender }; } const student = { name: "小红", gender:"woman" }; console.log(sayName.callSub(student, 12, "小明")); //{age: 12, name: "小明", gender: "woman”} function sayHello() { console.log("hello"); } sayHello.callSub(); // hello
call和apply的区别就是传参的方式不一样
apply的模拟
// apply接受的参数是一个数组 Function.prototype.applySub = function(context, arr) { var context = context || window; context.fn = this; var result; if (!arr) { result = context.fn(); } 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; }; function sayName(age, name) { return { age: age, name: name, gender: this.gender }; } const student = { name: "小红", gender: "woman" }; console.log(sayName.applySub(student, [12, "小明"])); //{age: 12, name: "小明",gender:"woman"} function sayHello() { console.log("hello"); } sayHello.applySub(); // hello
以上是关于理解javascript中call/apply的使用和模拟实现的主要内容,如果未能解决你的问题,请参考以下文章
理解 JavaScript call()/apply()/bind()
javascript中call()apply()bind()的用法终于理解
javascript中call()apply()bind()的用法终于理解