Js:apply/call/bind、作用域/闭包、this指向(普通,箭头,JS/Vue的this)

1、apply/call/bind

共同点:

  1. apply()、call() 和 bind() 方法 三者作用都是 改变this指向。
  2. 接收的第一个参数都是this要指向的对象

区别:

  1. apply只有两个参数,第二个参数为数组;
  2. call和bind传参相同,多个参数依次传入的;
  3. call和apply都是对函数进行直接调用(立即执行),而bind方法不会立即调用函数,而是返回一个修改this后的函数(调用 修改后的函数)。
function testFn (param1, param2, param3) {
  console.log(this, '-----this')
  console.log(param1, param2, param3, '----------->param1, param2, param3')
}

testFn() 											// 指向window

const obj = {
  type: 'this指向'
}

testFn.apply(obj, ['参1', '参2']) 					// 指向obj
testFn.call(obj, '参1', '参2') 						// 指向obj

const newFn = testFn.bind(obj, '参1', '参2') 		// '参3'也可以写在这	
newFn('参3')	// '参3'---> param3					// 指向obj

// 二次调用
testFn()											// 指向window
newFn()												// 指向obj

2、作用域、作用域链和闭包

核心

  1. 作用域:也叫执行上下文环境
  2. 作用域:只有全局作用域、函数体代码段(函数作用域)----ES5之前
  3. 作用域中变量的值是在执行过程中产生的(根据作用域上下文查找),
  4. 作用域是在函数创建时就确定了(★★★★★★★非常重要★★★★★★★★)。
  5. 作用域别想成this指向

1、预处理(解析阶段)——JS执行“代码段”之前

变量预解析(变量提升)

变量预解析就是指,把所有的变量声明提升到当前作用域的最前面,但不提升赋值操作。

例如:
console.log(a);
var a = 10;
上述代码的执行结果为undefined,它的实际执行顺序可以理解为:
var a;
console.log(a);
a = 10;

函数预解析(函数提升)

函数预解析则是把所有的函数声明提升到当前作用域的最前面,但不执行函数。

例如:
fn();
function fn(){ // 函数声明
    console.log("a");
}
相当于:
function fn(){
    console.log("a");
}
fn();

而:
a();
var a=function(){ // 函数表达式
    console.log("a");
}
上述函数执行时则会报错,a is not a function
上述函数可以理解为:
var a;
a();
a=function(){
    console.log("a");
}

2、生成执行上下文环境——对代码段(全局/函数体)进行处理

全局代码段

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值(window);
3.函数声明 —— 赋值。

函数体代码段

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值(参考this指向);
3.函数声明 —— 赋值;
4.参数 —— 赋值;
5.argument —— 赋值;
6.自由变量的取值作用域 —— 赋值。

注意:
函数每被调用一次,都会产生一个新的执行上下文环境
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域
这里要跟this指向区分开。(这里说的作用域先简单理解为,只是变量的作用域,跟this没关系)

3、执行上下文环境小结

执行上下文的定义可以通俗化为 —— 在执行代码段之前(预处理阶段),把将要用到的所有变量都事先拿出来,有的直接赋值,有的先用undefined占个空,这些变量共同组成的词法环境,即为执行上下文环境

4、多个执行上下文环境

处于活动状态的执行上下文环境只有一个。
其实这是一个压栈出栈的过程——执行上下文栈。
在这里插入图片描述

5、作用域

作用域是一个很抽象的概念,类似于一个“地盘”

在这里插入图片描述

如上图,全局代码和fn、bar两个函数都会形成一个作用域。
而且,作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的。
例如,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

6、作用域和执行上下文

在这里插入图片描述

执行过程:

第一步,在加载程序时,已经确定了全局上下文环境,并随着程序的执行而对变量就行赋值。
第二步,程序执行到第27行,调用fn(10),此时生成此次调用fn函数时的上下文环境,压栈,并将此上下文环境设置为活动状态。
第三步,执行到第23行时,调用bar(100),生成此次调用的上下文环境,压栈,并设置为活动状态。
第四步,执行完第23行,bar(100)调用完成。则bar(100)上下文环境被销毁。接着执行第24行,调用bar(200),则又生成bar(200)的上下文环境,压栈,设置为活动状态。
第五步,执行完第24行,则bar(200)调用结束,其上下文环境被销毁。此时会回到fn(10)上下文环境,变为活动状态。
第六步,执行完第27行代码,fn(10)执行完成之后,fn(10)上下文环境被销毁,全局上下文环境又回到活动状态。

总结:

作用域只是一个“地盘”,一个抽象的概念,其中没有变量。
要通过作用域对应的执行上下文环境来获取变量的值。
同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。

7、从【自由变量】到【作用域链】

自由变量:

在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。如下图

var x = 10,

function fn() {
  var b = 20;
  console.log(x + b);     这里的x就是一个自由变量
}

以上代码,在调用fn()函数时,函数体中第6行。取b的值就直接可以在fn作用域中取,因为b就是在这里定义的。而取x的值时,就需要到另一个作用域中取。到哪个作用域中取呢?


要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记,如下图示例:

var x = 10,

function fn() {
  console.log(x); 
}

function show(f) {
  var x = 20;
  (function () {
    f();         -------> 输出的是10,而不是20
  })();
}
show(fn)

自由变量的作用域

取自由变量x的值时,要到哪个作用域中取?
————要到创建fn函数(包含了自由变量x)的那个作用域中取——无论fn函数将在哪里调用。

作用域链

上面描述的只是跨一步作用域去寻找。
如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。

这个一步一步“跨”的路线,我们称之为——作用域链

案例:理解作用域取值
在这里插入图片描述

第13行,fn()返回的是bar函数,赋值给x。
执行x(),即执行bar函数代码。
取b的值时,直接在fn作用域取出。
取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了。

8、闭包(基础)

8.1 什么是闭包

var name = 'zs'
function foo(){
	var message = 'Hello JavaScript!'
	console.log(message, name)
}
foo()

这就是一个闭包的应用体现。

闭包:它储存了一个函数和一个关联的环境(上下文环境)
闭包和函数的最大的区别:,当捕获闭包的时候,它的自由变量会在捕获时被确定,这样即使脱离了捕获时的上下文,它也能照常运行。
js的闭包是通过作用域链实现的。并且每当创建一个函数,就会创建一个闭包。

闭包的个人理解
一个普通的function函数,如果它可以访问外层作用域中的自由变量,name这个函数就是一个闭包。
广义来讲:JavaScript中的函数都是闭包
狭义上讲:JavaScript中一个函数,如果访问了外层作用域的变量,那么它一定是一个闭包。

8.2 闭包的问题:会引起内存泄漏

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意
记得变量的初始化处理,手动释放闭包产生的内存数据。

9、推荐:上下文,作用域和闭包文章

参考链接:https://www.cnblogs.com/wangfupeng1988/p/3977924.html

3、this指向

1、确认时机

普通函数–this–确定时机(调用

(1)在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。

箭头函数–this–确定时机(定义

(1)箭头函数没有自己的this, 它的this是继承而来;
(2)默认指向在定义它时父级作用域(函数作用域)的this,而不是执行时的对象。也可理解为:默认指向最近作用域( 【函数作用域】/【代码块】)中的this
(2.1)最近作用域:可以理解为是函数作用域,或者是代码块。注意,对象的{}不是代码块。
(3)所谓父级,可以理解为:箭头函数外层的打印console.log(this)这个this指向谁就是谁
(4)父级作用域准确来讲是父级的函数作用域

demo案例

const message = '全局message'
const obj = {
  message: 'obj的message',
  foo: () => {
    // const message = 'foo的message'
    const bar = () => {
      console.log('bar', message) // 当foo函数里没有message定义时,会打印const message = '全局message'
      console.log('bar', obj.message) // 只有这样才能拿到obj里的message
      console.log('bar', this)
    }
    return bar
  }
}
const fn = obj.foo()
fn()

在这里插入图片描述

2、JS的this指向的四种情况

个人理解

首先:this一定要指向一个对象。
其次:最顶层的对象,在浏览器环境中指的是window对象
然后:所有属性或者方法,都需要挂靠到对象下,即都是通过:对象.属性,对象.方法这种方式使用
最后:执行时,如果没有对象,那就算到window这个顶层对象上

2.1、构造函数—>指向new出的对象

function Foo() {
  this.bar = '构造函数this指向new出来的对象'
  console.log(this);  // this指向new出来的对象
}
let f1 = new Foo()
console.log(f1,'f1对象');
console.log(f1.bar = 'new出来的对象');

2.1.2 原型对象—>指向调用的函数对象

function foo(name, age) {
  console.log(this, name, age)
}

Function.prototype.hyapply = function (thisArg) {
  console.log(this) // -> 当前调用的**函数对象**,即:foo函数
}
// Function.prototype是原型对象,原型对象也是对象
// Function.prototype.hyapply 就是对象里的一个方法
// foo可以看成是new Function构造函数出来的
// 因此调用时,this--->new出来的对象: foo

foo.hyapply()

2.2、函数作为对象的一个属性—>指向该对象

var obj = {
  x:10,
  fn:function () {
    console.log(this);  // this指向obj对象
    function f() {        // 函数f虽然在obj.fn内部定义的,但是它仍是个普通函数
      console.log(this);  // this指向window-------->情况特殊
    }
    f()
  }
}
obj.fn()
console.log(obj);

2.3、函数用call或者apply调用—>指向传入对象的值

var obj = { x:10 }
var fn = function () {
  console.log(this);  // this指向obj对象
}
fn.call(obj)

2.4、全局 & 调用普通函数—>指向window对象

function Foo() {
  console.log(this);  // this指向window对象
}
var boo = function(){
  console.log(this)  // this指向window对象
}
boo()
Foo()

3、箭头函数的this指向

遇到箭头函数,直接在箭头函数的外层,打印console.log(this),看外层打印的this指向谁即可

let obj = {
	console.log(this)   // 指向window
	bbb:()=>{
	    console.log(this)    // flag0:定义时判断,取父级的this指向,对象中this->window
	},
	aaa:function(){
		console.log(this)   // flag1:执行时判断,最近一层的对象是obj,this->obj
		setTimeout(function(){
			console.log(this)  // flag2:执行时判断,最近一层无对象,this->window
			setTimeout(function(){
				console.log(this);  //  flag3:执行时判断,最近一层无对象,this->window
			})
			setTimeout(()=>{
				console.log(this)   //  flag4:定义时判断,取父级的this指向,是flag2处的this->window
			})
		})
		setTimeout(()=>{
			console.log(this)  // flag5:定义时判断,取父级的this指向,是flag1处的this->obj
			setTimeout(()=>{
				console.log(this); //flag6:定义时判断,取父级的this指向,是flag5处的this->obj
			})
		})
	}
}
obj.aaa()

4、Vue的this指向(组件中this大多是指向组件实例)

  • 组件中data的this指向当前的组件实例对象,无论是return前后
    • main.js中new Vue()中,data的this指向undefined
  • 所有的生命周期函数中的this都指向vue实例对象[组件实例对象]
  • vue的v-on指令中可以接收JS语句,其中的this是window(vue组件中的v-on指令除外)
  • computed中的this指向vue实例对象[组件实例对象]
  • methods中的this有以下几种情况
    • 普通函数的this指向vue实例对象[组件实例对象]
    • 箭头函数没有自己的this,因此this指向其宿主对象的this(注意宿主对象是函数对象(它被调用后this的指向要进行具体分析),简单对象没有this)
    • 普通函数形式的回调函数的this是window,箭头函数形式的回调函数的this遵循箭头函数的原则(大多数情况下是vue实例对象)
// test.vue
<script>
export default {
  data () {
    let data1 = this // 组件实例: test.vue
    console.log(data1, '---->return前的this');
    return {
      data2: this, // 组件实例: test.vue
      data3: '',
    }
  },
  computed: {
    data4 () {
      console.log(this, '---->computed的this'); //Vue实例(组件实例: test.vue)
      return this
    }
  },
  created: function () {
    console.log(this, '---->所有生命周期里的this') //使用它的Vue实例(组件实例: test.vue)
    this.data3 = this
    this.showMessage1();
    this.showMessage2();
    this.showMessage3();
  },
  methods: {
    // 普通函数
    showMessage3 () {
      console.log('普通函数........................指向vue实例(组件实例)');
      console.log(this)  //vue实例调用了showMessage3方法,this自然指向vue实例(组件实例: test.vue)
    },
    // 箭头函数
    showMessage2: () => {
      console.log('箭头函数........................指向undefined');
      console.log(this);   // this---->undefined
    },
    showMessage1 () {
      setTimeout(function () {
        console.log('函数未调用........................指向window');
        console.log(this) //指向window  因为没人调用这个普通函数
      }, 10)
    }
  }
}
</script>

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

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

相关文章

小米万兆路由器里的 Docker 安装 Gitea

小米万兆路由器里的 Docker 安装 Gitea准备工作创建存储查看Docker Hub镜像信息拉取 gitea 镜像和运行容器配置通过 ssh 访问(Optional)其他小米2022年12月份发布了万兆路由器&#xff0c;里面可以使用Docker。 今天尝试在小米的万兆路由器里安装Gitea。 准备工作 先将一块US…

Java企业级开发学习笔记(2.1)MyBatis实现简单查询

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【http://t.csdn.cn/zi0wB】 文章目录零、创建数据库与表一、基于配置文件方式使用MyBatis基本使用1.1 创建Maven项目 - MyBatisDemo1.2 在pom文件里添加相应的依赖1.3 创建与用户表对应的用户实体类 - User1.4 创建用…

没有他们,人工智能只能死翘翘

我过去写过一篇文章《很多所谓伟大的贡献&#xff0c;其实都是狗屎运》&#xff0c;今天我也写写人工智能。&#xff08;1&#xff09;人才深度神经网络如果不从明斯基和罗森布拉特说起&#xff0c;那就应该可以从1965年Ivakhnenko发明前馈神经网络说起。但关键里程碑是出自Rum…

SpringBoot2核心功能 --- 原理解析

一、Profile功能 为了方便多环境适配&#xff0c;springboot简化了profile功能。 1.1、application-profile功能 默认配置文件 application.yaml&#xff1b;任何时候都会加载指定环境配置文件 application-{env}.yaml激活指定环境配置文件激活 命令行激活&#xff1a;java -…

【快乐手撕LeetCode题解系列】—— 环形链表 II

【快乐手撕LeetCode题解系列】—— 环形链表 II&#x1f60e;前言&#x1f64c;环形链表 II&#x1f64c;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客…

STM32与Python上位机通过USB虚拟串口通信

文章目录前言1. 查看原理图2. 新建工程3.添加代码与烧录4. python代码编写总结问题解决思路前言 在详细阅读广大网友的教程之后&#xff0c;我对STM32和Python通过USB通信的流程烂熟于心。 尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试&#xff0c;发现还是存在网上…

Linux·异步IO编程框架

hi&#xff0c;大家好&#xff0c;今天分享一篇Linux异步IO编程框架文章&#xff0c;对比IO复用的epoll框架&#xff0c;到底性能提高多少&#xff1f;让我们看一看。 译者序 本文组合翻译了以下两篇文章的干货部分&#xff0c;作为 io_uring 相关的入门参考&#xff1a; Ho…

【RocketMQ】顺序消息实现原理

全局有序 在RocketMQ中&#xff0c;如果使消息全局有序&#xff0c;可以为Topic设置一个消息队列&#xff0c;使用一个生产者单线程发送数据&#xff0c;消费者端也使用单线程进行消费&#xff0c;从而保证消息的全局有序&#xff0c;但是这种方式效率低&#xff0c;一般不使用…

Web 攻防之业务安全:接口未授权访问/调用测试(敏感信息泄露)

Web 攻防之业务安全&#xff1a;接口未授权访问/调用测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台&#xff08;操作系统、数据库&#xff0c;中间件等&#xff09;、业务系统自身&#xff08;软件或设备&#xff09;、业…

ViT/vit/VIT详解

参考&#xff1a; Vision Transformer详解: https://blog.csdn.net/qq_37541097/article/details/118242600 目录&#xff1a; x.1 (论文中)模型理解x.2 代码理解 建议阅读时间&#xff1a;10min x.1 模型理解 ViT是发表在ICLR2021上的一篇文章&#xff0c;通过将图片分割…

Java并发控制 学习笔记1

一、并发控制的方法 1、悲观锁&#xff1a;常用的互斥锁都属于悲观锁&#xff0c;一个线程访问共享资源时其他线程不能访问。 2、乐观锁&#xff1a;允许同时访问共享数据&#xff0c;只有在提交时利用如版本号检查是否有冲突&#xff0c;应用github。 3、什么时候用乐观锁、什…

携程平台增长部总经理王绩强:原生互联网企业正在经历一场数字升级丨数据猿专访...

‍数据智能产业创新服务媒体——聚焦数智 改变商业以大数据和人工智能为核心&#xff0c;众多新兴技术开始赋能数字营销。于是&#xff0c;智能营销已然从工具化走向了业务化。如今&#xff0c;数字化营销已经成为了企业数字化转型中的重要一环。相较于传统营销逻辑&#xff0…

新版新款影视直播粉红色UI的麻豆CMS源码/带教程/支付已接

基于苹果CMS v10影视系统框架开发的前端模板&#xff0c;带会员中心&#xff0c;可设置试看付费观看等功能。 经过测试及修复&#xff0c;这套源码功能还是很强大的&#xff0c;可以设置一键采集&#xff0c;并且支付我们给他接到了易支付&#xff0c;拓展性强&#xff0c;基本…

【压测】通过Jemeter进行压力测试(超详细)

文章目录背景一、前言二、关于JMeter三、准备工作四、创建测试4.1、创建线程组4.2、配置元件4.3、构造HTTP请求4.4、添加HTTP请求头4.5、添加断言4.6、添加察看结果树4.7、添加Summary Report4.8、测试计划创建完成五、执行测试计划总结背景 通过SpringCloudGateway整合Nacos进…

如何下载ChatGPT-ChatGPT如何写作

CHATGPT能否改一下文章 ChatGPT 作为一种自然语言处理技术&#xff0c;生成的文章可能存在表达不够准确或文风不符合要求等问题。在这种情况下&#xff0c;可以使用编辑和修改来改变输出的文章&#xff0c;使其符合特定的要求和期望。 具体来说&#xff0c;可以采用以下步骤对…

超越竞争对手:利用Facebook A/B测试优化广告效果!

随着社交媒体广告的普及&#xff0c;Facebook已经成为了许多公司推广业务的重要平台。但是&#xff0c;在Facebook上发布广告并不意味着成功&#xff0c;这也让许多公司开始关注如何优化广告效果。 在这篇文章中&#xff0c;我将介绍如何使用A/B测试来优化Facebook广告&#x…

纳米软件关于集成电路测试的分类介绍

集成电路测试可以按照测试目的、测试内容、按照器件开发和制造阶段分类。参照需要达到的测试目的对集成电路测试进行分类&#xff0c;可以分为:验证测试、制造测试、老化测试、入厂测试等。按照测试所涉及内容&#xff0c;集成电路测试可分为:参数测试、功能测试、结构测试等。…

2023/4/4总结

题解&#xff1a; Problem ​​​​​​ A - Codeforces 1.这道题目我们需要判断。 2.如果是奇数&#xff0c;亦或出来的总值不为0&#xff0c;那么每一个数字再去亦或任何一个数字&#xff0c;都不会为0。 3.如果是偶数并且亦或总值为0&#xff0c;那么我们亦或的总值不满…

记录重启csdn

有太多收藏的链接落灰了&#xff0c;在此重启&#xff5e; 1、社会 https://mp.weixin.qq.com/s/Uq0koAbMUk8OFZg2nCg_fg https://mp.weixin.qq.com/s/yCtLdEWSKVVAKhvLHxjeig https://zhuanlan.zhihu.com/p/569162335?utm_mediumsocial&utm_oi938179755602853888&ut…

使用npm包,全局共享数据,分包

使用 npm 包 1、Vant Weapp 1.1、什么是 Vant Weapp Vant Weapp 是有赞前端团队开源的一套小程序 UI 组件库&#xff0c;助力开发者快速搭建小程序应用。它所使用的是MIT 开源许可协议&#xff0c;对商业使用比较友好。 官方文档地址 https://youzan.github.io/vant-weapp …