前言
在看面经的时候,发现自己对this的指向依然有些模糊。看了很多this相关文章,打算写一篇文章梳理this相关知识,并用一些常见的面试题加强理解,供复习巩固使用。
如果觉得文章有歧义,请大佬指出,避免误导更多的人!!
正文
this
JavaScript 语言之中,一切皆对象,运行环境也是对象。函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。
this的为什么会出现? 因为函数可以在不同的运行环境执行,this的出现就是为了在函数内部获得(函数)当前的运行环境。
但JavaScript 支持运行环境动态切换,也就是说this的指向是动态的,没有办法事先确定到底指向哪个对象。这就需要我们额外的注意函数的运行环境。
先来举个例子(来自阮一峰前辈的文章),虽然都在执行foo。但是执行环境并不一样。
- 对于
obj.foo()
来说,foo运行在obj环境,所以this指向obj; - 对于
foo()
来说,foo运行在全局环境,所以this指向全局环境。
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
如果想更深入的了解该问题,就必须了解内存的数据结构,以下图片内容来自阮一峰的this原理
var obj = { foo: function () {} };
对于上面的代码,变量obj是一个地址。如果要读取obj.foo,要先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。foo属性的值保存在属性描述对象的value属性里面。函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。
所以,函数是一个单独的值,所以它可以在不同的环境(上下文)执行,且JavaScript 允许在函数体内部,引用当前环境的其他变量。
使用环境
全局环境
在全局环境
使用this,它指的就是顶层对象window
。不管是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window。
console.log(this === window); // true
function f() {
console.log(this === window); //true
}
f() // true
如果使用了严格模式"use strict";
,则其为undefined
function f(){
"use strict";
console.log(this);
}
f() //undefined
构造函数
构造函数
中的this,指的是实例对象。
var Obj = function (p) {
this.p = p;
console.log(this);
};
var o = new Obj('Hello World!');
console.log(o.p); // "Hello World!"
对象
如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。 该方法赋值给另一个对象,就会改变this的指向。如下面这个例子
var a = 20;
var obj = {
a: 10,
getA: function () {
return this.a;
}
}
console.log(obj.getA()); //10
var test = obj.getA;
console.log(test()); //20 独立调用test
注意!!!下面这种用法,会改变this的指向。obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj。
var obj ={
foo: function () {
console.log(this);
}
};
(obj.foo = obj.foo)() // window
此外,如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。如下,a.b.m
方法在a对象的第二层,该方法内部的this不是指向a,而是指向a.b
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
显示绑定
可查看该文章:
前端面试高频题——手撕call()、apply()和bind()函数代码,了解函数this的默认绑定、隐式绑定、显示绑定
相关面试题
1
var m = 10;
function fn() {
return this.m + 1;
}
var obj = {
m: 5,
test1: function() {
return fn();
}
};
obj.test2 = fn;
console.log(obj.test1(), fn(), obj.test2()) // 11 11 6
如果把var 变成 let答案就是:NaN NaN 6
。前两个的this.m
为undefined,因为用let声明的m,属于script的作用域,而不是windows
身上的属性。
2
var obj = {
a: 10,
b: this.a + 10, //这里的this指向window(全局),a为undefined ==> undefined + 20 = NaN
fn: function () {
return this.a;
}
}
console.log(obj.b); //NaN
console.log(obj.fn()); //10
3
var a = 5;
function fn1(){
var a = 6;
console.log(a); //6
console.log(this.a); //5
}
function fn2(fn) {
var a = 7;
fn();
}
var obj = {
a: 8,
getA: fn1
}
fn2(obj.getA);
4
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this); //Person实例对象
}
Person(); //window
Person.prototype.getName = function () {
console.log(this); //Person实例对象
};
var p1 = new Person("test", 18);
p1.getName();
5
var obj = {
foo: "test",
fn: function(){
var mine = this;
console.log(this.foo); //test
console.log(mine.foo); //test
(function(){
console.log(this); // window
console.log(this.foo); //undefined
console.log(mine.foo); //test
})();
}
};
obj.fn();
6
function test(arg) {
this.x = arg;
return this;
}
var x = test(5); //此时 x = window, y = undefined
var y = test(6); //此时 x = 6, y = window
console.log(x.x); //undefined, 6.x是undefined
console.log(y.x); //6 实际上是window.x 也就是6
7
var obj = {
data: [1,2,3,4,5],
data2: [1,2,3,4,5],
fn: function () {
console.log("--test--");
console.log(this); //Object
return this.data.map(function (item) {
console.log(this); //window
return item * 2;
});
},
fn2: function () {
console.log("---test2---");
console.log(this); //Object
return this.data2.map(item=>{
console.log(this); //Object
return item * 2;
});
}
};
obj.fn();
obj.fn2();
如果觉得本篇文章有用,记得点赞、收藏!!