文章导图先来一下:
一、this 初探 ???
1、为什么要使用this?
如何更好的理解this,我想需要先了解this解决了什么问题。在写文章的过程中也看了一些其他人写的文章和博客,其中大都是将this是什么,this的使用场景,this的指向。当然这些是需要掌握的一些this知识点。但在开篇之前想先提个问题?
你知道javascript中为什么需要this吗?
看到这个问题,我在心里想的就是,this是一个执行上下文的属性,在调用时确定指定什么,但直接问我javascript为什么需要this,可能我一下回不上来。下面就来理一理吧
让我们先来看看下面这个例子:
var person = {
name:'jingda',
age:'23',
phone:'1234567',
FuncHi: function() {
//实现功能:打印出「你好,我是 jingda,今年 23 岁」
},
FuncBye: function() {
//实现功能:打印出「再见,记得我叫 jingda ,我的电话是1234567
}
}
上面这段代码定义了一个person对象,其中有name,age,phone三个属性,以及FuncHi,FuncBye两个方法,各自实现自己的功能。我们可以看到,此时的两个方法中并没有传递任何参数,因此它是无法达到输出这个对象的属性的,当我们尝试采取一些方法,比如说去传递参数,我们可以把代码写成这样:
var person = {
...
FuncHi: function(name, age){
console.log('你好,我是 ${name},今年 ${age} 岁')
},
FuncBye: function(name, phone){
console.log('再见,记得我叫 ${name} 我的电话是 ${phone}')
}
person.FuncHi(person.name, person.age)
person.FuncBye(person.name, person.phone)
可以在控制台打印
这种传递参数的方式虽然能实现功能,但使用起来就知道,它让代码变得很笨,比如你看调用过程的代码。
于是我们尝试改进一下调用时的代码,我们可以在调用的时候把person当做参数传入到两个方法中,当方法里需要用到person当中的属性时,自己取就可以了。 于是代码改成这样:
var person = {
...
FuncHi: function(p){
console.log('你好,我是 ${p.name},今年 ${p.age} 岁')
},
FuncBye: function(p){
console.log('再见,记得我叫 ${p.name} 我的电话是 ${p.phone}')
}
person.FuncHi(person);
person.FuncBye(person);
这样似乎比刚才的代码,参数缩短了很多。
但是开发者可能最终更想要的代码是
var person = {
...
FuncHi: function(){
console.log('你好,我是 ${this.name},今年 ${this.age} 岁')
},
FuncBye: function(){
console.log('再见,记得我叫 ${this.name} 我的电话是 ${this.phone}')
}
person.FuncHi();
person.FuncBye();
这么一下就把this丢出来是个啥意思嘞?实际上 this 是隐藏的第一个形参。在你调用 person.FuncHi() 时,这个 person 会「变成」 this。也就是说,参数隐藏了,而我们使用的this.
相当于person.
但this远远不会就只是由对象.
出来的,会涉及很多情形,下面再聊哈。
2、你对this如何定义?
首先要理解的就是单单这个“this”,在javascript中到底指的是什么呢?这里我参考了 MDN上的解释,得出的结果:
(话说,我能有什么定义,打扰了。。。)下一个:
二、不同场合下的this指向 ???
在这一节会涉及很多this使用场景,因为在浏览器和node下执行的结果可能并不一致,所以这里如果没有特殊说明,即运行在浏览器中。
自己也可尝试哦
1、全局上下文
- 浏览器
- node
console.log(this) //{}
2、函数上下文
在非严格模式下:
- 浏览器
因为this 的值不是由该设置的,所以 this 的值默认指向全局对象,浏览器中就是 window。
- node
function f() {
return this;
}
console.log(f() === globalThis) //true
console.log(globalThis) //Object [global]
在严格模式下
浏览器和node下都是undefined:
function f() {
"use strict";
return this;
}
console.log(f() === undefined) //true
这里在严格模式下,如果进入执行环境没有设置this的值,this会保持为undefined,就像上面这个例子,f()被直接调用,不是作为对象的属性或者方法调用。
3、构造函数
当一个函数用做构造函数时,也就是使用了new关键字的时候,它的this就被绑定到正在构造的新对象。 首先来看看构造函数是如何工作的?
function constructorFun() {
this.name = 'jingda'
//当然你可以设置更多值
//当函数具有返回对象的return语句时
//该对象是new表达式的结果
//否则,表达式的结果是当前绑定到this的对象
}
好吧,这里自觉的想到了new的实现原理:
1、创建一个新的空的对象
2、把这个对象链接到原型对象上
3、这个对象被绑定为this
4、如果这个函数不返回任何东西,那么就会默认return this
但是new这个关键字,并不是所有返回值都原封不动地返回的。如果返回的是undefined,null以及基本类型的时候,都会返回新的对象;而只有返回对象的时候,才会返回构造函数的返回值。
好了,new说到这里,继续我们的this,来看下面两个例子:
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a);//37
function C2() {
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a);//38
因为C中没有return对象,所以this就是指向的创建的实例 ,o.a就是37,而在C2,因为在调用构造函数的过程中,手动的设置了返回对象,与this绑定的默认对象被丢弃了,此时this就是指向return返回的对象,o.a为38
4、原型链中的this
首先大致理解一下原型链
JavaScript 常被描述为一种基于原型的语言
——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain)
来个例子:
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f());//5
我们通过Object.create(o),指定o作为原型对象,创建出了p这个对象实例。但是在对象p上并没有属于它自己的f属性,这个f属性继承于它的原型,也就是o。
5、对象方法
当函数作为对象里的方法被调用时,this 被设置为调用该函数方法的对象。该函数方法赋值给另一个对象,就会改变this的指向。
var obj = {
name:"jing",
foo: function () {
return this.name;
}
};
console.log(obj.foo()); //jing
当 obj.foo() 被调用时,函数内的this 将绑定到 o 对象。
需要注意的问题!!
先改一下上面的代码,以便理解:
var obj = {
foo: function () {
console.log(this)
}
};
obj.foo();//指向obj
- 当函数作为一个值,而不是对象的属性或是方法
(obj.foo = function () {
console.log(this);
})()
//指向全局
- 当this所在的方法不是在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
var a = {
p: 'jing',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() //undefined
a.b.m方法在a对象的第二次,该方法的内部this不是指向a,而是指向a.b,如果我们需要打印出p的值:
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
a.b.m() //'Hello'
- 将嵌套对象内部的方法赋值给一个变量,this依然会指向全局对象。还是上面的例子,当我们这么写:
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
上面这个例子,m是多层对象内部的一个方法,如果将它赋值给hello变量,this指向了顶层对象。如何解决这个问题,可以只将m所在的对象
赋值给hello,这样调用时,this的指向就不会变。
var hello = a.b;
hello.m() // Hello
6、作为一个DOM事件处理函数
当函数被用作事件处理函数时,它的 this 指向触发事件的元素
7、箭头函数下的this
最后不得不提的是箭头函数有关this指向的问题:
1、箭头函数没有单独的this,箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。因此,在下面的代码中,传递给setInterval的函数内的this与封闭函数中的this值相同:
function Person(){
this.age = 1;
setInterval(() => {
this.age++; //正确地指向 p 实例
}, 1000);
}
var p = new Person();
console.log(p.age);
2、严格模式下不是输出undefined
var f = () => { 'use strict'; return this; };
f() === window; // 或者 global
3、使用call(),apply()方法调用一个函数,只能传递参数,不能绑定this
var person = {
age:18,
sum: function(a) {
var f = a => a + this.age;
return f(a);
},
sumCall: function(a) {
var f = a => a + this.age;
var b = {
age:20
};
return f.call(b,a)
//call的第一个参数是需要绑定到的对象,上,如果成功的话,指向的b age为20,但是下面输出的是19,并没有绑定到b上
}
}
console.log(person.sum(1));//19
console.log(person.sumCall(1));//19
输出两个都是19,也就是说在sumCall中,f.call(b,a),本来想把this绑定到b上,没有成功。
三、this使用需要注意的点 ???
1、避免多层this(包括数组处理方法中的this)
因为this的指向是不确定的,所以尽量不要在函数中包含多层this:
var o = {
name:'jing',
f1: function() {
console.log(this.name);
var f2 = function () {
console.log(this.name);
}();
}
}
o.f1()
//node 下执行
// jing
// undefined
f2中的this丢失了,指向了全局。
如果要避免,则需要在f2中改用一个指向外层的变量。
var o = {
name:'jing',
f1: function() {
console.log(this.name);
var that = this;
var f2 = function() {
console.log(that.name);
}();
}
}
o.f1()
//jing
//jing
2、 还有一个就是数组map和foreach方法,
允许提供一个函数作为参数。这个函数内部不应该使用this。会造成和上面一样的丢失this现象,像下面这个例子,f可以去到o的this,但是f中的forEach方法中的this丢失了,指向的全局。因此为undefined。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
//node 下执行
// undefined a1
// undefined a2
浏览器中输出这个东西???
解决的方法:
- 1、需要在中forEach改用一个指向外层的变量
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
- 2、将this当作foreach方法的第二个参数,固定它的运行环境
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
3、null undefined传入call apply bind
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null) //2
这里的call方法传递的参数是null,this指向的是全局 (严格模式下是undefined)
四、测试几个题目(答案在结尾)✍️✍️✍️
1、对象属性方法相关
const object = {
message:'hello,world',
getMessage() {
const message = 'hello,earth';
return this.message;
}
};
console.log(object.getMessage());
不熟悉的话可以看 场合5,此时调用的是object中的this。
2、原型链相关
function Person(name) {
this.name = name;
this.getName = () => this.name;
}
const cat = new Person('jing');
console.log(cat.getName());
const { getName } = cat;
console.log(getName());
3、setTimeout()延迟相关
const object = {
message:'hello world',
logMessage() {
console.log(this.message);
}
};
setTimeout(object.logMessage,1000);
4、箭头函数相关
const object = {
who: 'World',
greet() {
return `Hello, ${this.who}!`;
},
farewell: () => {
return `Goodbye, ${this.who}!`;
}
};
console.log(object.greet());
console.log(object.farewell());
5、指向window
var length = 4;
function callback() {
console.log(this.length);
}
const object = {
length:5,
method(callback) {
callback();
}
};
object.method(callback,1,2);
6、最后一个前两天看到的
var myObject = {
foo:"bar",
func:function() {
var self = this;
console.log(this.foo);
console.log(self.foo);
(function() {
console.log(this.foo);
console.log(self.foo);
}());
}
}
myObject.func();
答案(可以自己运行哈)
有任何问题可以评论区回复,也可以自己查资料,看小黄书。
好啦,this就先到这了。文章也是看了书看博客看题,总结的一些东西,可能写的并不系统。也可能我理解的不好的地方,欢迎评论区批评建议。
我是婧大,一名大三学崽,你的建议和点赞是给我最大的鼓励。?
目前在默默学习中,也在寻找合适的实习岗位,如果有小伙伴一起学习,欢迎加我wx:lj18379991972 ???
参考链接:
你不知道的javascript (看书哦,this节)
MDN this
网道 javascript教程 this关键字
饥人谷 -- JS 里为什么会有 this
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!