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 相关文章推荐
javascript同步Import,同步调用外部js的方法
Jul 08 Javascript
Js组件的一些写法
Sep 10 Javascript
THREE.JS入门教程(2)着色器-上
Jan 24 Javascript
js获取select选中的option的text示例代码
Dec 19 Javascript
使用Javascript简单实现图片无缝滚动
Dec 05 Javascript
jquery.gridrotator实现响应式图片展示画廊效果
Jun 23 Javascript
在Mac OS下使用Node.js的简单教程
Jun 24 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
Dec 31 Javascript
通过jquery-ui中的sortable来实现拖拽排序的简单实例
May 24 Javascript
jquery实现点击a链接,跳转之后,该a链接处显示背景色的方法
Jan 18 jQuery
webpack4 css打包压缩问题的解决
May 18 Javascript
webpack4 SplitChunks实现代码分隔详解
May 23 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
PHP实现MySQL更新记录的代码
2008/06/07 PHP
php self,$this,const,static,-&amp;gt;的使用
2009/10/22 PHP
PHP中time(),date(),mktime()区别介绍
2013/09/28 PHP
php采集内容中带有图片地址的远程图片并保存的方法
2015/01/03 PHP
远离JS灾难css灾难之 js私有函数和css选择器作为容器
2011/12/11 Javascript
使图片旋转的3种解决方案
2013/11/21 Javascript
js中匿名函数的创建与调用方法分析
2014/12/19 Javascript
JavaScript使用push方法添加一个元素到数组末尾用法实例
2015/04/06 Javascript
用Angular实时获取本地Localstorage数据,实现一个模拟后台数据登入的效果
2016/11/09 Javascript
BootStrap按钮标签及基本样式
2016/11/23 Javascript
jQuery点击弹出层弹出模态框点击模态框消失代码分享
2017/01/21 Javascript
highcharts 在angular中的使用示例代码
2017/09/20 Javascript
NodeJs实现定时任务的示例代码
2017/12/05 NodeJs
JS+WCF实现进度条实时监测数据加载量的方法详解
2017/12/19 Javascript
JS实现左边列表移到到右边列表功能
2018/03/28 Javascript
jQuery实现的电子时钟效果完整示例
2018/04/28 jQuery
vue项目如何刷新当前页面的方法
2018/05/18 Javascript
js实现京东秒杀倒计时功能
2019/01/21 Javascript
js利用递归与promise 按顺序请求数据的方法
2019/08/30 Javascript
[15:57]教你分分钟做大人:斧王
2014/10/30 DOTA
python中os操作文件及文件路径实例汇总
2015/01/15 Python
Python用GET方法上传文件
2015/03/10 Python
使用Python实现BT种子和磁力链接的相互转换
2015/11/09 Python
python3实现表白神器
2019/04/09 Python
我就是这样学习Python中的列表
2019/06/02 Python
python自动化工具之pywinauto实例详解
2019/08/26 Python
2021年值得向Python开发者推荐的VS Code扩展插件
2021/01/25 Python
学前教育学生自荐信范文
2013/12/31 职场文书
初一科学教学反思
2014/01/27 职场文书
县优秀教师事迹材料
2014/01/31 职场文书
《忆江南》教学反思
2014/04/07 职场文书
听课评语大全
2014/04/30 职场文书
奥林匹克运动会口号
2014/06/19 职场文书
镇党委书记群众路线整改措施思想汇报
2014/10/13 职场文书
带你彻底理解JavaScript中的原型对象
2021/04/14 Javascript
TensorFlow的自动求导原理分析
2021/05/26 Python