JavaScript this关键字的深入详解


Posted in Javascript onJanuary 14, 2021

一、前言

this关键字是JavaScript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有函数的作用域中。对于那些没有投入时间学习this机制的JavaScript开发者来说,this的绑定一直是一件非常令人困惑的事。

JavaScript this关键字的深入详解

二、了解this

学习this的第一步是明白this既不指向函数自身也不指向函数的词法作用域,你也许被这样的解释误导过,但其实它们都是错误的。随着函数使用场合的不同,this的值会发生变化。但总有一条原则就是 JS中的this代表的是当前行为执行的主体 ,在JS中主要研究的都是函数中的this,但并不是说只有在函数里才有this, this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用 。如何的区分this呢?

三、this到底是谁

这要分情况讨论,常见有五种情况:

1、函数执行时首先看函数名前面是否有".",有的话,"."前面是谁,this就是谁;没有的话this就是window

function fn(){
 console.log(this);
}
var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj
function sum(){
 fn();//this->window
}
sum();
var oo={
 sum:function(){
 console.log(this);//this->oo
 fn();//this->window
 }
};
oo.sum();

2、自执行函数中的this永远是window

(function(){ //this->window })();
 ~function(){ //this->window }();

3、给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的this是当前的元素,除了IE6~8下使用attachEvent(IE一个著名的bug)

DOM零级事件绑定

oDiv.onclick=function(){
 //this->oDiv
 };

DOM二级事件绑定

oDiv.addEventListener("click",function(){
 //this->oDiv
 },false);

在IE6~8下使用attachEvent,默认的this就是指的window对象

oDiv.attachEvent("click",function(){
 //this->window
 });

我们大多数时候,遇到事件绑定,如下面例子这种,对于IE6~8下使用attachEvent不必太较真

function fn(){
 console.log(this);
}
document.getElementById("div1").onclick=fn;//fn中的this就是#divl
document.getElementById("div1").onclick=function(){
console.log(this);//this->#div1
fn();//this->window
};

4、在构造函数模式中,类中(函数体中)出现的this.xxx=xxx中的this是当前类的一个实例

function CreateJsPerson(name,age){
//浏览器默认创建的对象就是我们的实例p1->this
this.name=name;//->p1.name=name
this.age=age;
this.writeJs=function(){
console.log("my name is"+this.name +",i can write Js");
 };
//浏览器再把创建的实例默认的进行返回
}
var p1=new CreateJsPerson("尹华芝",48);

必须要注意一点: 类中某一个属性值(方法),方法中的this需要看方法执行的时候,前面是否有".",才能知道this是谁 。大家不妨看下接下来的这个例子,就可明白是啥意思。

function Fn(){
this.x=100;//this->f1
this.getX=function(){
console.log(this.x);//this->需要看getX执行的时候才知道
 }
}
var f1=new Fn;
f1.getX();//->方法中的this是f1,所以f1.x=100
var ss=f1.getX;
ss();//->方法中的this是window ->undefined

5.call、apply和bind

我们先来看一个问题,想在下面的例子中this绑定obj,怎么实现?

var obj={name:"浪里行舟"};
function fn(){
console.log(this);//this=>window
}
fn();
obj.fn();//->Uncaught TypeError:obj.fn is not a function

如果直接绑定obj.fn(),程序就会报错。这里我们应该用fn.call(obj)就可以实现this绑定obj,接下来我们详细介绍下call方法:

call方法的作用:

①首先我们让原型上的call方法执行,在执行call方法的时候,我们让fn方法中的this变为第一个参数值obj;然后再把fn这个函数执行。

②call还可以传值,在严格模式下和非严格模式下,得到值不一样。

//在非严格模式下
var obj={name:"浪里行舟 "};
function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}
fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
//严格模式下 
fn.call();//在严格模式下this->undefined
fn.call(null);// 在严格模式 下this->null
fn.call(undefined);//在严格模式下this->undefined

**apply和call方法的作用是一模一样的,都是用来改变方法的this关键字并且把方法

执行,而且在严格模式下和非严格模式下对于第一个参数是null/undefined这种情况的规

律也是一样的。**

两者唯一的区别:call在给fn传递参数的时候,是一个个的传递值的,而apply不是一个个传,而是把要给fn传递的参数值统一的放在一个数组中进行操作。但是也相当子一个个的给fn的形参赋值。 总结一句话:call第二个参数开始接受一个参数列表,apply第二个参数开始接受一个参数数组

fn.call(obj,100,200);
fn.apply(obj,[100,200]);

bind:这个方法在IE6~8下不兼容,和call/apply类似都是用来改变this关键字的 ,但是和这两者有明显区别:
fn.call(obj,1,2);//->改变this和执行fn函数是一起都完成了

fn.bind(obj,1,2);//->只是改变了fn中的this为obj,并且给fn传递了两个参数值1、2,
 但是此时并没有把fn这个函数执行
var tempFn=fn.bind(obj,1,2);
tempFn(); //这样才把fn这个函数执行

bind体现了预处理思想:事先把fn的this改变为我们想要的结果,并且把对应的参数值也准备好,以后要用到了,直接的执行即可。

call和apply直接执行函数,而bind需要再一次调用。

var a ={
 name : "Cherry",
 fn : function (a,b) {
 console.log( a + b)
 }
 }
 var b = a.fn;
 b.bind(a,1,2)

JavaScript this关键字的深入详解

上述代码没有执行,bind返回改变了上下文的一个函数,我们必须要手动去调用:

b.bind(a,1,2)() //3

必须要声明一点:遇到第五种情况(call apply和bind),前面四种全部让步。

四、箭头函数this指向

箭头函数正如名称所示那样使用一个“箭头”(=>)来定义函数的新语法,但它优于传统的函数,主要体现两点: 更简短的函数并且不绑定this 。

var obj = {
 birth: 1990,
 getAge: function () {
 var b = this.birth; // 1990
 var fn = function () {
 return new Date().getFullYear() - this.birth; // this指向window或undefined
 };
 return fn();
 }
};

现在,箭头函数完全修复了this的指向, 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this 。

换句话说, 箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window 。

<button id="btn1">测试箭头函数this_1</button>
 <button id="btn2">测试箭头函数this_2</button>
 <script type="text/javascript"> 
 let btn1 = document.getElementById('btn1');
 let obj = {
 name: 'kobe',
 age: 39,
 getName: function () {
 btn1.onclick = () => {
 console.log(this);//obj
 };
 }
 };
 obj.getName();
 </script>

JavaScript this关键字的深入详解

上例中,由于箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。其实可以简化为如下代码:

let btn1 = document.getElementById('btn1');
 let obj = {
 name: 'kobe',
 age: 39,
 getName: function () {
 console.log(this)
 }
 };
 obj.getName();

那假如上一层并不存在函数,this指向又是谁?

<button id="btn1">测试箭头函数this_1</button>
 <button id="btn2">测试箭头函数this_2</button>
 <script type="text/javascript"> 
 let btn2 = document.getElementById('btn2');
 let obj = {
 name: 'kobe',
 age: 39,
 getName: () => {
 btn2.onclick = () => {
 console.log(this);//window
 };
 }
 };
 obj.getName();
 </script>

JavaScript this关键字的深入详解

上例中,虽然存在两个箭头函数,其实this取决于最外层的箭头函数,由于obj是个对象而非函数,所以this指向为Window对象

由于this在箭头函数中已经按照词法作用域绑定了,所以, 用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略 :

var obj = {
 birth: 1990,
 getAge: function (year) {
 var b = this.birth; // 1990
 var fn = (y) => y - this.birth; // this.birth仍是1990
 return fn.call({birth:2000}, year);
 }
};
obj.getAge(2018); // 28

扩展阅读

总结

到此这篇关于JavaScript this关键字深入详解的文章就介绍到这了,更多相关JavaScript this关键字内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jquery 表单取值常用代码
Dec 22 Javascript
JS判断数组中是否有重复值得三种实用方法
Aug 16 Javascript
js字符串截取函数substr substring slice使用对比
Nov 27 Javascript
js对字符串进行编码的方法总结(推荐)
Nov 10 Javascript
分享十三个最佳JavaScript数据网格库
Apr 07 Javascript
微信小程序 支付后台java实现实例
May 09 Javascript
js获取一组日期中最近连续的天数
May 25 Javascript
Vue.js 实现微信公众号菜单编辑器功能(二)
May 08 Javascript
详解通过源码解析Node.js中cluster模块的主要功能实现
May 16 Javascript
Angularjs实现多图片上传预览功能
Jul 18 Javascript
Vue插值、表达式、分隔符、指令知识小结
Oct 12 Javascript
javascript利用键盘控制小方块的移动
Apr 20 Javascript
Vue实现多页签组件
Jan 14 #Vue.js
如何在vue中使用HTML 5 拖放API
Jan 14 #Vue.js
Vue中引入svg图标的两种方式
Jan 14 #Vue.js
vue+element table表格实现动态列筛选的示例代码
Jan 14 #Vue.js
vue 递归组件的简单使用示例
Jan 14 #Vue.js
vue element和nuxt的使用技巧分享
Jan 14 #Vue.js
原生JavaScript实现随机点名表
Jan 14 #Javascript
You might like
法国:浪漫之都的咖啡文化
2021/03/03 咖啡文化
PHP中MVC模式的模板引擎开发经验分享
2011/03/23 PHP
php判断是否为json格式的方法
2014/03/04 PHP
ThinkPHP实现支付宝接口功能实例
2014/12/02 PHP
详解PHP数据压缩、加解密(pack, unpack)
2016/12/17 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
PHPMAILER实现PHP发邮件功能
2018/04/18 PHP
PHP pthreads v3在centos7平台下的安装与配置操作方法
2020/02/21 PHP
javascript引用对象的方法
2007/01/11 Javascript
IE下写xml文件的两种方式(fso/saveAs)
2013/08/05 Javascript
js读写cookie实现一个底部广告浮层效果的两种方法
2013/12/29 Javascript
jquery获取及设置outerhtml的方法
2015/03/09 Javascript
jQuery表单元素选择器代码实例
2017/02/06 Javascript
详解在Vue中通过自定义指令获取dom元素
2017/03/04 Javascript
js实现4个方向滚动的球
2017/03/06 Javascript
Vue实例中生命周期created和mounted的区别详解
2017/08/25 Javascript
微信小程序template模板实例详解
2017/10/27 Javascript
vue实现form表单与table表格的数据关联功能示例
2019/01/29 Javascript
JavaScript中.min.js和.js文件的区别讲解
2019/02/13 Javascript
layui表单提交到后台自动封装到实体类的方法
2019/09/12 Javascript
[03:57]《不朽》——2015DOTA2国际邀请赛—中国军团出征主题曲MV
2015/07/15 DOTA
Python插件virtualenv搭建虚拟环境
2017/11/20 Python
python对日志进行处理的实例代码
2018/10/06 Python
python针对不定分隔符切割提取字符串的方法
2018/10/26 Python
django 控制页面跳转的例子
2019/08/06 Python
Window10下python3.7 安装与卸载教程图解
2019/09/30 Python
超实用的 30 段 Python 案例
2019/10/10 Python
Pytorch: 自定义网络层实例
2020/01/07 Python
Python3 xml.etree.ElementTree支持的XPath语法详解
2020/03/06 Python
Python操作Jira库常用方法解析
2020/04/10 Python
关于Theano和Tensorflow多GPU使用问题
2020/06/19 Python
python 常见的排序算法实现汇总
2020/08/21 Python
CSS3绘制超炫的上下起伏波动进度加载动画
2016/04/21 HTML / CSS
2014年党的群众路线活动个人整改措施
2014/10/28 职场文书
python生成可执行exe控制Microsip自动填写号码并拨打功能
2021/06/21 Python
PYTHON基于Pyecharts绘制常见的直角坐标系图表
2022/04/28 Python