博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于 this 的一篇总结
阅读量:6476 次
发布时间:2019-06-23

本文共 4745 字,大约阅读时间需要 15 分钟。

前言

前排声明,这真的是写 this,没有什么太多新的东西,就是一个自己对 this 绑定规则的总结,也许后期水平提高会从更深的角度去解释 JavaScript 中的 this 绑定规则。本文从分别从绑定全局对象和绑定具体对象的角度总结了一下 this 的绑定规则。

绑定全局对象

this 绑定全局对象,分为两种情况:

  • 直接在全局作用域下使用 this
  • 被调用的函数不满足绑定具体对象的绑定规则

直接在全局作用域下使用 this

这种没什么好说的,全局作用域下调用 this 绑定的是全局对象,例如浏览器中绑定的是 window 对象。

console.log(this)复制代码

直接拿上面这段代码在浏览器控制台运行下得到的就是全局对象。

被调用的函数不满足绑定具体对象的绑定规则

直接使用函数名调用函数

function test () {    console.log(this);  // window}test();var test1 = function() {    console.log(this);  // window}test1();复制代码

定义某对象的属性为函数,该属性值被赋值被另一变量时。

var aObj = {    propertyFn: function() {        console.log(this.testName);    },    testName: "aObj"};aObj.propertyFn();  //  "aObj"var aFn = aObj.propertyFn;aFn();  //  undefined复制代码

绑定具体对象

绑定具体对象的情况较多,可总结为以下几种

  • 函数作为对象属性被获取并调用
  • 使用 new 关键字调用函数
  • 使用 call,apply,bind 调用函数
  • 函数被注册为事件监听器和事件处理器
  • 箭头函数中的 this

函数作为对象属性被获取并调用

函数作为对象属性被获取并调用时,函数中的 this 绑定的是获取该属性的对象。考虑如下代码

function test() {    return this.testName;}var testObj = {    testName: "testObj",    getTestName: test,    getTestNameFn: function() {        return this.testName;    }}console.log(testObj.getTestName()); // "testObj" 函数虽然是在全局作用域下定义的,                                    // 但是被赋值给了testObj的getTestName属性,且是被作为对象的属性调用的console.log(testObj.getTestNameFn());   // "testObj"复制代码

使用 new 关键字调用函数

使用 new 关键字调用函数时,该函数会生成并返回以个新的对象,函数中的 this 绑定的生成的新对象。

function Test(name) {    this.name = name;    console.log(this);}var s = new Test("s");  //  {name: "s"}function Test1(name) {    this.name = name;    console.log(this);    return true;}var s1 = new Test1("s1");   //  {name: "s1"}function Test2(name) {    this.name = name;    console.log(this);    return {};}var s2 = new Test2("s2");   //  {name: "s2"}复制代码

上述结果表明,使用 new 调用函数的时候一定会生成一个新对象,且 this 绑定的就是这个新对象,只不过当你在函数中 return 了非 Object 类型的值时,这个对象不会被赋值给你定义的接收变量,这时接收的变量被赋的是函数中使用 return 返回的值。

使用call ,apply,dind 调用函数

这里call和apply的作用是类似的,都是函数的实例方法,可为函数指定 this 绑定的对象,两者区别在于 apply 的第二个参数是数组,该数组中的值会以实参形式被传递给调用 apply 的函数,而 call 函数除了第一个参数外的参数均被传递给调用 call 的函数。

function test(param1, param2) {  console.log(this.name, param1 + ", " + param2);}var a = {  name: "a"};var b = {  name: "b"}test.call(a, "aParam1", "bParam2");test.apply(b, ["bParam1", "bParam2"]);test();复制代码

bind函数的作用和以上两者与别很大,其作用是将函数中的 this 绑定对象与指定的对象绑定起来,返回一个函数,每次调用返回的函数时,其 this 都是绑定的指定对象。

function test() {    console.log(this.name);}var a = {    name: "a"}var bindTest = test.bind(a);bindTest(); // "a"var b = {    name: "b",    getName: bindTest}b.getName();    // "a"var c = new bindTest(); // undefinedbindTest.call(b);   // "a"复制代码

从上面的示例代码可以看出,函数和指定对象被绑定后使用 new 关键字是绑定失效,在以上示例中绑定函数中的 this 绑定的是一个新创建的对象实例,且该对想的构造函数时test函数。由此也可得出,this 绑定场景同时出现的情况下 new 的优先级是高于调用 bind 函数的优先级的。

箭头函数

关于箭头函数中 this 绑定,MDN 中的说法是箭头函数是没有自己的 this 的,其 this 是从其作用域链上层作用域继承而来的。那么怎么理解呢?下面上代码:

let arrowFn = () => {
console.log(this === window)};arrowFn(); // truelet a = { name: "a", getSelf: arrowFn};a.getSelf(); // truelet b = { name: "b"}arrowFn.call(b); // true复制代码

以上代码是箭头函数直接在全局作用域下定义的情况,那么其作用域链上层就是全局作用域,而在浏览器中全局作用域 this 绑定的值是 window 。由于 JavaScript 中的作用域是静态作用域,那么箭头函数在全局作用域中定义时便已经可以确定其 this 就是 window 了,而且后面的该箭头函数作为对象属性值被调用,还是使用 call 显示指定 this 其 this 均为改变。而非箭头函数的画风是这样的:

let fn = function() {    console.log(this === window);}fn();   // truelet a = {    name: "a",    getSelf: fn};a.getSelf();    // falselet b = {    name: "b"}fn.call(b);    // false复制代码

那么是不是一旦箭头函数被定义了,其 this 的绑定就已经被确定了呢?

let createArrowFn = function() {    return () => {
console.log(this)};}let a = { name: "a", getSelf: createArrowFn};let aArrow = a.getSelf(); aArrow(); // 对象alet b = { name: "b"}var bArrow = createArrowFn.call(b); bArrow(); // {name: "b"}复制代码

上面代码两次 this 打印的结果是不一样的,那么是不是就推翻了箭头函数一旦被定义,其 this 就已经确定了的结论。其实不然,这里箭头函数的是上层作用域是createArrowFn这个函数的作用域,这个函数作用域中的 this 会随着调用场景的不同发生发生变化,所以继承其作用域绑定 this 的箭头函数中的 this 自然也会发生改变了。其实箭头函数中的 this 可以这么理解,相当于将函数的上层作用域的 this 用一个变量保存下来,然后在其子函数中使用它。

let fn = function() {    var _this = this;    return function() {        console.log(_this);    }}let a = {    name: "a",    getSelf: createArrowFn};let aArrow = a.getSelf();   aArrow();    // 对象alet b = {    name: "b"}var bArrow = createArrowFn.call(b);  bArrow();   // {name: "b"}复制代码

在理解箭头函数中的 this 时只需理解其被定义时所在作用域的 this 绑定的是什么就可以了。

事件监听器和事件处理器中的 this

先说明不管是事件监听器还是事件处理器中 this 绑定的都是当前触发该事件的节点,即绑定了该事件的元素节点。

这里主要是区分下事件监听器和事件处理器,事件处理器其实是指 html 标签的 on... 属性定义的函数,比如 οnclick="function() {}",当然也可以在 JavaScript 中去设置该属性,事件处理器的特点是其只能有一个,因为是 html 标签属性所以可以覆盖。

事件监听器是指使用addEventListener函数注册的事件回调函数,可同时注册多个。

结论

在讨论 JavaScript 中 this 的绑定值时,其实就是几种情况:

  1. new 函数时this是新创建的对象
  2. call, apply, bind 指定的对象
  3. 调用该函数的对象
  4. 箭头函数的 this 取决于其定义时所在作用域的 this 绑定值
  5. 事件监听器和事件处理器的中的 this 绑定的是绑定函数节点
  6. 上面的情况都不是时,严格模式下的 undefined ,非严格模式下的全局变量

以上总结仅仅为粗浅的不同场景下 this 的绑定值的总结,没有从更深的层次(比如ES标准中的定义)去讨论,主要个人时间水平有限,所以大佬请忽略。如有错误欢迎各位指正,不胜感激。

转载地址:http://pvqko.baihongyu.com/

你可能感兴趣的文章
[转]Windows的批处理脚本
查看>>
多维数组元素的地址
查看>>
数据库运维体系_SZMSD
查看>>
福大软工1816 · 第三次作业 - 结对项目1
查看>>
静态库 调试版本 和发布版本
查看>>
JAVA中的finalize()方法
查看>>
慕课网学习手记--炫丽的倒计时效果Canvas绘图与动画基础
查看>>
基本分类方法——KNN(K近邻)算法
查看>>
.NET Framework3.0/3.5/4.0/4.5新增功能摘要
查看>>
熟悉常用的Linux操作
查看>>
面象过程与面象对象
查看>>
谷歌设置支持webgl
查看>>
js的AJAX请求有关知识总结
查看>>
Eclipse添加新server时无法选择Tomcat7的问题
查看>>
nginx 配置https 负载均衡
查看>>
三分 POJ 2420 A Star not a Tree?
查看>>
修改OBS为仅直播音频
查看>>
OCA读书笔记(3) - 使用DBCA创建Oracle数据库
查看>>
Python基础进阶之路(一)之运算符和输入输出
查看>>
阻塞非阻塞异步同步 io的关系
查看>>