学习笔记(三):JavaScript中的this
this
调用位置:顾名思义,调用位置就是寻找函数被调用的位置,换句话说就是找到当前正在执行的函数的前一个调用,调用位置决定了this的绑定。
调用栈:为了达到当前执行位置所调用的所有函数,调用位置就在当前正在执行函数的前一个调用中。
this的绑定规则
默认绑定:
通过调用位置来决定默认绑定的对象是谁;
函数运行(也可以理解为函数体存在与)在严格模式下无法将this绑定在全局对象上,它绑定的是undefined,但是函数调用在严格模式下时则不影响;
在严格模式下调用函数不会影响this的默认绑定:
function fn () {console.log('--->', this.a) } var a = '我是a'"use strict" fn() // ---> 我是a
严格模式下运行,则会限制默认绑定:
"use strict"function fn () {console.log('--->', this.a) } var a = '我是a' fn() // TypeError: Cannot read properties of undefined (reading 'a')
隐式绑定
- 是否被某个上下文对象所“拥有”或“包含”
- 对象的属性引用链的生效范围只在最后一层起作用,换句话说就是this只绑定在上一层对象的作用域中。
const obj1 = {a: 'obj1---a',fn: fn }const obj2 = {a: 'obj2---b',obj: obj1 }function fn () {console.log(this.a) } // 只查找了上一层 console.log(obj2.obj.fn()) // obj1---a
- 被隐式绑定的函数会丢失绑定对象从而应用默认绑定(函数传参也会导致this丢失)
const obj1 = {a: 'obj1---a',fn: fn }const obj2 = {a: 'obj2---b',obj: obj1 }function fn () {console.log(this.a) }var a = '全局a' const copyFn = obj2.obj.fn // 实际引用的是函数本身 console.log(copyFn()) // 全局a
显示绑定
- 通过
call
、apply
、bind
指定要绑定的this对象。
如果第一个参数传入的是一个基本类型,会将其转换成它的对象形式;// 传入基本类型 String.prototype.num = 2 function handelNumber (val) {console.log(this) // String {''}return this.num += val // 4 } console.log(handelNumber.call('', 2)) // 4;
- 硬绑定:属于显示绑定的一个变种方法,与之相对的还有软绑定。实现方式是在函数内部通过
bind
或另外两种显示绑定的方式强制绑定到指定对象上,后续不管如何调用,都在内部执行这一步骤;
创建一个可以复用的辅助函数:const obj = {num: 0 }function handelNumber (val) {return this.num += val }function costomBind (fn, obj) {return (...args) => {return fn.apply(obj, args);} }const next = costomBind(handelNumber, obj) const a = next(1) const b = next(2) const c = next(3) console.log(a, b, c); // 1 3 6
- new 绑定
要知道new绑定的原理,首先要清楚new操作符做了什么:
- 创建一个新的对象;
- 将构造函数的protptype指向这个新对象;
- 新对象会绑定到函数调用的this,换句话说就是构造函数的this会指向这个新对象;
- 返回这个新对象。
function customNew (fn, ...args) {// 创建一个新对象const obj = {};// 将构造函数的protptype指向这个新对象obj.__proto__ = fn.prototype;// 绑定this到这个新对象上fn.call(obj, ...args);// 返回新对象return obj };function fn1() {this.a = '111' };const obj1 = customNew(fn1); const obj2 = new fn1(); console.log(obj1); console.log(obj2);
new是能够影响到this绑定行为的最后一种方式。
优先级
- new 绑定(new操作符的底层实现上,对硬绑定函数做了判断,如果被new调用则会用新的this替换硬绑定函数的this);
- 显示绑定;
- 隐式绑定;
- 默认绑定。
绑定例外
- 如果在使用
call
、apply
、bind
时,第一个参数传入null或undefined,那么this会被忽略,实际上应用的是默认绑定。 - 如果函数不关心this,这种方式是一个很好的选择。
function foo (a, b) {console.log(a, b); } const bar = foo.bind(null, 2) console.log(bar(3)) // 2 3;
- 这种方式的弊端在于某个函数确实使用了this,但传入null默认绑定的是全局对象,会造成难以追踪的bug。为了解决这个问题,可以使用另一种更安全的this,空对象委托,这在《你不知道的JavaScript 上》中称之为DMZ对象。将this绑定到这个对象上不会产生任何副作用,因为所有的this都会限定在这个对象中:
- 创建一个真正的空对象,最简单的方式是
Object.create(null)
,看起来跟{}
很像,但Object.create(null)
并不会创建prototype,所以它比{}
更空。
const obj1 = Object.create(null) // 无属性 const onbj2 = {} // [[Prototype]]: Object
- 创建一个真正的空对象,最简单的方式是
- 如果在使用
间接引用
- 在函数赋值时,容易造成间接引用。
function foo () {console.log(this);console.log(this.a); }var a = 3; const obj1 = { a: 1, foo: foo }; const obj2 = { a: 2 };(obj2.foo = obj1.foo)() // 调用位置是foo
- 在函数赋值时,容易造成间接引用。
软绑定
- 相对于硬绑定无法再次修改this,软绑定可以同时支持隐式绑定和显示绑定再次修改this;
- 下面是《你不知道的JavaScript 上》中软绑定的代码实现:
if (!Function.prototype.softBind) {Function.prototype.softBind = function (obj) {var fn = this;console.log('fn--', fn);var curried = [].slice.call(arguments, 1); // 获取第一个参数之后的所有参数。var bound = function () {return fn.apply((!this || this === (window || global)) // 判断是否传入的是可用this(不能是null或者undefined),或不能是全局对象。? obj // 如果是null或全局对象,默认绑定在初始化传入的对象上。: this, // 允许后续隐式或显示的修改this的绑定。curried.concat.apply(curried, arguments) // 柯里化);}/*** 1. 这里是为了将绑定对象上的prototype拷贝到新制定的对象上* 2. 注意是拷贝不是引用,新对象上修改prototype上的属性不会影响到其他对象*/bound.prototype = Object.create(fn.prototype);return bound;} }
- 测试一下是否达到上面说的效果:
function foo () {console.log('name:', this.name); };var obj = { name: 'obj' }; var obj2 = { name: 'obj2' }; var obj3 = { name: 'obj3' };var fooObj = foo.softBind(obj) fooObj() // obj fooObj.call(obj2, '111', 2, [1]) // obj2 fooObj.call(obj3, '111', 2, [1]) // obj3
this的词法
- 无法适用this规则:箭头函数;
- 箭头函数不能使用this的四种规则,而是根据外层(函数或者全局)作用域来决定this;
function foo () {return () => {console.log(this.a);} } var a = 0 const obj = { a: 1 } const obj2 = { a: 2 }const bar = foo.call(obj) bar.call(obj2) // 1
- 上面例子可以看到箭头函数的 this 永远指向其上下文的this,任何方法都改变不了其指向,如 call() , bind() , apply() ,new也不行!
总结
- 判断this绑定,需要找到函数的调用位置,并根据this的四种绑定规则:
- new调用,绑定到新对象;
- 显示绑定调用,绑定到指定对象;
- 上下文调用,绑定到上下文对象;
- 默认:函数体(注意是函数体而不是函数调用位置)处于严格模式下时绑定到undefined,否则绑定到全局对象。
- 箭头函数不应用与this的绑定规则,而是继承外层函数的this绑定。跟ES6之前的self = this一样。
学习笔记(三):JavaScript中的this相关推荐
- html5学习笔记---05.JavaScript 中的面向对象,继承和封装
05.JavaScript 中的面向对象 a.创梦技术qq交流群:CreDream:251572072 a.JavaScript 是一种基于对象的语言 类:JavaScript 对象很抽象,所以下 ...
- 【theano-windows】学习笔记三——theano中的导数
前言 就一个NN而言,包含梯度.偏置.参数更新,而前面第一篇博客学习了theano中符号变量的定义, 第二篇博客学习了变量的随机初始化, 变量之间的互相操作(类似于sigmoid(w∗x+b)sigm ...
- docker学习笔记(三)docker中的网络
目录 Linux中的网卡 Network Namespace Docker中的Bridge网络 使用自定义Bridge网络创建容器 Container中的其他网络 Host网络 None网络 Linu ...
- 【theano-windows】学习笔记十七——梯度中的consider_constant
前言 主要是在写玻尔兹曼机相关的theano时, 在计算梯度grad的时候发现一个参数名字叫做consider_constant,来看看这个到底做了什么事情 参考博客: using consider_ ...
- Mr.J-- jQuery学习笔记(三十二)--jQuery属性操作源码封装
扫码看专栏 jQuery的优点 jquery是JavaScript库,能够极大地简化JavaScript编程,能够更方便的处理DOM操作和进行Ajax交互 1.轻量级 JQuery非常轻巧 2.强大的 ...
- 【AngularJs学习笔记三】Grunt任务管理器
为什么80%的码农都做不了架构师?>>> #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...
- J2EE学习笔记三:EJB基础概念和知识 收藏
J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...
- tensorflow学习笔记(三十二):conv2d_transpose (解卷积)
tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...
- Ethernet/IP 学习笔记三
Ethernet/IP 学习笔记三 原文为硕士论文: 工业以太网Ethernet/IP扫描器的研发 知网网址: http://kns.cnki.net/KCMS/detail/detail.aspx? ...
- Spring学习笔记(三) AOP_annotation,AOP_XML
在学习课程以前,听说AOP有种很神秘的感觉,好像很好深的技术.其实原理很简单,使用动态代理的方式给程序增加逻辑.与此相似的有struts2中的filter拦截器. 再讲AOP之前先把需求说一下: 同S ...
最新文章
- 五分钟了解机器学习十大算法
- Android LruCache 压缩图片 有效避免程序OOM
- hdu 3863 No Gambling (不会证明,但是是对的,,)
- 启动Cognos时报0106错误
- 会员连锁配置以及金额走向
- mysql安装主从配置_MySQL安装与主从配置
- 基于交换机的PC端网络通信
- django mongodb mysql,Django MongoDB Django NoSQL方案
- 软考设计师15-数据结构01
- 详解:MySQL数据库的权限管理和运维实操
- 94G的kindle电子书btsync分享
- 九、Kali Linux 2 社会工程学工具
- 医疗行业源代码该如何保密
- 国开大学c语言程序设计形考任务1,国开电大《数控编程技术》形考任务1-4答案...
- php max file uploads,php上传多文件max_file_uploads限制问题
- Python_计算加速度
- 拇指玩安装器提示“存储卡空间不足”的解决办法
- 使用电脑麦克风的时候发现有杂音怎么办
- android电话本导入iphone,换手机之后安卓通讯录怎么导入iphone手机
- matlab-频响函数
热门文章
- c语言编程cos怎么用,cos x的taylor公式用c语言如何编程
- 本人每天懒床(起不来)问题的分析及相关的想法
- linux文本编辑的几种退出方法
- matlab阵风仿真,基于matlab风力发电系统的建模与仿真毕业设计论文
- 模具的分类,模具的等级,你知道吗,你知道什么是软模和硬模吗?
- 快速幂算法及其在动态规划中的应用(矩阵幂)
- Windows下安装cab文件
- C/C++经典项目:用C++制作在线考试系统(附源码)
- NPC中点箝位三电平 SVPWM控制 T型三电平 羊角波调制
- 1,Linux命令行设置中文显示