this
在工作和学习的过程中,我们会一不小心的掉进this
的坑中,今天我们就来把这些坑给他填满,防止以后再入坑。
我们要填this
的坑,那么就要先来了解一下什么是this
。
this是什么呢?
this是一个关键字,被自动的定义在所有函数的作用域中。而且this的指向非常的多变,它会根据调用者的不同而不同。
this在不同环境下的指向
-
在全局的环境中
废话不多说,直接上代码
所以在全局的环境下,console.log(this); // window
this
的指向是window
。这句话看似没有问题,但是却一点不严谨,因为大家都知道,js
是分为两种模式的,一种是严格模式,一种是非严格模式。非严格模式我们已经试过了,那么接下来我们在看看在严格模式下的this
是什么吧。
在严格模式的全局环境下,this的指向也是window。'use strict' console.log(this); // window
但是,这只是在浏览器的环境下,要是在node的环境下呢?我们一起来看看
在node的环境下,运行一个js文件,里面的一切都是属于这个js文件的,所以this指向的是module.exports。console.log(this); //接下来的代码是模拟在控制台中输入(假设这段代码是写在understandThis.js文件中) node understandThis.js {} //返回了一个空对象
-
函数内部环境
全局环境下,我们知道了,现在就来看看在函数内,this的指向是什么吧。function understandThis() { console.log(this) } understandThis(); // window
在非严格模式的函数内,this的指向是window。我们在来看看在严格模式下的是什么情况。
'use strict' function understandThis() { console.log(this) } understandThis(); // undefined window.understandThis(); // window
在严格模式下,返回的是undefined,为什么会这样呢?
因为在严格模式下,你需要严格的写出调用函数的对象,不可以有任何的省略。 -
事件调用
接下来我们就看看,在事件调用下,this的指向吧。//html代码 <div class="one">1</div> <div class="two">2</div> //js代码 var one = document.getElementsByClassName('one')[0]; var two = document.getElementsByClassName('two')[0]; function This() { console.log(this); } one.onclick = This; //<div class="one">1</div> two.onclick = This; //<div class="two">2</div>
谁调用了This这个函数,那么this就指向谁。在严格模式中也是一样噢~
//html代码 <div class="one">1</div> <div class="two">2</div> //js代码 'use strict' var one = document.getElementsByClassName('one')[0]; var two = document.getElementsByClassName('two')[0]; function This() { console.log(this); } one.onclick = This; //<div class="one">1</div> two.onclick = This; //<div class="two">2</div>
-
构造函数中
function understandThis() { this.name = 'xiaoqi'; console.log(this); // {name: "xiaoqi"} } var a = new understandThis(); console.log(a); // {name: "xiaoqi"}
构造函数的调用方式和普通函数的调用方式不同,构造函数需要用new关键字来调用。当用new来调用这个函数的时候,会发生几件事件(隐式的):
- 在构造函数的内部创建一个名为this的空对象
- 然后在执行this.xxx = xxx;
- 最后,要是构造函数没有返回值的话,会默认的返回this对象;如果设置的返回值是值类型,那么还是会返回this对象,如果设置的是引用值(函数、对象、数组),那么就会返回该引用类型。null是一个例外,如果是null的话,还是返回this对象。
不管是在非严格模式下还是严格模式下,构造函数中的this指向都是使用该构造函数创建的实例对象。
-
对象
var obj = { fn: function() { console.log(this); } } obj.fn(); // obj var a = obj.fn; a(); // window var b = obj.fn(); // obj
this有一个特点,那就是谁调用它,那么它就指向谁。
明白这个道理之后,我们一个个来解答obj.fn(); // 因为这里是obj来调用它的,所以它当然指向obj啦 var a = obj.fn; a(); //这边在全局环境下(window)创建了一个变量a,然后把obj.fn存储到变量a里面去,但是没有执行。因为变量a是储存在window对象中的,所有a()相当于window.a(),所以最后输出的是window var b = obj.fn(); // 这边虽然也是在全局下创建了变量b,但是它存储的是obj.fn()执行之后的结果,还是obj调用了fn函数。
-
箭头函数
var obj = { a: 10, fn: function(){ console.log(this) console.log(this.a); }, fn1: () => { console.log(this) console.log(this.a) } } obj.fn(); // obj 10 obj.fn1(); // window undefined
从上面的代码可以看出来,普通函数的this还是谁调用它,那么就指向谁;而箭头函数的this却不一样,虽然是obj调用它,但是它还是指向的window。难道在箭头函数内,this是指向window的吗?我们在来看看下面的代码
var obj = { b : function () { (() => { console.log(this); })() } } obj.b(); // obj
这段代码中的this指向了obj,为什么会这样呢?
因为箭头函数它本身是没有this的,它会捕获自己位置上下文的this值来作为自己的一个this值。因为这段代码中,是obj调用了b函数,让b函数里面的this指向了obj,然后被箭头函数所捕获,所以此刻箭头函数内的this也变成了obj。
-
call() 、apply() 、 bind()
有没有什么方法,可以改变this的指向呢?当然有啦,今天他们来了!首先出场的是call()
var obj = { b : function (a, b, c) { (() => { console.log(this, a, b, c); })() } } var box = {}; obj.b.call(box,1 ,2 ,3); // {} 1 2 3
原本this应该是obj的,但是用call改变了this的指向,现在this指向了box。我们再来看看它的兄弟appy()
var obj = { b : function (a, b, c) { (() => { console.log(this, a, b, c); })() } } var box = {}; obj.b.apply(box,[1 ,2 ,3]); //// {} 1 2 3
他们两个除了传参列表的不一样,其他都差不多。
方法 第一位参数 剩余参数 call() 改变this指向的参数 把实参按照形参的个数传进去 apply() 改变this指向的参数 传入一个数组(arguments) 现在我们再来看看bind(),它和其它两个都不一样,具体哪里不一样呢?看下面的代码
var obj = { a: 1 } var obj1 = { b: function() { console.log(this.a) } } obj1.b.bind(obj)(); // 1
原本在obj1的作用域中是没有a这个属性的,直接执行obj1.b()会返回undefind,现在通过bind方法改变this的指向,让this指向为obj,这样输出的就是obj里面a的属性值了。而且bind返回的结果是一个函数,你需要再去执行它。
后续我会再写一篇关于 call() 、apply() 、 bind() 的笔记,记得关注哟~