〖大前端 - 基础入门三大核心之JS篇(51)〗- 面向对象之认识上下文与上下文规则

  • 说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费如需要项目实战或者是体系化资源,文末名片加V!
  • 作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司担任研发部门CTO。
  • 荣誉:2022年度博客之星Top4、2023年度超级个体得主、谷歌与亚马逊开发者大会特约speaker全栈领域优质创作者

  • 🏆 白宝书系列
    • 🏅 启示录 - 攻城狮的自我修养
    • 🏅 Python全栈白宝书
    • 🏅 ChatGPT实践指南白宝书
    • 🏅 产品思维训练白宝书
    • 🏅 全域运营实战白宝书
    • 🏅 大前端全栈架构白宝书


文章目录

  • ⭐认识上下文
  • ⭐上下文规则
    • 🌟上下文规则1
    • 🌟上下文规则2
    • 🌟上下文规则3
    • 🌟上下文规则4
    • 🌟上下文规则5
    • 🌟上下文规则6

⭐认识上下文

小时候学习做阅读理解时,老师经常会强调,注意上下文。比如有一个句子:这是一个好习惯,我们应该坚持。如果不结合上文的意思,根本不知道“这”指的是什么。如果结合上文,比如,随手关灯,这是一个好习惯,我们应该坚持。我们就知道此时的“这”指的是“随手关灯”,那么整个句子的语义就好理解了。

在函数中,也经常需要结合上下文来编写和理解代码。

函数中可以使用this,表示函数的上下文

与中文中的“这”类似,函数中的this具体指代什么必须通过调用函数时的“前言后语”来判断

下面看一个例子:

  • 第一种调用方式:对象打点调用自己的方法:
var xiaomumu = {
    name: '小沐沐',
    age: 2,
    sex: '男',
    hobbies: ['秋千', '滑梯'],
    sayHello: function () {
        console.log('Hello, 我是' + this.name + ',我今年' + this.age + '岁了');
    }
};
xiaomumu.sayHello();   // Hello, 我是小沐沐,我今年2岁了
  • 第二种调用方式:对象的方法被赋值给一个全局变量,全局变量加圆括号调用:
var xiaomumu = {
    name: '小沐沐',
    age: 2,
    sex: '男',
    hobbies: ['秋千', '滑梯'],
    sayHello: function () {
        console.log('Hello, 我是' + this.name + '我今年' + this.age + '岁了');
    }
};

var sayHello = xiaomumu.sayHello;   // 将这个属性存入了一个全局变量(变量的名称可以和属性名相同,方便理解)
sayHello();   //Hello, 我是undefined我今年undefined岁了

这两种调用方式,this指代的对象分别是:

  • 第一种,对象打点调用方式,函数中的this指代这个打点的对象
  • 第二种,圆括号直接调用函数,函数中的this指代window对象

其实可以这么理解,第二种中,因为全局变量都是window的属性,相当于用window.sayHello()的方式调用了这个函数。所以this指代的就是window对象。

所以我们一定要记住:只有函数被调用时,我们才能知道this指代的是什么函数的上下文由函数的调用方式决定,同一个函数,用不同的形式调用它,则函数的上下文不同

⭐上下文规则

我们知道,不同的调用方式,函数的上下文就不同,那么我们怎么去判断函数的上下文呢?这就要学习各种不同的函数调用规则。

🌟上下文规则1

规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象

下面看几个案例,练习一下:

题目一: 下面代码的运行结果是什么?

function fn() {
    console.log(this.a + this.b);
}
var obj = {
    a: 11,
    b: 22,
    fn: fn
};

obj.fn();

运行结果:33

题目二: 下面代码的运行结果是什么?

var obj1 = {
    a: 1,
    b: 2,
    fn: function () {
        console.log(this.a + this.b);
    }
};

var obj2 = {
    a: 3,
    b: 4,
    fn: obj1.fn
};

obj2.fn();

运行结果: 7

一定要记住,谁打点调用函数,函数中的this指代的就是谁

题目三: 下面代码的运行结果是什么?

function outer() {
    var a = 11;
    var b = 22;
    return {
        a: 33,
        b: 44,
        fn: function () {
            console.log(this.a + this.b);
        }
    };
}

outer().fn();

运行结果:77

上面的代码中,outer()返回了一个对象,相当于还是一个对象打点调用了函数,所以适用于对象打点调用函数,函数的上下文就是这个对象的规则。

题目四: 下面代码的运行结果是什么?

function fun() {
    console.log(this.a + this.b);
}

var obj = {
    a: 1,
    b: 2,
    c: [{
        a: 3,
        b: 4,
        c: fun
    }]
};

var a = 5;
obj.c[0].c();

运行结果:7

上面的代码中,最终调用函数的是obj.c[0]这个对象,所以函数中的this指代的就是obj.c[0]

🌟上下文规则2

规则2:圆括号直接调用函数,则函数的上下文是window对象

题目一: 下面代码的运行结果是什么?

var obj1 = {
    a: 1,
    b: 2,
    fn: function () {
        console.log(this.a + this.b);
    }
};
var a = 3;
var b = 4;

var fn = obj1.fn;
fn();

运行结果:7

题目二: 下面代码的运行结果是什么?

function fun() {
    return this.a + this.b;
}
var a = 1;
var b = 2;
var obj = {
    a: 3,
    b: fun(),
    fun: fun
};
var result = obj.fun();
console.log(result);

运行结果:6,题目分析如下:

image-20230509165256380

这是一道非常正规的面试题,大家一定要学会分析其中的代码逻辑

🌟上下文规则3

规则3:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)

数组[下标]()

类数组对象:所有键名为自然数序列(从0开始),且有length属性的对象

arguments对象是最常见的类数组对象,它是函数的实参列表

题目一: 下面代码的运行结果是什么?

var arr = ['A', 'B', 'C', function () {
    console.log(this[0]);
}];

arr[3]();

运行结果: “A”

上面的代码适用规则3,this指代的就是arr这个数组

题目二: 下面代码的运行结果是什么?

function fun() {
    arguments[3]();
}

fun('A', 'B', 'C', function () {
    console.log(this[1]);
});

运行结果:“B”。我们可以打印一下arguments,可以看到arguments是一个类数组对象,里面其实就是fun()函数被调用时,传入的实参:

image-20230509164048474

🌟上下文规则4

规则4:IIFE中的函数,上下文是window对象。(IIFE在以前的文中提到过,是立即可执行函数。)

(function (){

​ //立即可执行函数

})()

题目一: 下面代码的运行结果是什么?

var a = 1;
var obj = {
    a: 2,
    fun: (function () {
        var a = this.a;
        return function () {
            console.log(a + this.a);
        }
    })()
};
obj.fun();

运行结果:3。这是一个大厂的面试题,题目分析如下:

image-20230509165927143

🌟上下文规则5

规则5:定时器、延时器调用函数,上下文是window对象

setInterval(函数,时间);

setTimeout(函数,时间);

题目一: 下面代码的运行结果是什么?

var obj = {
    a: 1,
    b: 2,
    fun: function () {
        console.log(this.a + this.b);
    }
};
var a = 3;
var b = 4;

setTimeout(obj.fun, 2000);

运行结果:7。因为用延时器调用的函数,适用规则5,this指代的就是window对象,即this.athis.b的值分别是3,4

题目二: 下面代码的运行结果是什么?

var obj = {
    a: 1,
    b: 2,
    fun: function () {
        console.log(this.a + this.b);
    }
};
var a = 3;
var b = 4;

setTimeout(function () {
    obj.fun();    //适用规则1
}, 2000);

运行结果:3。这里要注意,延时器不是直接调用obj里的fun函数,而是一个匿名函数,这个匿名函数里又调用了obj.fun(),所以此时的this应指代的是obj这个对象,即this.athis.b的值分别是1,2

🌟上下文规则6

规则6:事件处理函数的上下文是绑定事件的DOM元素

DOM元素.onclick = function() {

};

题目一: 点击哪个盒子,哪个盒子就变红,要求使用同一个事件处理函数实现(不能用事件委托)

<html lang="en">
<head>
    <style>
        div {
            width: 200px;
            height: 200px;
            float: left;
            border: 1px solid #000;
            margin-right: 10px;
        }
    </style>
</head>
<body>
    <div id="box1"></div>
    <div id="box2"></div>
    <div id="box3"></div>
    <script>
        function setColorToRed(o) {
            this.style.backgroundColor = 'red';
        }
        
        var box1 = document.getElementById('box1');
        var box2 = document.getElementById('box2');
        var box3 = document.getElementById('box3');

        box1.onclick = function () {
            setColorToRed(box1);
        }
        box1.onclick = setColorToRed;
        box2.onclick = setColorToRed;
        box3.onclick = setColorToRed;
    </script>
</body>
</html>

运行结果:点击哪个盒子,哪个盒子就变红。这里的this指代的就是绑定事件的DOM元素;注意区分this和e.target的不同

题目二: 点击哪个盒子,哪个盒子在2000毫秒后就变红,要求使用同一个事件处理函数实现(不能用事件委托)

题目分析:这个题目其实就是上个题目+一个延时器,但是需要注意的是加上延时器后,this指代的可能会不一样,所以需要使用一个程序猿非常常用的技术:备份上下文

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            float: left;
            border: 1px solid #000;
            margin-right: 10px;
        }
    </style>
</head>
<body>
    <div id="box1"></div>
    <div id="box2"></div>
    <div id="box3"></div>
    <script>
        function setColorToRed(o) {
            //备份上下文
            var self = this;  // 通常使用self来备份上下文,也有使用that或_this的,这个技术非常的常用!

            setTimeout(() => {
                self.style.backgroundColor = 'red';
            }, 2000);
        }
        
        var box1 = document.getElementById('box1');
        var box2 = document.getElementById('box2');
        var box3 = document.getElementById('box3');

        box1.onclick = function () {
            setColorToRed(box1);
        }
        box1.onclick = setColorToRed;
        box2.onclick = setColorToRed;
        box3.onclick = setColorToRed;
    </script>
</body>
</html>

注意:程序员在书写this的时候通常会看一下这个this到底指代什么,需不需要备份

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/237320.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)

WebAppDbTest 项目准备 项目准备1、.net cli 创建项目2、nuget 包引用和项目结构2.1、项目添加相关 nuget 包2.2、WebAppDbTest 项目结构 3、项目代码说明3.1、CSharp/C# 类文件说明3.2、json 配置文件说明 4、项目运行预览 数据库 .db 文件准备1、创建 SQLite 数据库1.1、在 W…

区块链实验室(31) - 交叉编译Ethereum的客户端Geth

编译Geth到X86架构平台 下载Geth源码&#xff0c;直接编译Geth源码&#xff0c;见下图。用file命令观察编译后的文件&#xff0c;架构是x86-64。 编译Geth到Arm64架构平台 直接用命令行编译&#xff0c;同时指定期望的架构为Arm64。编译脚本如下所示。 CGO_ENABLED0 GOOSlin…

VSCode使用Remote-SSH连接服务器时报错:无法与“***”建立连接: XHR failed.

关于VSCode的报错问题&#xff1a;无法与“***”建立连接: XHR failed 问题描述问题理解解决方法手动在本地下载安装包&#xff0c;然后手动传到服务器端 问题描述 是的&#xff0c;我又踩坑了&#xff0c;而且这个弄了好久&#xff0c;也重新装了VSCode软件&#xff0c;好像结…

arm-linux设备fsck命令移植

arm-linux设备fsck命令移植 文章目录 **arm-linux设备fsck命令移植**1、下载e2fsprogs-源码2、解压3、进入源码目录4、配置编译环境&#xff1a;使用以下命令配置交叉编译环境5、测试 1、下载e2fsprogs-源码 首先要确定自己的文件系统格式&#xff0c;IG2000的文件系统是ext4&…

防止域名被攻击或盗刷产生突发高带宽,阿里云国际如何设置带宽上限值?

为防止域名被攻击或盗刷产生突发高带宽&#xff0c;导致产生高额账单&#xff0c;可通过配置带宽封顶&#xff0c;控制用户访问该域名的带宽上限值&#xff0c;减少因突发流量导致的损失。 功能介绍 带宽封顶&#xff0c;即通过设置带宽上限&#xff0c;来控制带宽用量。当指…

博士毕业需要发表几篇cssci论文

大家好&#xff0c;今天来聊聊博士毕业需要发表几篇cssci论文&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 博士毕业需要发表几篇CSSCI论文 背景介绍 CSSCI即“中文社会科学引文索引”&#xff0c;被…

从生态的角度看容器和虚拟化的区别

「执笔人」品高股份 cloud native 资深架构师&#xff1a;继承 概述 容器技术和虚拟机技术都是近些年计算平台的主要的计算虚拟化技术&#xff0c;各家云厂商都提供虚拟机和容器服务&#xff0c;关于容器和虚拟化的区别&#xff0c;业界已经有很多共识了&#xff0c;例如&…

【NR技术】NR NG-RAN整体架构 -功能划分(三)

1 概述 NG-RAN节点包括: gNB&#xff0c;向终端提供NR用户平面和控制平面协议终端;ng-eNB&#xff0c;向终端提供E-UTRA用户平面和控制平面的协议终端。gNB和ng- eNB通过Xn接口相互连接。gnb和NG- eNB也通过NG接口连接到5GC&#xff0c;更具体地说&#xff0c;通过NG-C接口连…

c语言插入排序及希尔排序详解

目录 前言&#xff1a; 插入排序&#xff1a; 希尔排序&#xff1a; 前言&#xff1a; 排序在我们生活中无处不在&#xff0c;比如学生成就排名&#xff0c;商品价格排名等等&#xff0c;所以排序在数据结构的学习中尤为重要&#xff0c;今天就为大家介绍两个经典的排序算法&…

1.9 实战:Postman全局变量

上一小节我们学习了环境管理器以及环境变量&#xff0c;这一小节我们再看另一种变量&#xff0c;全局变量。我们知道环境变量只能在当前环境下使用&#xff0c;如果换了一个环境&#xff0c;那之前的变量就没法使用了。比如我们建了两个环境&#xff0c;一个生产环境&#xff0…

开箱即用的C++决策树简单实现

一个数据结构期末作业&#xff08;有兴趣用的话可以高抬贵手star下⭐~&#xff09;GitHub - mcxiaoxiao/c-Decision-tree: 决策树c简单实现 &#x1f333; c-Decision-tree 附大作业/课设参考文档.doc &#x1f333; c-Decision-tree Introduction &#x1f64c; c-Decision…

PPT插件-好用的插件-超级对齐-大珩助手

超级对齐 包含对齐幻灯、对齐对象、对齐文本三个层级&#xff0c;可共用水平分布、垂直分布、交换位置、统一尺寸、垂直居中、水平居中、绝对居中、靠左对齐、靠右对齐、靠上对齐、靠下对齐 可配合图形缩放使用 可配合文本打散使用 可配合素材库中的一键替换使用 选中场景中的…

新增模板中心和系统设置模块,支持飞书平台对接,DataEase开源数据可视化分析平台v2.1.0发布

这一版本的功能升级包括&#xff1a;新增模板中心&#xff0c;用户可以通过模板中心的模板快速创建仪表板和数据大屏&#xff1b;新增“系统设置”功能模块&#xff0c;该模块包含系统参数、认证设置、嵌入式管理、平台对接四个子模块。在“系统参数”子模块中&#xff0c;用户…

uniapp - 简单版本自定义tab栏切换

tab切换是APP开发最常见的功能之一&#xff0c;uniapp中提供了多种形式的tab组件供我们使用。对于简单的页面而言&#xff0c;使用tabbar组件非常方便快捷&#xff0c;可以快速实现底部导航栏的效果。对于比较复杂的页面&#xff0c;我们可以使用tab组件自由定义样式和内容 目录…

C# OpenCvSharp DNN 部署FastestDet

目录 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN 部署FastestDet 效果 模型信息 Inputs ------------------------- name&#xff1a;input.1 tensor&#xff1a;Float[1, 3, 512, 512] --------------------------------------------------------------- Outpu…

大数据技术10:Flink从入门到精通

导语&#xff1a;前期入门Flink时&#xff0c;可以直接编写通过idea编写Flink程序&#xff0c;然后直接运行main方法&#xff0c;无需搭建环境。我碰到许多初次接触Flink的同学&#xff0c;被各种环境搭建、提交作业、复杂概念给劝退了。前期最好的入门方式就是直接上手写代码&…

Java SPI机制学习

最近阅读源码时看到了SPI加载类的方式 这里学习一下 SPI Service Provider Interface 服务提供接口 API和SPI区别 API是接口和实现类均有服务提供方负责 服务调用方更多是一种主动调用 只是调用SDK来实现某个功能&#xff1b;接口的归属权在于服务提供方 SPI是接口在调用方…

N体问题-MATLAB 中的数值模拟

一、说明 万有引力是宇宙普适真理&#xff0c;当计算两个物体的引力、质量、距离的关系是经典万有引力物理定律&#xff0c;然而面向复杂问题&#xff0c;比如出现三个以上物体的相互作用&#xff0c;随时间的运动力学&#xff0c;这种数学模型将是更高级的思维方法。本文将阐述…

jsp文件引用的css修改后刷新不生效问题

问题 在对 JavaWeb 项目修改的过程中&#xff0c;发现修改了 jsp 文件引入的 css 文件的代码后页面的样式没有更新的问题。 原因 导致这个问题的原因可能是因为浏览器缓存的问题。 解决方法 下面介绍两种解决方法&#xff0c;供大家参考&#xff1a; 1、给 link 标签的 c…

软件测试(接口测试业务场景测试)

软件测试 手动测试 测试用例8大要素 编号用例名称&#xff08;标题&#xff09;模块优先级预制条件测试数据操作步骤预期结果 接口测试&#xff08;模拟http请求&#xff09; 接口用例设计 防止漏测方便分配工具&#xff0c;评估工作量和时间接口测试测试点 功能 单接口业…
最新文章