前言

前面我们已经讲完了js的作用域和作用域链,说白了就是js的一个变量的查找规则,但是由于js静态作用域的这么一个特性,导致作用域只在声明的时候就已经创建好了,这对于我们开发来说有时候不是那么友好:
具体看下列代码:

     var bar = {myname: '拉亚斯特',printName: function () {console.log(myname)},}function foo() {let myname = '凯隐'return bar.printName}let myname = '影流之镰'let _printName = foo()_printName()    //影流之镰bar.printName() //影流之镰

bar函数中的printName函数打印的也会是全局的myname,但是我们想要它打印bar对象中的myname--拉亚斯特,
这时候js就引入了this.
this在某种意义上说具备动态作用域的特性,它的绑定跟作用域毫无关联,它只关心函数在何时,在哪被调用,然后运行时进行一个this的绑定

JS中的this到底是什么?

关于this,我们还是从执行上下文说起.前面我们说过了,执行上下文包括了保存let和const声明的块级变量的词法环境,也有保存var变量,函数声明和outer的变量环境.但是执行上下文其实包括一个this,而这个this,在执行上下文入栈的时候会进行一个绑定.

全局上下文中的this

首先我们来看执行上下文中的this是什么

<script>console.log(this);</script>

打印出来window,这说明全局状态下,window是指向this的,这也是作用域链和this的唯一交点,作用域链的最底端包含了window对象,全局执行上下文中的this也指向了window对象,(node环境下,全局this是指向undefined的,以下例子全部用浏览器环境,node暂不考虑)

函数执行上下文中的this

现在我们知道了全局执行上下文中的this指向window对象,那么函数执行上下文中的this是指向什么呢?

默认绑定

function foo() {console.log(this);}foo()

大家可以自己试一试,打印的也是window,不管你这个函数是什么,打印this都是指向window的,这就说明函数执行上下文中,this存在一个默认绑定,默认绑定this到window对象中,(严格模式下,函数中的this为undefined)

隐式绑定

那么我们一开始中的代码,this被绑定到了bar这个对象上,这是属于什么绑定规则呢,

var bar = {myname: '拉亚斯特',printName: function () {console.log(this.myname)},}bar.printName() //拉亚斯特

这就属于隐式绑定,就是函数调用前,把他"挂载"到一个对象上,这样函数中的this,就指向这个对象了
不过隐式绑定存在隐式丢失
还是我们开头的代码

var bar = {myname: '拉亚斯特',printName: function () {console.log(myname)},}function foo() {let myname = '凯隐'return bar.printName}let myname = '影流之镰'let _printName = foo()_printName()    //影流之镰

这时另一个函数foo中明明返回了bar.printName这个函数,按道理来讲接收这个函数的变量_printName执行也因该打印拉亚斯特的,可惜它打印的确实全局状态下的影流之镰,这就是隐式丢失,我们在分析this的时候有时候要特别注意这种情况,这也this设计的缺陷之一,不符合人的直觉,导致一些难以预料的错误,而这些错误,按照人的惯性思维很难去发现

显示绑定

可能有人就会好奇啦,说:’'马老师啊,有没有什么办法,通过我自己的意愿绑定意思啊"
当然有啦.js为我们指定了3个方法来通过自己的意愿绑定this

call方法
     let bar = {myname: '拉亚斯特',}function foo() {this.myname = '凯隐'}foo()foo.call(bar)console.log(myname); //凯隐console.log(bar); //{}

观察上面的代码,打印的是凯隐,{myname:‘凯隐’},为什么呢?
执行foo函数,this.myname = '凯隐',这里的this指向的是谁?没错,就是window,此时window会挂载一个myname = ‘凯隐的属性’,这就解释了console.log(myname);为什么会输出凯隐了

然后就是foo.call(bar),这里使用了一个call方法,将bar对象作为参数传入,使foo中的这个this指向了bar,call方法的具体使用
这时候我们console.log(bar);,发现bar中的myname变成了凯隐,因为执行foo.call(bar)函数的时候,foo函数内this.name = ‘凯隐’,这个this指向的bar,等于将bar中的name修改为了凯隐
这时候我们看到的bar对象中的myname就变为凯隐了

显示绑定另外两种方法就是apply方法和bind方法了,这两种方法都是Object.prototype上定义的方法.具体使用大家自行百度,我这里只做this 的讲解

new绑定

这是优先级最高的绑定不过只用来设置构造函数,利用new关键字,设置构造函数中的this指向

function Person(name, age, declar) {this.name = namethis.age = agethis.declar = declar}let person = new Person('张嘉文', '3', '没有狐臭!')console.log(person);


this就指向了这个person对象
这里统一解释一下,new关键字到底干了什么

new关键字到底干了什么?

  • 在内存中开辟了一个新的空对象
  • 接着将新对象的原型指向构造函数的原型对象
  • 接着将构造函数中的this指向这个新对象
  • 判断构造函数有没有返回自己的对象,如果有自己返回的对象就返回
  • 否则返回这个开辟的新对象

说起来可能有点绕
通过代码可能会更好的理解,下面是我实现的new方法

function myNew(fn,...args){ //fn是构造函数 ...args是参数,es6剩余参数的写法//Object.create方法传入一个对象,创建一个新对象并将参入对象作为这个新对象的原型let obj = Object.create(fn.prototype) //将fn的this绑定为obj中的thislet fnObj = fn.call(obj,...args)//判断fn是否有自己return的对象,有就优先返回fn的,否则返回新的objreturn fnObj instance Object ? fnObj:obj
}

this的缺陷以及应对方案

this的一些缺陷,会导致初学者甚至很多有经验的开发者会前仆后继的写出一些bug,并且由于惯性思维而发现不了(对于先学习过其他语言的人来说,冲击更大)

我们看看this有哪些设计缺陷

嵌套函数中的this不会继承外层函数的this

我认为这是一个严重的设计失误,影响了很多开发者和初学者,并让他们迷失在这个错误中(包括我自己初学的时候,给我整懵逼了)

var myObj = {myname: '凯隐',showThis: function () {console.log(this)function bar() {console.log(this)}bar()},
}
myObj.showThis()

我们知道showThis这个函数通过隐式绑定,是指向myObj本身的,那么第一个打印语句毫无疑问是myObj,
那么,问题来了,bar函数中的this指向谁呢?
自己动手实验一下,发现打印出来window

也就是符合了我们的小标题嵌套函数中的this不会继承外层函数的this,不做特殊处理,它默认绑定到window
解决方法也很简单,就是使用作用域链或者箭头函数

var myObj = {myname: '凯隐',showThis: function () {console.log(this)let self = thisfunction bar() {console.log(self)}//bar = ()=>{console.log(self)} 箭头函数也是可行的bar()},
}
myObj.showThis()

将this用一个self变量保存,然后嵌套函数使用作用域链来查找到外层的this

普通函数中的this指向全局window

这个前面我已经说过了,默认情况下,函数中的this是默认绑定规则绑定到window的,这也是一个设计缺陷,因为我们的直觉想法是不希望这样做的,这回打破数据的边界,通过this,函数内访问甚至修改了全局状态下的一些数据,就会造成一些误操作
当然我们也有call,apply,bind方法来绑定这些函数中的this,就是多写点代码(程序员:确实是酱紫,不过掉点头发而已啦,都说了没有秃头,没有秃头!)

好啦,今天写到着,哦,对了,如果对我刚刚提到的原型和原型链不熟悉的话,可以去看看我之前的博客.或者自己查找资料理解,这里就不做多写了

各位彦祖再见

js中的this:从执行上下文的角度看this相关推荐

  1. JS深入--词法作用域、执行上下文与闭包

    文章目录 词法作用域 执行上下文与词法环境 闭包 闭包练习 作用域链 REF   个人博客文章同步地址 词法作用域   JS 使用的是词法作用域(或称为静态作用域),函数的作用域在定义的时候就决定了, ...

  2. 在Node js中实现任务调度与执行

    在Node.js中实现任务调度与执行 作者:chszs,未经博主允许不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 批处理是业务开发中经常会遇到的需求 ...

  3. 自学js第十天:JS对象和this和执行上下文

    复习数组基础方法 作用域 函数 小测试: <!DOCTYPE html> <html lang="en"><head><meta char ...

  4. JS中双层for循环执行顺序

    js中双层for循环的执行顺序 1 首先会先执行第一层循环,执行顺序如图所示.1:执行变量**(仅执行一次)**2:执行条件 3:执行代码块区域(注意,代码块中包含第二层循环) 4最后执行++. 2 ...

  5. JS中同名函数有效执行顺序

    html中如果出现函数同名时: 如果有多个外部引入的js文件,例如a.js和b.js(引入顺序假定是a.js,然后是b.js),同时html中本身也有内部的js. 那么针对 出现函数名一样的情况时,无 ...

  6. js中clearInterval的重新执行/重新开始

    问题描述:系统进入页面,可以通过setInterval的方式进行定时执行某一个任务,当使用clearInterval之后需要再次调用setInterval,却不能够再次让setInterval执行的函 ...

  7. js中的装饰器执行顺序

    /*** 执行顺讯* [(property)...]->[(parameter->method)...]->constructor->class* [属性...]->[( ...

  8. JS中单击多次执行一次的问题

    在对日的一个项目之中遇到一个要求,多次点击按键只执行一次的问题 下面是一个ajax的方法 点击之后变成 执行之后又恢复到可用状态 代码如下:只是个例子 //lockTarget: '#G707_cmd ...

  9. js中怎么写自执行函数

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

最新文章

  1. bBank 开源Javascript框架(最后更新:2010-7-6)
  2. 百度地图api改变覆盖物背景实例及css颜色值简介
  3. java商品展示页面代码_java学习(十四)实现商品的展示、curd以及分页展示
  4. 怎样删除oracle中的用户,Oracle 中删除已经连接的用户
  5. LeetCode Algorithm 3. 无重复字符的最长子串
  6. linux中项目部署和日志查看
  7. (34)VHDL实现T触发器
  8. Android中什么是Dex文件
  9. vm压缩linux vmdk文件,vmware下vmdk文件越来越大的解决方法探讨
  10. 怎么出家ajax假死状态,Ajax如何解决假死?
  11. 本人亲身实践,不要给软屏幕笔记本贴钢化膜(T_T)
  12. Windows Server 2008 简体中文 正式版 下载
  13. 解锁bitlocker码
  14. Echarts之饼图
  15. java :工资计算
  16. 6-10 两个字符串穿插 (10分)pta,c
  17. 微型计算机系统结构中的总线有哪三种,微机原理习题答案
  18. 医院业务系统设计(四) --- 患者管理之分诊系统
  19. 2022-2028年全球与中国射频识别打印机行业深度分析
  20. CAS latex模板中参考文献使用APA引用格式的解决方案

热门文章

  1. Python 直接读取 16进制 8进制 整数
  2. ExpandableListView的使用以及更换前边的图片
  3. 马斯克疯狂省钱:断供厕纸,辞退保洁,退租办公室
  4. 关于软件测试你需要知道的常见概念
  5. 数据库题目之数据库恢复技术
  6. Oracle数据库题目
  7. 清雅园智能办公系统 v0.3.1(源码)
  8. 百变冰冰!使用飞桨的PaddleGAN实现妆容迁移
  9. HDU 4940 Destroy Transportation system(无源汇上下界网络流)
  10. 使用 PowerDesigner 和 PDMReader 逆向生成 MySQL 数据字典