JavaScript this指向
- 全局环境下:
this 始终指向全局对象(window), 无论是否严格模式
console.log(this.document === document); // true// 在浏览器中,全局对象为 window 对象: console.log(this === window); // truethis.a = 37; console.log(window.a); // 37
- 普通函数调用:
非严格模式下:指向全局对象 :window; 严格模式:this是undefined
function f1(){ return this; // 非严格模式:普通函数的this指向window } f1() === window; // true
function f1(){ "use strict"; // 这里是严格模式 return this; // 严格模式:普通函数的this是undefined } f1() === window; // false
// let 声明的变量 let username = 'username' function fn(){ alert(this.username); // undefinedlet声明的变量不在window上 } fn();
// var 声明的变量 var username = 'username' function fn(){ alert(this.username); // username } fu();
// 直接挂载在 window 上的变量 window.username = 'username' function fn(){ alert(this.username); // username } fn(); //可以理解为 window.fn();
- 对象函数调用:
哪个对象函数调用,this就指向哪里
window.b=2222 let obj={ a:111, fn:function(){ alert(this.a); //111 alert(this.b); //undefined } } obj.fn(); // 此时的 this 指向对象 obj; this.a 相当于 obj.a, this.b 相当于 obj.b ; // this.a = 'username', obj没有 b 属性,所以 this.b = undifined
- 构造函数调用:
this指向实例对象
// 例1: function Person(age, name) { this.age = age; this.name = name console.log(this)// 此处 this 分别指向 Person 的实例对象 p1 p2 console.log(this.age) console.log(this.name) } var p1 = new Person(18, 'zs')// 输出:18, zs var p2 = new Person(18, 'ww')// 输出:18, ww// 例2: let TestClass = function() { this.name = '111'; } let subClass = new TestClass(); subClass.name = 'cn'; console.log( subClass.name ); // cn let subClass_1 = new TestClass(); console.log( subClass_1.name ) // 111
new() 方法做了什么
(1) 创建一个新对象
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
new 操作符
在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在JavaScript中,我们将这类方式成为Pseudoclassical。
基于上面的例子,我们执行如下代码
var obj = new Base();
我们在Javascript引擎中看到的对象模型:
文章图片
new操作符具体干了什么呢? 其实很简单,就干了三件事情。
var obj= {}; // 创建一个新对象 obj.__proto__ = Base.prototype; // 新对象的 __proto__(隐式原型)指向Base函数对象的prototype(显式原型)原型对象 Base.call(obj); // 将Base的this指针利用 call()方法替换成obj,然后调用Base函数,给obj对象赋值了一个id成员变量,这个成员变量的值是”base”
如果我们给Base.prototype的对象添加一些函数会有什么效果呢?
例如代码如下:
Base.prototype.toString = function() { return this.id; }
那么当我们使用new创建一个新对象的时候,根据__proto__的特性,toString这个方法也可以做新对象的方法被访问到。于是我们看到了:
构造函数中,我们来设置‘类’的成员变量(例如:例子中的id),构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符,模拟出类和类实例化的效果。
- 定时器函数调用:
this 指向 window
setInterval(function () { console.log(this); // window }, 1000);
- 事件绑定调用:
this 指向 绑定事件的对象
> var oBtn = document.getElementById("btn"); oBtn.onclick = function() { console.log(this); // btn }
- apply和call调用【修改this指向】
var Person = { name:"lixue", age:21 } function fn(x,y){ console.log(x+","+y); // hh20 console.log(this); // this 指向 Person console.log(this.name); // lixue console.log(this.age); // 21 } fn.call(Person,"hh",20); fn.apply(Person,["hh",20]);
- 箭头函数调用:
自身没有this,箭头函数的this是父级所在的执行上下文中的this
注意:简单对象(非函数)是没有执行上下文的!所以在定义对象的时候,定义对象属性,里面的this指向的一般是全局,或者这个对象所在的那个环境中的this。
实际原因: 箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
// 例1: function Person() { this.age = 0; setInterval(() => { this.age++; // this的指向是 p 实例对象,执行上下文是Person() }, 3000); }var p = new Person(); // 输出 0 1 2 ..... 间隔三秒// 例2: var b = 'BBBB'; var obj = { b : 'bbbb' say:()=>{ console.log(this.b)// this 的指向是上级对象obj所在的执行上下文:window,所以输出的是:BBBB } } obj.say()// 输出 BBBB
- call、bind、apply区别
- call:第一个参数是要绑定给this的值,后面传入的是一个参数列表 list 。当第一个参数为null、undefined的时候,默认指向window。
特点:从第二个参数开始是一个参数列表 list
var arr = [1, 2, 3, 89, 46] var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4]) // 输出 89;因为call方法的第一个参数为null,则 this 指向 window,从window中找 arr 变量
- apply:第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
特点:从第二个参数是一个参数数组
var arr = [1, 2, 3, 89, 46] var max = Math.max.call(null, arr) // 输出 89;因为call方法的第一个参数为null,则 this 指向 window,从window中找 arr 变量
示例:
var obj = { message: 'My name is: ' }function getName(firstName, lastName) { // 原本的this指向window,但是call/apply改变后,此时的this指向 obj,所以 this.message 相当于 obj.message = My name is: console.log(this.message + firstName + ' ' + lastName) }getName.apply(obj, ['Dot', 'Dolby']) // My name is: Dot Dolby getName.apply(obj, 'Dot', 'Dolby') // My name is: Dot Dolby
var Person1= function () { this.name = 'Dot'; } var Person2 = function () { this.getname = function () { console.log(this.name); } Person1.call(this); // 改变this指向,使得Person2的this指向 Person1;相当于Person2 拥有了Person1 的所有属性,所以this.name = 'Dot' } var person = new Person2(); person.getname(); // Dot
- bind:第一个参数是this的指向,第二个参数开始是接收的参数列表。
特点:bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数以及bind接收的参数列表的使用。。
返会的函数
var obj = { name: 'Dot' }function printName() { console.log(this.name)// 函数本身的this指向没有被改变,依然指向 window } var dot = printName.bind(obj)// this指向 obj console.log(dot)// function () { … } dot()// Dot printName(); // undefiined
【前端面试题|前端面试题之JavaScript【this指向】】参数的使用
function fn(a, b, c) { console.log(a, b, c); } var fn1 = fn.bind(null, 'Dot'); //this 指向 windowfn('A', 'B', 'C'); // A B C fn1('A', 'B', 'C'); // Dot A B fn1('B', 'C'); // Dot B C fn.call(null, 'Dot'); // Dot undefined undefined
- call:第一个参数是要绑定给this的值,后面传入的是一个参数列表 list 。当第一个参数为null、undefined的时候,默认指向window。
推荐阅读
- java|《JavaScript经典面试题》第3道(关于this指向的常见面试题)
- web前端|面试必问JavaScript基础面试题(附答案详解)
- 经典面试|Javascript中经典面试题 原型链 闭包 this指向问题 类的继承
- javaScript|2021年Javascript最常见的面试题以及答案
- 面试题|牛客网前端刷题(一)
- JavaScript高级语法|ES6——class类实现继承
- 前端面试题|JavaScript前端经典面试题之ES6面试题汇总es6
- vue|Vue+Electron开发跨平台桌面应用(实战)
- JS中的while和for循环用法和区别