V少JS基础班之第八弹
文章目录
- 一、 前言
- 二、本节涉及知识点
- 三、重点内容
- 1、从新的角度认识this
- 2、this是函数的参数
- 3、this的值
- 4、函数的调用
- 1- 裸函数调用
- 2- 函数作为构造函数调用
- 3- 函数作为对象的方法调用
- 4- 函数显示调用
- 5- 箭头函数
一、 前言
第八弹内容是this。this相对来说难度不大,外面都说this背诵多过于理解,但是今天我们在这篇文章里由浅入深的理解一下this。文章更新有点慢,脑子一热跑去学后端去了。最近赶赶进度,把之前丢的给补回来。
本系列为一周一更,计划历时6个月左右。从JS最基础【变量与作用域】到【异步编程,密码学与混淆】。希望自己能坚持下来, 也希望给准备入行JS逆向的朋友一些帮助, 我现在脸皮厚度还行。先要点赞,评论和收藏。也是希望如果本专栏真的对大家有帮助可以点个赞,有建议或者疑惑可以在下方随时问。
先预告一下【V少JS基础班】的全部内容,我做了一些调整。看着很少,其实,正儿八经细分下来其实挺多的,第一个月的东西也一点不少。
第一个月【变量 、作用域 、BOM 、DOM 、 数据类型 、操作符】
第二个月【函数、闭包、原型链、this】
第三个月【面向对象编程、 异步编程、nodejs】
第四个月【密码学、各类加密函数】
第五个月【jsdom、vm2、express】
第六个月【基本请求库、前端知识对接】
==========================================================
二、本节涉及知识点
函数的参数、对象、 this
==========================================================
三、重点内容
市面上很多资料都说:this的学习记忆多于理解。 只有记住了各种使用方法才能灵活运用。所以经常会有一批人上来就去背诵this的五种绑定方式:
默认绑定,隐式绑定,显示绑定,new绑定和箭头函数绑定。
刚开始我也是这么学习的,但是说实话,这种靠背诵学习知识点的方式并不适合爬虫逆向。
今天,我们还是以理解为主,让我们开始今天的this学习。
1、从新的角度认识this
首先,学习这篇文章时,请丢掉在外面学习的关于this的任何知识。无论是python中的self,还是c++中的指针。
- 在this的学习中,我们最先接触的:
- 第一个概念就是函数。
- 函数是什么,不知道的去看我之前关于函数的章节。
- 第二个要接触的概念就是函数的参数
- 一个函数可以传参也可以不传参
- 第一个概念就是函数。
function say_hello(){console.log('hello world!')
}function sum_op(a, b) {return a + b;
}// 调用示例
say_hello()
console.log(sum_op(3, 5)); // 输出 8
但是, 有一个参数, 无论函数传不传参,它都在那。我们不用赋值,只要函数调用时我们就可以使用的一个参数:this
那到这里,我们都能理解的话,那我们已经能掌握this了。 如果我们能理解函数的参数,那理解this就是很简单的事。
我们丢掉我们无法理解的这句话:this是函数执行上下文里的隐藏变量
只需要记住这句话:
- this是函数调用时的隐藏参数
- 1- this是什么:this是个参数
- 2- this的值: 对象 或 undefined
- 3- 什么时候用:函数调用时使用
我们今天的大部分内容都围绕着这三个点展开
2、this是函数的参数
我们认识this的途径不止从上下文中来,还可以从函数的参数的角度。
this是作为函数的隐藏参数在函数调用时传递。 函数在定义时我们给定一个形参,而只有在函数调用时,他才会有值。
甚至我们都不用去给他定义形参,在函数调用时,我们随时可以使用this。hook住指定调用时的函数,随意的就能取到this的值
3、this的值
this的值只会有两种类型: 对象 & undefined
在严格模式下,裸函数调用, this的返回值就是undefined。 在非严格模式下,裸函数调用返回的是window。 window本身就是一个对象。 那其他情况下也是如此,this要么是对象,要么是undefined。
4、函数的调用
说了这么多,其实就是一句话的事情。 那么我们学习this到底是在学习什么呢?那就是现在要说的。 函数的调用。 我们都说了:this是函数调用时的隐藏参数,参数的值只能是对象或者undefined。 那么什么情况下this是对象什么情况下是undefined。 我们如何判断this的值呢。 这就是我们要学习的,函数的几种调用方式。
函数的各种调用方式:
1- 裸函数调用
“裸函数调用”其实就是最直接、最普通的函数调用方式,没有通过对象、call/apply/new 等手段修饰,只是单纯调用函数本身。
function foo() {console.log(this);
}// 裸函数调用
foo();
为什么会有严格和非严格模式下值不同的区别。
其实很简单,严格模式下,是真正的裸函数调用。 没有任何对象调用它,函数自己执行了。此时,是没有任何对象传递的,所以,此时的this是undefined。而在非严格模式下,浏览器环境中,所有的属性和方法都是挂在在window上的,所以非严格模式下,浏览器中的裸函数调用不是真正的裸函数,而是window.fn()
同理,在不同的环境中裸函数调用的this值也是不一样的。比如在浏览器中this为window,而在nodejs的全局中this的值则为global
这就是this背诵点之: 默认绑定
很多课件上来就说,this有五种绑定,我们背诵一下第一种绑定:默认绑定。默认绑定就是:当一个函数被调用时,如果 没有明确指定 this(没有作为对象方法调用、没有 call/apply/bind、没有 new 构造),那么 this 会按照 默认规则绑定。
我觉得这是一种本末倒置的学习方式,如果我们要理解默认绑定,因为由以下方式理解。
什么是默认绑定: 默认绑定就是裸函数调用,没有任何第三方对象或者方法介入。直接用函数名+() 的形式运行函数,就是裸函数调用,也就是八股文中的 this的默认绑定
到此,我们就学会了this的第一个绑定方式。它是一种绑定方式,是由裸函数调用的方式决定了它的绑定方式。
2- 函数作为构造函数调用
那函数除了裸函数调用之外,还有哪些调用方式呢。
还记得函数篇中我们提到的构造函数吗。 除了箭头函数,任何普通函数都可以使用new的方式进行调用,此时的函数为构造函数。 当使用 new 调用一个函数时,就变成构造函数调用:
function Person(name) {this.name = name;
}const p = new Person('Alice');
console.log(p.name); // Alice
- this 指向
- 构造函数调用时,this 永远指向新创建的对象
- 和普通函数或方法调用不同:
-
- 普通函数(裸函数调用)→ this 根据严格模式决定(undefined 或 window/global)
-
- 方法调用 → this 指向点左边对象
-
- 构造函数 → this 永远是新对象(或被返回对象覆盖)
为什么呢, 我们看一下new调用函数时发生了什么
- new 会做几件事情:
- 创建一个空对象(obj)
- 将 this 指向这个新对象
- 执行构造函数的代码
- 默认返回 this(新对象),除非显式返回对象
所以此时的this指向是固定的。
3- 函数作为对象的方法调用
当函数作为 对象的属性 被调用时,我们称它为 方法调用。示例如下:
const obj = {x: 42,foo: function() {console.log(this);}
};obj.foo(); // 方法调用
this 指向: 调用时,点前面的对象
作为对象方法调用时的典型例子
1) 对象直接调用
const obj = {name: 'Alice',greet: function() {console.log(this.name);}
};obj.greet(); // Alice// 这里 this 指向 obj
2) 多层对象
const obj = {inner: {name: 'Bob',say: function() { console.log(this.name); }}
};obj.inner.say(); // Bob// 点运算符左边是 obj.inner → this = obj.inner
3) 注意“丢失”情况
const obj = {x: 10,foo: function() { console.log(this.x); }
};const bar = obj.foo;
bar(); // undefined 或 window.x(非严格模式下)
原因:bar 是函数的引用,不再是对象的方法调用. 这种情况就变成了 裸函数调用
总结:
在对象调用方法的情况下一定要注意对象丢失的情况。 使用函数赋值的时候,裸函数调用和对象的方法调用不能混为一谈
好, 那对象.方法的形式,是什么绑定呢。 答案是: this的隐示绑定。
当我们用对象.函数的方式调用函数,此时函数中的this就已经与该对象进行了绑定。 此时就是隐示绑定。
这就是我们背诵的this的第二个绑定方式
4- 函数显示调用
不知道大家有没有见过call和apply调用函数。比较经典的:
function sum(a, b, c) { return a + b + c; }
const nums = [1, 2, 3];
console.log(sum.apply(null, nums)); // 6
// 用于计算
const arr = [3, 1, 4];
const max = Math.max.apply(null, arr); // 4
// 查找最大值
其实,函数作为所谓的一等公民,他是给我们提供了很多的方便,有些已经写好的库,我们可以直接拿过来使用,我们对指定的对象用现有的函数,这种调用方式并不少见。
此时,我们对指定的函数进行call和apply与对象进行绑定。这种方式从this的角度看,他就是this的显示绑定。我们直接明确的告诉大家,该函数是由该对象调用的。this也就是显示的绑定在这个对象上。
总结:
ok,到这里大家已经学习了this的四种绑定方式了。 如此的水到渠成,完全不用背诵各种复杂的逻辑。函数怎么调用,this就是对应的绑定。 他的值也就是对应的值。
那我们最后再看一个特殊的绑定方式。
5- 箭头函数
1) 箭头函数的基本调用(裸函数调用)
const add = (a, b) => a + b;console.log(add(2, 3)); // 5
this 指向定义时的外层作用域(词法绑定),而不是调用者
2)作为对象方法调用
const obj = {name: 'Alice',arrow: () => console.log(this.name)
};obj.arrow(); // undefined(this不是obj,而是定义时的外层 this)
箭头函数不会绑定对象
无论怎么调用,this 都不会指向调用对象
如果想访问对象属性,需要用普通函数或外部变量
3) 作为回调函数调用
setTimeout(() => {console.log('Hello');
}, 1000);
常用于回调函数中
不会创建新的 this,继承外层作用域的 this
避免了普通函数在回调中 this 指向全局或 undefined 的问题
4)与 call / apply / bind 的区别
const arrow = () => console.log(this);
arrow.call({ a: 1 }); // 依然指向定义时的 this
箭头函数 不能被 call / apply 改变 this
- 总结:
- 调用方式:基本和普通函数一样,可直接调用、作为回调或对象属性
- 特殊点:
-
- 没有自己的 this,继承外层作用域
-
- 不能被显式绑定(call/apply/bind 改变 this 无效)
-
- 适合用于回调或内部函数,避免 this 指向混乱
口语化OS:
我们其实完全可以这么理解。当上下文在被调用的函数中时: 箭头函数本身就无法改变this,所以,此时的this就是被调用函数当前状态的this
这句话怎么理解呢。 就是,除了箭头函数之外的其他调用方式大家都理解了。 此时情况:
上下文正在debugger处。 在函数内部。 此时的this绑定的哪个就是哪个,这时候,函数内部有再多的箭头函数,都不会改变this的指向。我们就记住一句话,箭头函数无法改变this的绑定
以上就是this的五种绑定的全部内容。 今天就先到这里。如果本篇文章对大家有帮助,还望点赞关注,后续再给大家加一点this优先级的相关内容。