用实例带你走进this、执行上下文世界[js篇一]

前言:

通过本文你将学习到this的多种用法和使用场景、执行上下文(执行环境)的相关概念。

javascript 中this 指向多变、使用场景复杂,也是因其的强大灵活,在前端进阶中也是重要一环,如果能熟练驾驭就能写出更简洁、高质量的代码。

1.执行上下文(执行环境)

在说this之前不得不先说说执行上下文,可以理解为当前代码的执行环境。js中执行环境大概分为三种情况:

  • 全局执行上下文(js运行首次会进入该环境) ---->eg: window对象
  • 函数执行上下文(函数调用)
  • eval

JS首次运行都会创建全局执行上下文,并秉承栈先进后出的概念,将全局执行上下文放入栈底,全局执行上下文在浏览器关闭后出栈。栈底永远是全局执行上下,栈顶则是当前正在执行的上下文。

全局执行上下文之后每当有函数调用,都会创建一个函数执行上下文并且入栈。由于js 是单线程的,所以栈顶的上下文正在执行,则其他上下文需要等待。

执行上下文分为创建阶段、执行阶段、销毁阶段。确定this指向主要在创建阶段

通过下图你就能明白了(备注:图来源于网络)

2. this

用一句话概述this执行,广泛的说法是谁调用它,this就指向谁,也就是说this 指向是在发生调用时才确定的指向,其实这说法很笼统,按照全面来讲应该是:在发生调用函数时会创建属于该函数自身的执行上下文,执行上下文创建阶段会确定this的执行。总结==》this指向是在调用函数时根据执行上下文所动态确定的。

 

关于this,下面笔者将会围绕下图进行分析讲解

先熟悉几条规律:

  • 在函数体中非显式/隐式绑定下,严格模式下this绑定到undefined,否则绑定到window/global 全局对象。
  • call、apply、bind 显式调用,绑定到指定对象。
  • 构造函数new 调用,绑定到新创建的对象上。
  • 由上下文对象调用,绑定到该对象上。
  • 箭头函数this由外层上下文绑定的this决定执向。

下面将结合实例分析this: 笔者将反着来说,先讲复杂的

箭头函数和this

箭头函数中的this指向非常固化,因为箭头函数没有自己的this,所以箭头函数中不能用call、bind、apply来改变this的指向。

  1. 在对象方法内实例场景
const fun = {
    fn:function () {
    setTimeout(function(){
        console.log(this)
    })
  }
}

console.log(fun.fn()) // window
如果需要将this指向foo时,用箭头函数即可解决
const fun = {
    fn:function () {
    setTimeout(() =>{
        console.log(this)
    })
  }
}
console.log(fun.fn()) // {fn: ƒ}

 

2.箭头函数在函数内部,以非方法形式使用

function Person() {
    setTimeout(function(){
    console.log(this)
  })
}
var p = new Person(); // Window

 // 箭头函数形式:
function Person() {
    setTimeout(()=>{
    console.log(this)
  })
}
var p = new Person(); // Person{ }

 

箭头函数的this指向了构造函数新生成的对象,而普通函数指向了全局window对象普通函数在定义的时候并不知道自己的this要指向哪里,所以在被调用的时候普通函数里的this会指向调用它的那个对象,而箭头函数本身没有this,所以他会直接绑定到定义它时的作用域内的this,通俗讲就是会直接绑定到它父级的执行上下文里的this.

 

this优先级

this绑定有显示、隐式绑定一说,通常把call\bind\apply\new 等情况称为显示绑定,而根据关系调用来确定this指向称为隐式绑定,那么显示绑定和隐式绑定谁的优先级更高呢?

cont obj1={
    a:1,
  fun:fun
}

const obj2={
    a:2,
  fun:fun
}
function fun() {
    console.log(this.a)
}

obj1.foo.call(obj2) // 2
obj2.foo.call(obj1) // 1
// 也就是说call\apply\bind显示绑定优先级更高

再看

const obj={}
function fun(a) {
    this.a=a;
}
let foo = fun.bind(obj)
foo(1)
console.log(obj.a) // 1

上面代码通过bind,将fun 函数中的this绑定为obj对象。执行foo(1)之后obj对象为{a:1}

当foo作为构造函数时,let fss=new foo(2), console.log(fss.a) // 2 

看foo函数本身是通过bind方法构造函数,其内部已经将this 绑定为obj,它再作为构造函数,通过new调用时,返回的实例已经与obj 解绑,

总结: new绑定修改了bind绑定中的this,因此new绑定优先于bind显示绑定

 

function foo() {
    return a => {
        console.log(this.a)
    };
}
const obj1 = {
    a: 2
}
const obj2 = {
    a: 3
}
const bar = foo.call(obj1)
console.log(bar.call(obj2))  // 2

这里有些同学会疑问为什么会输出2 不是3,那他肯定没有好好看上面章节,由于foo()的this 绑定到obj1,bar的this也会绑定到obj1,箭头函数的绑定无法被修改

如果将foo 全修改成箭头函数时

var a = ‘老番茄‘
const foo =() => {
    a => {
        console.log(this.a)
    };
}
const obj1 = {
    a: 2
}
const obj2 = {
    a: 3
}
var bar = foo.call(obj1)
console.log(bar.call(obj2)) // ‘老番茄‘

//如果不懂请翻到上面查看箭头函数和this 章节

 

bind/call/apply

区别: 他们都是用来改变相关函数this指向,但是call/apply 是直接惊醒相关函数调用(其两者区别主要体现在参数上),bing不会执行相关函数,而是返回一个自动绑定新this指向的新函数。

  • fun.apply(obj, arg1, arg2)
  • fun.call(obj, [arg1,arg2])
  • fun.bind(obj, ‘arg1‘, ‘arg2‘)()
const obj = {
    name: ‘zhangsan‘,
  showName: function() {
    console.log(this.name)
  }
}

const bar = {
    name: ‘lisi‘
}
console.log(foo.showName.call(bar)) // ‘lisi‘

bing/call/apply 高级难度往往在结合构造函数和组合式实现继承,构造函数笔者下面会讲解

构造函数和this

new 调用构造函数,具体做了什么?

  • 创建新对象
  • 将构造函数的this指向这个新对象
  • 为这个对象增加属性和方法
  • 最终返回新对象
function Foo(){
    this.name=‘laofanqie‘,
}
const bar = new Foo()
console.log(bar.name) // laofanqie

可以用一下代码概述下过程:

var foo = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)

构造函数中出现了显示return,那么要注意两种情况:

1.
function Foo() {
    this.name = ‘laofanqie‘;
  const obj={};
  return obj;
}
const bar = new Foo();
console.log(bar.name) // undefined 此时bar ={}

2.
function Foo() {
    this.name = ‘laofanqie‘;
  return 1;
}
const bar = new Foo();
console.log(bar.name) // ‘laofanqie‘

总结: 如果构造函数中显式返回一个值,且返回的是一个对象,那么this就指向这个返回的对象;如果返回的不是一个对象,那么this仍然指向实例.

全局环境下的this

全局环境下的this,比较简单,不详细说明,直接看实例吧。

function f1 () {
    console.log(this)
}
function f2 () {
 ‘use strict‘
    console.log(this)
}
f1() // window
f2() // undefined
const foo = {
    bar: 10,
    fn: function() {
        console.log(this)
        console.log(this.bar)
    }
 }
 var fn1 = foo.fn
 fn1()

这里 this 仍然指向的是 window。虽然 fn 函数在 foo 对象中作为方法被引用,但是在赋值给 fn1 之后,fn1 的执行仍然是在 window 的全局环境中。

const foo = {
    bar: 10,
    fn: function() {
        console.log(this)
        console.log(this.bar)
    }
 }
 foo.fn() // {bar:10,fn: f} 10

总结:在执行函数时,如果函数中的 this 是被上一级的对象所调用,那么 this 指向的就是上一级的对象;否则指向全局环境

总结: 通过以上的描述,this的确错综复杂,想要完全掌握还是得结合实践,本文章也尽可能的进行讲解说明,希望读者能各有所获,祝大家身体健康,武汉加油!。

觉得不错的请给个三连哦??,接下我会更新大家感兴趣的话题,让我们一起进步。

原文地址:https://www.cnblogs.com/Dobin/p/12367645.html

时间: 2024-10-09 09:04:18

用实例带你走进this、执行上下文世界[js篇一]的相关文章

GDI+入门——带你走进Windows图形的世界

一.GDI+基础 1.GDI+简介 GDI+是微软的新一代二维图形系统,它完全面向对象,要在Windows窗体中显示字体或绘制图形必须要使用GDI+.GDI+提供了多种画笔.画刷.图像等图形对象,此外还包括一些新的绘图功能,比如渐变.除锯齿.纹理等. GDI+包括三部分:二维矢量图形的绘制.图像处理和文字显示.GDI+使用的各种类大都包含在命名空间system::Drawing中. 2.常用的数据结构 在使用GDI+显示文字和绘制图形时,需要用到一些数据结构,例如Size.Point.Recta

Miox带你走进动态路由的世界——51信用卡前端团队

写在前面: 有的时候再做大型项目的时候,确实会被复杂的路由逻辑所烦恼,会经常遇到权限问题,路由跳转回退逻辑问题.这几天在网上看到了51信用卡团队开源了一个Miox,可以有效的解决这些痛点,于是乎我就做了一些尝试,确实很不错,star增速也表明了业界对其的认可!由于自己能力有限,不能很好地解读Miox,于是我就把Miox作者的文章给搬过来了,希望对大家有所帮着.(跟作者聊过之后,了解到作者团队开发了2年多,沉淀了很深,后来选择了开源,如果大家觉得好的话,可以去点一下star!) github地址:

我的博文观——带你走进优秀博文的世界

优秀博文的特点 一篇好的博文不仅能让人获得知识.开阔见闻,还能启发人的思考,使人身心愉悦!我拜读过众多博文,也发现了许多优秀的博文,于是总结了这些优秀博文的特点,发现好的博文一般具有的以下特点: 1.结构清晰.整洁大方 就算内容或观点不是很精辟,整个博文的格式也要保持简约.大方.清爽,同时还要保持结构清晰,一般会有多级的目录. 2.图文并貌 好处是易于理解,好的图片给人赏心悦目的感觉,可以缓解疲劳 3.古意新谈 一些老生常谈的大道理能新的角度新的方式去解读 4.细致全面 如果是技术类博文,对讲解

走进windows编程的世界-----入门篇

1   Windows编程基础 1.1Win32应用程序基本类型 1)  控制台程序 不须要完好的windows窗体,能够使用DOS窗体方式显示 2)  Win32窗体程序 包括窗体的程序,能够通过窗体与程序进行交互 3)  Win32库程序 提供已有的代码,供其它程序使用 动态库(DLL):是在运行的时候能够载入的. 静态库(LIB):是在编译链接是使用的程序.成为当前程序的一部分. 1.2头文件和库 1.2.1头文件 主要的头文件windows.h包括了windows经常使用的定义等,其它,

278 执行上下文、执行上下文栈:变量提升与函数提升,执行上下文,执行上下文栈,全局执行上下文,函数执行上下文,练习题

变量提升与函数提升 变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined) 函数提升: 在函数定义语句之前, 就执行该函数 先有变量提升, 再有函数提升 变量声明提升.函数声明提升 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>01_变量声明提升.函数声明提升</title> &l

SQL Server安全(6/11):执行上下文与代码签名(Execution Context and Code Signing)

在保密你的服务器和数据,防备当前复杂的攻击,SQL Server有你需要的一切.但在你能有效使用这些安全功能前,你需要理解你面对的威胁和一些基本的安全概念.这篇文章提供了基础,因此你可以对SQL Server里的安全功能充分利用,不用在面对特定威胁,不能保护你数据的功能上浪费时间. SQL Server决定主体是否有需要的许可执行代码的基本方式是它的执行上下文角色.这都是复杂的可能性,主体有执行代码的许可,但没有代码访问的潜在对象的许可,例如表里的数据.这篇文章会探寻SQL Server执行上下

JavaScript学习系列之执行上下文与变量对象篇

一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变量对象的基本理论知识,是理解闭包,原型链的关键所在(闭包与原型链会在接下来的文章中介绍).本篇文章就带你走进JavaScript的执行上下文与变量对象,由于本人才疏学浅,若有什么表述有误的地方,欢迎各位看官能够指点一二,在此不胜感激... 在阅读这边文章之前,默认您已经掌握了JavaScript的基

带你走进虚拟化世界之kvm(转载)

http://chuck.blog.51cto.com/10232880/1720953 带你走进虚拟化世界之kvm 2015-12-08 23:10:46 标签:云计算 虚拟化 kvm 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://chuck.blog.51cto.com/10232880/1720953 一.走进云计算 云计算:云计算是一种按使用量付费的模式,这种模式提供可用的.便捷的.按需的网络访问, 进入可配置的计算

带你走进虚拟化世界之KVM

带你走进虚拟化世界之KVM 一.走进云计算 云计算:云计算是一种按使用量付费的模式,这种模式提供可用的.便捷的.按需的网络访问, 进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供,只需投入很少的管理工作,或与服务供应商进行很少的交互. 1.1 云计算的特点和优势 1)云计算是一种使用模式 2)云计算必须通过网络访问 3)弹性计算,按需付费 1.2 在云计算之前的模式或技术 1)IDC托管 2)IDC租用 3)虚拟主机(卖空间的) 4)vps:虚拟专