当前位置: 首页 > news >正文

搞懂 JS this、call、apply、bind

搞懂 JS this、call、apply、bind

javascript 的 this

ECMAScript 规范中这样写:
this 关键字执行为当前执行环境的 ThisBinding。
MDN 上这样写:
In most cases, the value of this is determined by how a function is called.
在绝大多数情况下,函数的调用方式决定了 this 的值。
可以这样理解,在 JavaScript 中,this 的指向是调用时决定的,而不是创建时决定的,这就会导致 this 的指向会让人迷惑,简单来说,this 具有运行期绑定的特性。

首先需要理解调用位置,调用位置就是函数在代码中被调用的位置,而不是声明的位置。

通过分析调用栈(到达当前执行位置所调用的所有函数)可以找到调用位置。

全局上下文
在全局执行上下文中 this 都指代全局对象。

this等价于window对象
var === this. === winodw.
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6

在浏览器里面 this 等价于 window 对象,如果你声明一些全局变量,这些变量都会作为 this 的属性。

函数上下文
在函数内部,this 的值取决于函数被调用的方式。
直接调用
this 指向全局变量。

function foo(){return this;
}
console.log(foo() === window); // true

如何改变 this 指向

call()、apply()

this 指向绑定的对象上。

var person = {name: "test",age: 25
};
function say(job){console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // test:25
say.apply(person,["FE"]); // test:25

可以看到,定义了一个 say 函数是用来输出 name、age 和 job,其中本身没有 name 和 age 属性,我们将这个函数绑定到 person 这个对象上,输出了本属于 person 的属性,说明此时 this 是指向对象 person 的。

如果传入一个原始值(字符串、布尔或数字类型)来当做 this 的绑定对象, 这个原始值会被转换成它的对象形式(new String()),这通常被称为“装箱”。

call 和 apply 从 this 的绑定角度上来说是一样的,唯一不同的是它们的第二个参数。

bind()

this 将永久地被绑定到了 bind 的第一个参数。

bind 和 call、apply 有些相似。

var person = {name: "test",age: 25
};
function say(){console.log(this.name+":"+this.age);
}
var f = say.bind(person);
console.log(f());

call 和 apply 有什么区别

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

当调用一个函数时,可以赋值一个不同的 this 对象。this 引用当前对象,即 call 方法的第一个参数。

通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如 Object.prototype.toString.call([]),就是一个 Array 对象借用了 Object 对象上的方法。

语法 fun.call(thisArg[, arg1[, arg2[, …]]])

thisArg

在 fun 函数运行时指定的 this 值。需要注意的是下面几种情况

(1)不传,或者传 null,undefined, 函数中的 this 指向 window 对象
(2)传递另一个函数的函数名,函数中的 this 指向这个函数的引用,并不一定是该函数执行时真正的 this 值 (3)值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象,如 String、Number、Boolean
(4)传递一个对象,函数中的 this 指向这个对象

arg1, arg2, …

指定的参数列表。

语法与 call() 方法的语法几乎完全相同,唯一的区别在于,apply 的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply 的这个特性很重要,

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

语法:fun.apply(thisArg[, argsArray])

bind() 函数会创建一个新函数(称为绑定函数)

bind 是 ES5 新增的一个方法
传参和 call 或 apply 类似
不会执行对应的函数,call 或 apply 会自动执行对应的函数
返回对函数的引用
语法 fun.bind(thisArg[, arg1[, arg2[, …]]])

下面例子:当点击网页时,EventClick 被触发执行,输出 JSLite.io p1 p2, 说明 EventClick 中的 this 被 bind 改变成了 obj 对象。如果你将 EventClick.bind(obj,‘p1’,‘p2’) 变成 EventClick.call(obj,‘p1’,‘p2’) 的话,页面会直接输出 JSLite.io p1 p2

var obj = {name:'JSLite.io'};
/*** 给document添加click事件监听,并绑定EventClick函数* 通过bind方法设置EventClick的this为obj,并传递参数p1,p2*/
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//当点击网页时触发并执行
function EventClick(a,b){console.log(this.name, //JSLite.ioa, //p1b  //p2)
}

模拟实现 call

Function.prototype.call2 = function (context) {var context = context || window;context.fn = this;var args = [];for(var i = 1, len = arguments.length; i < len; i++) {args.push('arguments[' + i + ']');}var result = eval('context.fn(' + args +')');delete context.fnreturn result;
}// 测试一下
var value = 2;var obj = {value: 1
}function bar(name, age) {console.log(this.value);return {value: this.value,name: name,age: age}
}bar.call(null); // 2console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
//    value: 1,
//    name: 'kevin',
//    age: 18
// }

模拟实现 apply

Function.prototype.apply = function (context, arr) {var context = Object(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.fnreturn result;
}
http://www.lryc.cn/news/28924.html

相关文章:

  • 力扣209长度最小的子数组
  • 【mysql是怎样运行的】-InnoDB数据页结构
  • VIM实用指南(10)语法自动补全插件coc.nvim
  • 【Vue3 第二十二章】过渡动画
  • 【linux】:进程状态(僵尸进程等)以及环境变量
  • 【C语言——练习题】指针,你真的学会了吗?
  • Linux用户空间与内核空间通信(Netlink通信机制)
  • 3.3日报
  • 并发编程-进程
  • LeetCode196_196. 删除重复的电子邮箱
  • Auto.js Pro 替代品
  • 红日(vulnstack)2 内网渗透ATTCK实战
  • 一个好的工程项目管理软件所包含的主要功能
  • 【大数据监控】Grafana、Spark、HDFS、YARN、Hbase指标性能监控安装部署详细文档
  • 面试题---CSS
  • 【C++】vector
  • RocketMQ安装
  • Spring——什么是IOC?
  • 力扣(LeetCode)430. 扁平化多级双向链表(2023.03.04)
  • 条款13:优先考虑const_iterator而非iterator
  • 23考研 长安大学846计算机考研复试《数据库》
  • Android 9.0 系统去掉省电模式
  • 3 mmmmm
  • nvidia Jetson nano Linux内核编译
  • 理想汽车2023年销量冲击30万辆有戏吗?
  • 借助CatGPT让turtlesim小乌龟画曲线
  • Java面试总结(四)
  • 强强联合,再强的英伟达NVIDIA也不落俗套
  • maven使用心得
  • 【算法题】1958. 检查操作是否合法