首先「this」指当前执行代码的环境对象
一、在全局执行环境中,this 指向全局对象;在浏览器中全局对象是「window」,在「node」环境中全局对象是「global」:
var first_name = 'this_ex';
console.log(this.first_name);
console.log(first_name);
console.log(this === window);
console.log(window.first_name);
this.last_name = 'this_last';
console.log(last_name);
console.log(window.last_name);
// 输出
"this_ex"
"this_ex"
true
"this_ex"
"this_last"
"this_last"
但是在浏览器严格模式下(即浏览器按照 w3c 标准解析),this 将保持它进入指向环境时的值,所以下面的 this 将会默认为 undefined
function strict() {
'use strict'
return this;
}
console.log(strict() === undefined)
// 输出
true
在普通函数中,我们定义的属性如果与全局属性重名,那么在函数中更改属性值会覆盖全局属性:
var name = 'huang'
function Person() {
this.name = 'yao'
console.log(this.name)
console.log(name)
}
Person()
console.log(name)
// 输出
"yao"
"yao"
"yao"
所以我们为了避免这种情况,可以将「Person」作为构造函数,我们 new 一个「Person」的实例,这样就能避免破坏全局属性
var name = 'huang'
function Person() {
this.name = 'yao'
console.log(this.name)
console.log(name)
}
var p = new Person()
console.log(name)
// 输出
"yao"
"huang"
"huang"
OK,说到这里我们就要提一下这个「new」了,new 具体做了如下操作
- 创建一个空的对象(即{})
- 链接该对象(即设置该对象的构造函数)到另一个对象
- 将步骤1新建的对象作为 this 的上下文
- 如果该函数没有返回对象,则返回 this
我们先来看下列代码,再来分析步骤
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
console.log(car1.__proto__.constructor === Car)
// 输出
"Eagle"
true
「new」运算符先新建一个 car1 = {} 的对象,然后设置 car1 的构造函数等于「Car」,即设置 car1.__proto__.constructor = Car
然后将 car1 作为 Car 中 this 的上下文,然后执行 Car 函数,并且返回 this,所以最后我们才能通过 car1.make 输出内容
如果我们要把 this 值从一个环境传到另一个,那就要用到 call 或者 apply 方法
var obj = {
first_name: 'huang'
}
first_name = 'first_name'
function Person(data) {
console.log(this.first_name)
console.log(data)
}
Person();
Person.call(obj,'is call');
Person.apply(obj, ['is apply']);
// 输出
"first_name"
undefined
"huang"
"is call"
"huang"
"is apply"
call 和 apply 做的事情就是将 obj 的 this 传到 Person 中,并执行 Person 函数。
call 和 apply 第一个参数默认传对象,如果传的不是对象,JavaScript 会尝试使用内部 ToObject 操作将其转化为对象,例如将数字「7」转化为对象,通过 Object.prototype.toString.call(7) 返回 [Object,Number],所以这也是判断对象是「函数」「数组」还是「null」的一种方法。
call 和 apply 后面的参数就是作为 Person 函数的入参,apply 后面必须是数组格式。
二、箭头函数即函数的另一个表现语法,在箭头函数出现之前,每个新定义的函数都有它自己的 this 值(在构造函数的情况下是一个新对象,在严格模式的函数调用中为 undefined,如果该函数被作为“对象方法”调用则为基础对象等),例如
function Person() {
this.num = 0;
setTimeout(function grow(){
console.log(this.num + 1)
},1000)
}
Person() // 1
var p = new Person() // NaN
将 this 分配给封闭的变量可以解决这个问题
function Person() {
this.age = 0;
let _that = this;
setTimeout(() => {
_that.age++
console.log(this.age)
},1000)
}
var p = new Person()
// 输出
1
也可以用箭头函数替代,箭头函数会继承 Person 的 this,也就可以拿到 age 的值了
function Person() {
this.age = 0
setTimeout(() => {
this.age++
console.log(this.age)
},1000)
}
var p = new Person()
// 输出
1
在严格模式下也并不会影响箭头函数取得上一级作用链的 this
function Person() {
this.age = 0;
var closure = "123"
setTimeout(function growUp() {
this.age++;
console.log(this.age)
console.log(closure)
}, 1000);
}
var p = new Person();
function PersonX() {
'use strict'
this.age = 0;
var closure = "123"
setTimeout(()=>{
this.age++;
console.log(this.age)
console.log(closure)
}, 1000);
}
var px = new PersonX();
// 输出
NaN
"123"
1
"123"
由于箭头函数没有自己的 this ,所以调用 apply 或者 call 方法时,只能传递参数,第一个参数会被忽略,如下:
var adder = {
base: 1,
add: function(a){
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a){
var f = v => v + this.base;
var b = {
base: 2
}
return f.call(b,a)
}
}
console.log(adder.add(1));
console.log(adder.addThruCall(1))
// 输出
2
2
在 f.call(b,a) 中,b 会被忽略,所以 addThruCall 中的 this.base 取的还是 adder 中的 this.base
箭头函数中不绑定 arguments 对象,arguments 对象即调用函数时传递给函数的参数数组,它是一个类数组对象,除了 length 和 index 属性外,没有别的属性,所以想要调用数组的 pop 或者别的方法,要先将 arguments 转化为数组
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
// ES2015
const args = Array.from(arguments);
const args = [...arguments];
然而箭头函数并不会绑定 arguments 对象
var arguments = [2,3,4];
var args = () => arguments[0];
console.log(args());
function foo(n) {
var f = () => arguments[0] + n;
return f()
}
console.log(foo(1))
// 输出
2
2
因为箭头函数不会绑定 arguments,所以 args 中取的是全局属性 arguments 的值,所以输出 2,在 foo 函数中 f 函数中的 arguments 取的是 foo 函数的 arguments 对象,所以 arguments[0] 即是 n,所以输出 2
综上,箭头函数表达式对非方法函数是最好的,让我们看看把它们作为方法时会怎样
'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b();
// undefined
obj.c();
// 10, Object {...}
箭头函数中不会产生 this,所以 b 中的this 指向 window 对象,所以 this.i 为 undefined,再看一个 Object.defineProperty 例子
'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this)
return this.a + 10
}
})
obj.b
// 输出
undefined
"undefined"
[Object,Window]
箭头函数不能用作构造器,和 new 一起用会抛出错误
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
箭头函数没有 prototype 属性
var Foo = () => {};
console.log(Foo.prototype); // undefined

本文深入解析JavaScript中的this关键字,探讨其在不同环境下的行为,包括全局执行环境、普通函数、构造函数、箭头函数以及在严格模式下的表现。同时,文章讲解了如何使用call和apply方法改变函数执行时的this值,以及箭头函数与传统函数在this绑定上的区别。
1622

被折叠的 条评论
为什么被折叠?



