datagridview 当前上下文中不存在bind_全面解析JavaScript中this指向问题

9e5ee1961c337804acae5f7448dc63ba.png

this指向

参考文章:
* this

JavaScriptthis指向分为以下几种情况:

  • 普通函数或作为对象属性
  • 事件绑定
  • 构造函数
  • 箭头函数
  • call/apply/bind指定

下面我们来进行一一介绍

普通函数或作为对象属性

this取决于方法执行前面是否有“点”,有“点”的话,“点”前面是谁this就是谁,如果没有点的话,this指向window

const fn = function () {console.log(this);
};const obj = { name: 'OBJ', fn };fn();obj.fn();const fn1 = obj.fn;
fn1();

answer:

1. window
2. {name: 'OBJ', fn: function() {console.log(this)}} // obj
3. window

可以看到函数作为对象的属性被调用的时候,其this指向调用该函数的对象,否则其this指向window

事件绑定

在进行事件绑定的时候,事件绑定函数中的this是绑定事件的元素:

// 假设页面中有id为button的button元素
// var x = 100;
window.x = 100;
const fn = function () {console.log(this.x);
};
const obj = { x: 200, fn };
const $button = document.getElementById('button');
$button.x = 300;obj.fn();
const fn1 = obj.fn;
fn1();$button.addEventListener('click', fn);
$button.addEventListener('mouseenter', obj.fn);$button.addEventListener('mouseleave', function () {obj.fn();});

answer:

1. 200
2. 100
3. 点击button时:300
4. 鼠标移入button时:300
5. 鼠标移出时:200

但是需要注意的是,这里我们是在用户点击时,浏览器帮我们将点击事件的this指向绑定该事件的DOM元素。如果通过代码来触发对应的事件的话,我们可以通过call/apply/bind来指定其this

$button.click.call() // this为window,打印结果为100

构造函数(new Fn)

构造函数(new Fn)执行,函数中的this是当前类的实例,这是new关键字帮我们做到的:

var x = 100;
const Fn = function () {this.x = 200;console.log(this.x);
};const fn = new Fn();

answer:

1. 200

箭头函数

箭头函数中没有自身的this,所用到的this都是其最近父级上下文中的this

const fn = function () {console.log(this);setTimeout(() => {console.log(this);}, 1000);setTimeout(function () {console.log(this);});
};const obj = { x: 100, fn };obj.fn();

answer:

1. {x:100, fn: function() {...}} // obj
2. window
3. {x:100, fn: function() {...}} // obj

call/apply/bind改变this指向

call/apply/bind传入的第一个参数即为函数的this

var x = 100;
const obj = { x: 200, y: 200 };
const fn = function () {console.log(this.x);
};fn();
fn.call(obj);
fn.apply(obj);const fixedThisFn = fn.bind(obj);
fixedThisFn();

answer:

1. 100
2. 200
3. 200
4. 200
  • call在执行时,第一个参数为this指向,之后的参数为fn执行时的参数
  • apply在执行时,第一个参数为this指向,之后的参数为fn执行时的参数组成的数组,数组的每一项会和fn的每一个参数进行对应
  • bind在执行时,第一个参数为预先传入this指向,之后的参数为实际调用fn前预先传入的参数,返回值为一个函数fixedThisFnfixedThisFn内部会调用fn并指定其this指向

为了更深入的理解call/apply/bind是如何改变函数中this指向的,下面我们分别模拟实现这三个函数

call/apply/bind源码实现

根据前面的介绍,我们知道:当函数作为对象属性被调用时,this指向调用该函数的对象

const obj = { x: 100, fn () {console.log(this);} };
obj.fn(); // {x: 100, fn: function() {...}} => obj

利用JavaScript这个特性,我们可以将执行的函数作为call/apply的第一个参数context的属性,然后通过context来调用该属性对应的函数,函数的this便指向了context

call的源码模拟如下:

Function.prototype.myOwnCall = function (context, ...args) {const uniqueKey = new Date().getTime();// this为调用call方法的函数context[uniqueKey] = this;// 作为对象的方法被对象调用,this指向该对象contextconst result = context[uniqueKey](...args);delete context[uniqueKey];return result;
};

到这里,有的小伙伴可能已经发现了,如果call/apply传入的context不是对象呢?

首先我们看下mdncall方法的第一个参数的描述:

语法:function.call(thisArg, arg1, arg2, ...)
* thisArg
可选的。在function函数运行时使用的this值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定nullundefined时会自动替换为指向全局对象,原始值会被包装

接下来,我们对myOwnCall方法的第一个参数做如下处理:

function translateToObject (context) {// 可以通过 == 进行判断 context == null// null == undefined  => 2个等号是成立的// null,undefined => windowif (typeof context === 'undefined' || context === null) {context = window;} else if (typeof context === 'number') { // 原始值转换为包装对象context = new Number(context);} else if (typeof context === 'string') {context = new String(context);} else if (typeof context === 'boolean') {context = new Boolean(context);}return context;
}

myOwnCall方法中调用该函数:

Function.prototype.myOwnCall = function (context, ...args) {context = translateToObject(context);const uniqueKey = new Date().getTime();// this为调用call方法的函数context[uniqueKey] = this;// 作为对象的方法被对象调用,this指向该对象contextconst result = context[uniqueKey](...args);delete context[uniqueKey];return result;
};

apply的实现与call基本相同,只不过第二个参数是一个数组:

Function.prototype.myOwnBind = function (context, paramsArray) {context = translateToObject(context);const uniqueKey = new Date().getTime();// this为调用call方法的函数context[uniqueKey] = this;// 作为对象的方法被对象调用,this指向该对象contextconst result = context[uniqueKey](...paramsArray);delete context[uniqueKey];return result;
};

相比于call/applybind函数并没有立即执行函数,而是预先传入函数执行时的this和参数,并且返回一个函数,在返回的函数中执行调用bind函数并将预先传入的this和参数传入

bind的源码模拟:

Function.prototype.myOwnBind = function (context, ...outerArgs) {const fn = this;return function (...innerArgs) {return fn.call(context, ...outerArgs, ...innerArgs);};
};

精简版如下:

Function.prototype.myOwnBind = (context, ...outerArgs) => (...innerArgs) => this.call(context, ...outerArgs, ...innerArgs);
这里并没有实现通过new操作符来执行fn.bind(context)的操作,如果想知道其详细的实现过程,可以看我的这篇文章: JS进阶-手写bind

在深入理解call/apply/bind的实现原理后,我们尝试完成下面的测试:

function fn1 () {console.log(1);}
function fn2 () {console.log(2);}
fn1.call(fn2);fn1.call.call(fn2);Function.prototype.call(fn1);
Function.prototype.call.call(fn1);

answer:

1. 1
2. 2
3. 什么都不输出
4. 1

这里我们根据call的源码来进行推导一下Function.prototype.call.call(fn1),其它的执行过程类似:

// 1. 首先会将Function.prototype.call作为一个函数来执行它原型上的call方法
// 所以call方法内部:
//    this => Function.prototype.call
//    context => fn1
// 通过对象的属性来执行方法改变this指向
//    fn1[uniqueKey] = this(Function.prototype.call)
//    fn1[uniqueKey]() // 执行 Function.prototype.call方法,但是this是context
// 2. 在this为fn1的情况下执行Function.prototype.call方法
// 所以call方法内部:
//    this => fn1
//    context => window
// 通过对象的属性来改变this指向
//    window[uniqueKey] = fn1
//    window[uniqueKey]() // 执行fn1(),但是this是window

这里就是有笔者关于JavaScriptthis指向的相关内容的理解,希望能对阅读的小伙伴有所帮助

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity Scene为每一个游戏物体进行扩展编辑

2个月前还在忙碌的找实习工作,看见招聘信息上面有一条熟悉扩展Unity编辑器,配合美工编程. 自己动手写完这个代码时候,发现写代码就像弹钢琴多么神奇. TestEdit类: using UnityEngine; using System.Collections; using UnityEditor;[CustomEditor(typeof(Test))] public class …

esmini LongSpeedAction修改

esmini的LongSpeedAction,如果shape设置为step的话,DynamicsDimension实际就无效了, 因为step的意思就是直接设置,在LongSpeedAction的Start过程中设置完就结束了,Step就不在 执行。 如果我们在step的情况下&#x…

cc2530i2c可同时接受两个传感器的数据吗_汽车方向及维修_玉树沃尔沃S40方向机,宝马531电子方向机进水可以维修吗...

产品品牌:徐州永诚汽车方向及维修公司产品单价:最小起订:23供货总量:2交货期限:13发货城市:徐州玉树沃尔沃S40方向机,宝马531电子方向机进水可以维修吗 [qadxfh3k]凭以往的经验,像这种多个控制模…

Android项目实战视频教程_快递轨迹查询应用开发

基于Android平台的快递轨迹查询应用开发全程实录(MenuDrawer、DbUtils、HttpUtils、讯飞语音识别)课程分类:Android适合人群:中级课时数量:10(17节)课时用到技术:MenuDrawer、DbUtils、HttpUtils、讯飞语音…

OpenXLSX 字段读取问题

在读取excel的时候发现有些字段无法读取,通过把excel文件解压后对比发现,正常读取和不 能正常读取的字段在sharedString.xml中存储的格式有差异,取其中一个字段,如下图: 正常读取的 不能读取的 对比可以看到其区别&a…

六西格玛dfss_六西格玛系列知识之二:六西格玛管理的基本原理

六西格玛管理是一系统,其系统性体现在它既提出了面向产品和服务设计开发的六西格玛设计,又提出了面向流程、产品制造、服务提供的六西格玛改进方法。从当前的情况来看,六西格玛设计(DFSS)项目推进尚未形成统一的或得到…

XCode5.1.1怎样实现代码块自己主动排版

1.问题描写叙述 在开发中常常遇到代码块须要排版的情况,假设一行一行缩进就太out了,但在xcode中怎样实现还真不知道2.解决方法 2.1选中须要排版的代码块 2.2单击"Editor | Structure | Re-Indent"菜单项 2.3看看是不是已经得到想要的结果了&am…

数据库监控[Z]

--查看表锁 select * from sys.v_$sqlarea where disk_reads>100--监控事例的等待 select event,sum(decode(wait_Time,0,0,1)) "Prev", sum(decode(wait_Time,0,1,0)) "Curr",count(*) "Tot"from v$session_Wait group by event order by 4 …

Could NOT find XXX (missing: XXX_LIBRARY XXX_DIR)

有时候从github上下载一些新的包编译时经常会缺少一些插件或库文件,比如 Could NOT find Bullet (missing: BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY BULLET_SOFTBODY_LIBRARY BULLET_INCLUDE_DIR) 或者 Could NOT find SDL (miss…

minheight能继承吗_民法典亮点盘点 | 侄子能继承叔叔的遗产吗?

2020年5月28日,十三届全国人大三次会议表决通过了《中华人民共和国民法典》,《民法典》将于2021年1月1日起施行。《民法典》共7编,依次为:总则编、物权编、合同编、人格权编、婚姻家庭编、继承编、侵权责任编,以及附则…

多C++标准版本指定

本文主要介绍 CMake 中 include 指令的相关知识。 1 概述 引用 CMake 官网对于 include 指令的介绍&#xff0c;如下&#xff1a; Load and run CMake code from a file or module. include 指令的用法如下&#xff1a; include(<file|module> [OPTIONAL] [RESULT_VA…

STL容器存储的内容动态分配情况下的内存管理

主要分两种情况&#xff1a;存储的内容是指针&#xff1b;存储的内容是实际对象。 看以下两段代码&#xff0c; typedef pair<VirObjTYPE, std::list<CheckID>*> VirObj_CheckID_pair;class LangChecker{public: LangChecker(); ~LangChecker(); voi…

一分二功率分配器_一文学会微波功率分配器

功率分配器是微波电路设计中常用的一个无源元器件&#xff0c;简称功分器&#xff0c;顾名思义&#xff0c;就是把一路输出功率按照一定的比例分配成N路功率输出的一种微波元器件&#xff0c;如下图所示&#xff0c;A端口进入的信号分成两路信号从C1和C2端口输出。当然&#xf…

MinGW编译boost库

介绍 在windows下编译boost可以选择msvc和mingw两种方式&#xff0c;这里介绍使用mingw方式编译,msvc也是类似的操作 源码下载 例如1.48.0http://www.boost.org/users/history/version_1_48_0.html 使用不同的版本时将version_1_48_0替换即可 编译 编译 b2.exe 进入boost…

软件工程——理论、方法与实践 第一章

第一章 主要分为1、软件2、软件工程3、软件工程知识体系4、软件工程职业道德规范四部分。软件是人类思维创造的杰作,并成为人类现代生活的催化剂;第一部分软件对软件的特性、发展和软件的危机进行了讲解&#xff0c;主要描述软件是计算机程序、规程以及运行计算机系统可能需要…

高德地图定位精度多少米_中美俄卫星定位精度分别是多少?美0.1米,俄10米,中国呢?...

在古代&#xff0c;悬于天际的北斗七星就被那时候的人们当做辨别方向的工具&#xff0c;在现代&#xff0c;太空之中的北斗卫星也在为人们指引方向&#xff0c;为了这一天的到来&#xff0c;我们等待了太久太久。1993年发生的银河号事件里&#xff0c;美国宣称我国商船携带了违…

find_path、find_library备忘录

find_path用于查找包含的头文件&#xff0c;其查找顺序为&#xff1a;从CMAKE_INCLUDE_PATH中查找。从系统默认头目录即/usr/include、/usr/local/inlucde中查找。其中如果从CMAKE_INCLUDE_PATH中查找的话&#xff0c;需要在~/.profile或者/etc/profile中设置&#xff08;注意&…

Android成长日记-Android布局优化

Android常用布局 1. LinearLayout(线性布局) 2. RelativeLayout(相对布局) 3. TableLayout(表格布局) 4. AbsoluteLayou(绝对布局) 5. FrameLayout(帧布局) 低--------------使用量------------------高 4->3->5->1->2 Android布局原则 (1) 尽量多使用LinearLayout…

练字格子纸模板pdf_a4田字格练字纸打印版-练字标准田字格模板-a4打印版下载最新免费excel版-西西软件下载...

练字标准田字格模板-a4打印版是一份a4田字格练字模板下载&#xff0c;硬笔书法练习田字格模板-标准A4打印版、标准田字格模板-每日练字、硬笔书法练习田字格模板-标准A4打印版、标准田字格模板-A4打印、硬笔书法练习田字格模板可以使用。word制作练字田字格1、点击word“表格”…

vcpkg

git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install./vcpkg install curl[tool] ./vcpkg.exe install pthreads:x64-windows