this

函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。

解析器在调用函数时每次都会向函数内部递进一个隐含参数

当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。

这个对象我们称为函数执行的上下文对象

全局上下文

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // truea = 37;
console.log(window.a); // 37this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"

函数上下文

在函数内部,this的值取决于函数被调用的方式。

因为下面的代码不在严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向全局对象,浏览器中就是 window

严格模式下则为undefined

function f1(){return this;
}
//在浏览器中:
f1() === window;   //在浏览器中,全局对象是window//在Node中:
f1() === globalThis;// 然而,在严格模式下,如果进入执行环境时没有设置 this 的值,this 会保持为 undefined,如下:function f2(){"use strict"; // 这里是严格模式return this;
}f2() === undefined; // true

改变函数的调用对象

function  fun(){console.log(this);};var obj  = {  name:"xxx" ,  sayName:fun };obj.sayName(); //  {  name:"xxx" ,  sayName:fun }
//直接用fun ()显示的是object  window

类上下文

this 在 类 中的表现与在函数中类似,因为类本质上也是函数,但也有一些区别和注意事项。

在类的构造函数中,this 是一个常规对象。类中所有非静态的方法都会被添加到 this 的原型中:

class Example {constructor() {console.log(this); //  Example{}const proto = Object.getPrototypeOf(this);console.log(Object.getOwnPropertyNames(proto));} first(){} second(){}  //  constructor 、 first 、second 这些方法都会添加到this 的原型上,可以通过 类实例访问到static third(){} //  不会添加到this 原型上,添加到类本身的属性上,通过 Example.third访问
}new Example(); // ['constructor', 'first', 'second']
console.log(Example.first); //  undefined
console.log(Example.third); //  func()

备注:静态方法不是 this 的属性,它们只是类自身的属性。

派生类

不像基类的构造函数,派生类的构造函数没有初始的 this 绑定。在构造函数中调用 super() 会生成一个 this 绑定,并相当于执行如下代码,Base为基类:

警告:在调用 super() 之前引用 this 会抛出错误。

派生类不能在调用 super() 之前返回,除非其构造函数返回的是一个对象,或者根本没有构造函数

class Example {constructor() {}first(){}static third(){}
}
class A extends Example{constructor(){console.log(this); // 报错thissuper()  // 不调用super()则this会报错console.log(this); // 输出 A {}}
}
new A()

this 使用示例

call() apply() 使用

区别

apply(第二个参数为数组)

function add(c, d) {return this.a + this.b + c + d;// this call、apply调用时改this为 o 对象故能取到 o 对象上的属性值
}var o = {a: 1, b: 3};call()
// 第一个参数是用作“this”的对象
// 其余参数用作函数的参数
add.call(o, 5, 7); // 16
apply()
// 第一个参数是用作“this”的对象
// 第二个参数是一个数组,数组中的两个成员用作函数参数
add.apply(o, [10, 20]); // 34

在非严格模式下使用 callapply 时,如果用作 this 的值不是对象,则会被尝试转换为对象。
nullundefined 被转换为全局对象。
原始值如 7'foo' 会使用相应构造函数转换为对象。
因此 7 会被转换为 new Number(7) 生成的对象,字符串 'foo' 会转换为 new String('foo') 生成的对象。

function bar() {console.log(Object.prototype.toString.call(this));
}bar.call(7);     // [object Number]
bar.call('foo'); // [object String]
bar.call(undefined); // [object window]function bar() {console.log(this);
}
bar.call(7);     // Number{7}
bar.call('foo'); // String{'foo'}
bar.call(undefined); // 全局window{}

bind 方法

ECMAScript 5 引入了 Function.prototype.bind()。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

function f(){return this.a;
}var g = f.bind({a:"azerty"});
console.log(g()); // azertyvar h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azertyvar o = {a:37, f:f, g:g, h:h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty

箭头函数

在箭头函数中,this 永远指向它所处环境的最近的this 。在全局代码中,它将被设置为全局对象:

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

备注:如果将this传递给callbind、或者apply来调用箭头函数,它将被忽略。不过你仍然可以为调用添加参数,不过第一个参数(thisArg)应该设置为null

// 接着上面的代码
// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true// 尝试使用call来设定this
console.log(foo.call(obj) === globalObject); // true// 尝试使用bind来设定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

无论如何,foothis 被设置为他被创建时的环境(在上面的例子中,就是全局对象)

var obj = {bar: function() {var x = (() => this);return x; // 返回一个this// 它的this被永久绑定到了它外层函数的this。
// bar的值可以在调用中设置,这反过来又设置了返回函数的值。}
};// 作为obj对象的一个方法来调用bar,把它的this绑定到obj。
// 将返回的函数的引用赋值给fn。
var fn = obj.bar();// 直接调用fn而不设置this
console.log(fn() === obj); // true// 但是注意,如果你只是引用obj的方法,
// 而没有调用它
var fn2 = obj.bar;
// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
console.log(fn2()() == window); // true

作为对象的方法

var o = {prop: 37};function independent() {return this.prop;
}o.f = independent;console.log(o.f()); // 37

this 的绑定只受最接近的成员引用的影响。

o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42

原型链中的 this

var o = {f: function() {return this.a + this.b;}
};
var p = Object.create(o);
p.a = 1;
p.b = 4
console.log(p);// {a:1,b:4} __proto__  {f:func}

对象 p 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。
虽然最终是在 o 中找到 f 属性的,这并没有关系;查找过程首先从 p.f 的引用开始,所以函数中的 this 指向p
也就是说,因为f是作为p的方法调用的,所以它的this指向了p


作为构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

备注:虽然构造函数返回的默认值是 this 所指的那个对象,但它仍可以手动返回其他的对象(如果返回值不是一个对象,则返回 this 对象)。

/** 构造函数这样工作:** function MyConstructor(){*   // 如果函数具有返回对象的return语句,*   // 则该对象将是 new 表达式的结果。*   // 否则,表达式的结果是当前绑定到 this 的对象。*   //(即通常看到的常见情况)。* }*/function C(){this.a = 37;
}var o = new C();
console.log(o.a); // logs 37function C2(){this.a = 37;return {a:38}; //  该对象将是 new 表达式的结果。会将this绑定为该对象
}o = new C2();
console.log(o.a); // logs 38

在刚刚的例子中(C2),因为在调用构造函数的过程中,手动的设置了返回对象,与this绑定的默认对象被丢弃了。(这基本上使得语句 “this.a = 37;”成了“僵尸”代码,实际上并不是真正的“僵尸”,这条语句执行了,但是对于外部没有任何影响,因此完全可以忽略它)


作为 DOM 事件处理函数

当函数被用作事件处理函数时,它的 this 指向触发事件的元素(一些浏览器在使用非 addEventListener 的函数动态地添加监听函数时不遵守这个约定)。

// 被调用时,将关联的元素变成蓝色
function bluify(e){console.log(this === e.currentTarget); // 总是 true// 当 currentTarget 和 target 是同一个对象时为 trueconsole.log(this === e.target);this.style.backgroundColor = '#A5D9F3';
}// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){elements[i].addEventListener('click', bluify, false);
}

作为一个内联事件处理函数

当代码被内联 on-event 处理函数 (en-US) 调用时,它的this指向监听器所在的DOM元素:

<button onclick="alert(this.tagName.toLowerCase());">Show this
</button>
// 上面的 alert 会显示 button。注意只有外层代码中的 this 是这样设置的:
<button onclick="alert((function(){return this})());">Show inner this
</button>
// 在这种情况下,没有设置内部函数的 this,所以它指向 global/window 对象(即非严格模式下调用的函数未设置 this 时指向的默认对象)。

getter 与 setter 中的 this

再次,相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 gettersetter 的函数都会把 this 绑定到设置或获取属性的对象。

function sum() {return this.a + this.b + this.c;
}var o = {a: 1,b: 2,c: 3,// 在对象o中定义一个getter方法 的简写get average() {return (this.a + this.b + this.c) / 3;}
};
//  在o对象中定义一个sum键,值为一个getter sum函数
Object.defineProperty(o, 'sum', {get: sum, enumerable: true, configurable: true});
//  o.average 等于调用 o 的get average 方法返回一个 (1+2+3)/3 的值
// o.sum 等于调用 o 对象里的get sum 方法返回 1+2+3
console.log(o.average, o.sum); //  2, 6

类中 this

和其他普通函数一样,方法中的 this 值取决于它们如何被调用。
有时,改写这个行为,让类中的 this 值总是指向这个类实例会很有用。
为了做到这一点,可在构造函数中绑定类方法:

class Car {constructor() {// 绑定了 sayBye函数 而不绑定 sayHi 函数来展示不同的区别 this.sayBye = this.sayBye.bind(this);}sayHi() {console.log(`Hello from ${this.name}`);}sayBye() {console.log(`Bye from ${this.name}`);}get name() {return 'Ferrari';}
}class Bird {get name() {return 'Tweety';}
}const car = new Car();
const bird = new Bird();// 函数里的this 取决于调用它的人
car.sayHi(); // Hello from Ferrari
//  往bier 实例对象上添加一个Car 身上的 sayHi 方法使用
bird.sayHi = car.sayHi;
bird.sayHi(); // Hello from Tweety// 但是 对于绑定了 this 的函数,它的this 不取决于调用它的人
bird.sayBye = car.sayBye;
bird.sayBye();  // Bye from Ferrari

备注:类内部总是严格模式。调用一个 this 值为 undefined 的方法会抛出错误。

javascript(JS) 0基础快速入门 (二)(this指向问题)相关推荐

  1. 0基础快速入门CSS技术栈(4)—图解详细阐述CSS的复合选择器、标签显示模式、行高、CSS背景,及最为重要的CSS三大特性附带权重计算笔试题(附详细案例源码解析过程)

    文章目录 1. 0基础快速入门CSS技术栈(4) 2. 重点提炼 3. CSS复合选择器 3.1 后代选择器(重点) 3.1.1 example01 3.2 子元素选择器 3.2.1 exmaple0 ...

  2. javascript(JS) 0基础快速入门 (七)(事件的冒泡、委托和绑定)

    事件的冒泡 所谓冒泡指的就是事件的向上传导 当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发 类似于 给一个盒子绑定了鼠标点击事件,当点击了盒子后,该鼠标点击事件会一直往上触发, 如果这盒子 ...

  3. Vue 基础快速入门(二)

    Vue 组件化编程 模块, 组件, 模块化与组件化 模块 理解:向外提供特定功能的 js 程序, 一般就是一个 js 文件 为什么:js 文件很多很复杂 作用:复用 js,简化 js 的编写, 提高 ...

  4. 0基础快速入门WebPack(3)——图解详述plugins(插件)的安装及sourceMap的使用及WebpackDevServer正向代理和模块热更新等(附详细案例源码解析过程及版本迭代过程)

    文章目录 1. 重点提炼 2. 配置环境 3. Plugins(插件) 3.1 HtmlWebpackPlugin 3.1.1 example01 3.1.1.1 example01-1 3.1.1. ...

  5. 0基础快速入门CSS技术栈(6)—图解详细阐述说透CSS的浮动及应用、浮动的扩展及清除浮动和详解快速·1photoshop切图(附详细案例源码解析过程)2021-01-07更新

    文章目录 1. 浮动(float)重点提炼 2. CSS 布局的三种机制 3. 为什么需要浮动? 3.1 example01 4. 什么是浮动(float) 4.1 作用 4.1.1 example0 ...

  6. 0基础快速入门CSS技术栈(3)—图解详细阐述CSS文字文本样式及综合案例、样式调试工具、快速开发html的emment语法(附详细案例源码解析过程)

    文章目录 1. CSS字体样式属性调试工具 2. font字体 2.1 font-size:大小 2.2 font-family:字体 2.2.1 CSS Unicode字体 2.3 font-wei ...

  7. 【0基础快速入门】Python学习快速参考手册

    Python学习快速参考手册 目录 文章目录 Python学习快速参考手册 目录 @[toc] 下载 Python下载与配置 IDE下载与配置 第一章 · Python的基本语法 变量 数据类型 注释 ...

  8. 0基础快速入门CSS技术栈(5)—图解详细阐述说透CSS的盒子模型(超级重要)、圆角边框、盒子阴影及相关重要的笔试题——css的核心中的核心(附详细案例源码解析过程)2021.01.07更新

    文章目录 1. 盒子模型(CSS重点) 1.1 看透网页布局的本质 1.2 盒子模型(Box Model) 1.3 盒子边框(border) 1.3.1 边框综合设置 1.3.2 example01 ...

  9. 微信小程序0基础快速入门(史上最全!!!)

    产品定位及功能介绍 微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序注册 注册小程序帐号 在微信公众平台官网首页(mp.weixin.qq ...

最新文章

  1. hp compaq presarop v3009笔记本重新启动蓝屏!
  2. Apache服务器和tomcat服务器有什么区别?
  3. emwin修改text字体颜色_Rggplot2 绘制带颜色条的相关性散点图
  4. css之为文本添加线性渐变和外描边
  5. 服务器经常崩溃??让我们来看看简单的内存知识:C语言——内存管理
  6. 不要错过!MICCAI 2019 所有论文完整下载
  7. 叮叮叮 重点之中的python必备英语单词(2)来啦!请记得查收
  8. 【hadoop】ipc.Client: Retrying connect to server: xxx:8020. Already tried 37 time(s) RetryPolicy[Multi
  9. 潜移默化学会WPF(样式)-- DataGrid(转载)
  10. 用FileInputStream和FileOutputStream实现文件复制粘贴
  11. netdev_priv() 函数
  12. 初赛复习的一些零碎链接
  13. 初探socket 报式
  14. C#写的制程能力CPK分析程序
  15. RBF神经网络算法分析与应用(适合快速入门实战)
  16. 手机怎么打开psd文件(实用方法)
  17. 非结构化数据分析技术是忽悠
  18. 关于日期插件在chrome中出现被遮挡的问题
  19. 广义最小二乘法的基本思想是什么_解决异方差问题的方法可行广义最小二乘法fgls法.ppt...
  20. Linux笔记(更新中)

热门文章

  1. 点击事件返回上一页面
  2. axios的post请求
  3. java矩形_JAVA实现矩形(长方形)的周长面积计算
  4. 输入小写字母转换acii和对应大写
  5. ∑-Δ 型ADC原理
  6. (转)svn中更新工程出现如下错误:Working copy not locked; this is probably a bug, please report...
  7. python读取txt文本出现中文乱码已解决。
  8. 阿里云域名购买流程和备案流程
  9. Python判断字符串是否为字母或者数字或者数字字母组合
  10. 零售商商品管理系统——需求分析