一, this, 其实可以类比成人
说到 this 的话, 我们在 JS 中主要研究的都是函数中的 this, 在 JavaScript 中, this 代表当前行为的执行主体, 而 context 代表的是当前行为执行的的环境 (区域).
例如男神在北理珠吃饭, 这句话分别代表的含义如下
男神 -->> 主体 (this)
吃饭 -->> 函数 (function)
北理珠 -->> 环境 (context)
-->> 说明: 吃饭是函数, 男神是函数的主体, 北理珠就是当前行为的执行环境 (context), 主体跟上下文没有必然的联系, 主体只与函数有关系; 就好像男神吃饭, 其实哪里都可以吃饭, 吃饭这个动作的主体永远都是男神, 环境是可以变化的.
-->> 结论: 上面, 其实也可以说明, this 指的是谁, 和函数在哪里定义和执行是没有任何关系的.
二, 在函数中, 如何区分 this
1, 函数执行, 首先看函数名前面是否有 "." , 有的话, "." 前面是谁, this 就是谁; 没有的话 this 就是 Windows
- // 例子一
- console.log(this);
- function eat() {
- console.log(this); //this->Windows
- }
- ~function() {
- eat(); //this->Windows
- }();
- // 例子二
- function fn() {
- console.log(this);
- }
- var obj = {fn: fn};
- fn(); //this->Windows
- obj.fn() //this->obj
- // 例子三
- function sum() {
- fn();
- }
- sum(); //this->Windows
- // 例子四
- var oo = {
- sum: function() {
- console.log(this);
- fn();
- }
- }
- oo.sum(); // 首先 sum 函数里面, 第一个输出 this 为 oo; 之后执行 fn(),this 为 Windows
2, 立即执行函数中的 this 永远都是 Windows
- (function() {
- console.log(this); // this->Windows
- })()
3, 给元素的某一事件绑定方法, 当事件触发的时候, 执行对应的方法, 方法中的 this 是当前元素
- function fn() {
- console.log(this);
- }
- // 例子一
- document.getElementById("div1").onclick = fn; //this -> dom 元素
- // 例子二
- document.getElementById("div1").onclick = function() {
- console.log(this); // this->#div
- fn(); // this->Windows
- }
结论: 找到函数在哪里执行的, 有点, this 就是点前面东西; 没点, this 就是 Windows
三, 使用 this 分析一道面试题
- var num = 20;
- var obj = {
- num: 30,
- fn: (function(num) {
- this.num *= 3;
- num += 15;
- var num = 45;
- return function() {
- this.num *= 4;
- num + =20;
- console.log(num);
- }
- })
- };
- var fn = obj.fn;
- fn(); // ->65
- obj.fn(); // ->85
- console.log(Windows.num, obj.num) // ->240, 120
上面代码使用堆栈图来描述, 如下, 首先正方形代表栈内存 (函数执行环境, context), 圆边方形代表堆内存 (用来存放字符串).
第一步, 形成一个 JS 执行环境, Windows 作用域, 首先预解释 (声明 var, 声明 + 定义 function);
第二步, 代码由上往下执行, num = 20, obj = 引用数据类型 (数据存在堆内存里面)
第三步, 因为 obj.fn 是立即执行函数, 所以形成一个私有作用域 A, 执行 fn 里面的函数, 因为 fn 里面的函数返回值为 function,return 值被外面的 obj.fn 引用了, 这个立即执行函数 A 作用域不销毁.
第四步, Windows 作用域下面的代码继续往下执行.
四, this 实践运用
现在有这样一个需求, 要求做一个累加器, 每点击一次, 就累加 1.
- var oBtn = document.getElementById("btn");
- var spanNum = document.getElementById("spanNum");
- // 方法一
- // 利用全局作用域不销毁的原理, 把需要累加的数字定义为全局变量
- var count = 0;
- oBtn.onclikc = function() {
- count++;
- spanNuM.innerText = count;
- }
- // 弊端: 在项目中为了防止全局变量之间的冲突, 我们一般是禁止或者减少使用全局变量的
- // 方法二
- // 形成一个不销毁的私有作用域保存我们需要累积的数据
- // 1)
- (function() {
- var count = 0;
- oBtn.onclick = function() {
- ++count;
- spanNum.innerText = count;
- }
- })
- // 2)
- oBtn.onclick = (function() {
- var count = 0;
- return function() {
- ++count;
- spanNum.innerText = count;
- }
- })
- // 上面的两种写法都是表达同一个意思
- // 弊端: 有一个不销毁的私有作用域, 占那么一点点内存
- // 方法三
- // 利用 innerhtml 的方式处理, 每点击一次都需要到页面获取最新的值, 然后累加, 最后把结果放进去
- oBtn.onclick = function() {
- ++spanNum.innerHTML;
- }
- // 弊端: innerHTML 获取的时候本来就需要浏览器去处理, 每一次都需要把页面的内存先转化为字符串, 然后累加, 累加完重新添加回去, 当重新添加的时候浏览器需要重现渲染一遍页面.
- // 方法四
- // 利用自定义属性存储 (推荐!)
- oBtn.count = 0;
- oBtn.onclick = function() {
- spanNum.innerTxt = ++this.count;
- }
- // 注意, 这里的 count 只是对象的一个属性, 它既不是全局变量, 也不是局部变量哦.
- END
来源: https://www.cnblogs.com/pengshengguang/p/11105323.html