笔者最近在看 你不知道的JavaScript上卷,里面关于 this 的讲解个人觉得非常精彩。JavaScript 中的 this 算是一个核心的概念,有一些同学会对其有点模糊和小恐惧,究其原因,现在对 this 讨论的文章很多,让我们觉得 this 无规律可寻,就像一个幽灵一样
如果你还没弄懂 this,或者对它比较模糊,这篇文章就是专门为你准备的,如果你相对比较熟悉了,那你也可以当做复习巩固你的知识点
本篇文章,算是一篇读书笔记,当然也加上了很多我的个人理解,我觉得肯定对大家有所帮助
在理解 this 之前,我们先来看下什么是执行上下文
简而言之,执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行
JavaScript 中有三种执行上下文类型
这里我们先得出一个结论,非严格模式和严格模式中 this 都是指向顶层对象(浏览器中是window)
console.log(this === window); // true
'use strict'
console.log(this === window); // true
this.name = 'vnues';
console.log(this.name); // vnues
后面我们的讨论更多的是针对函数执行上下文
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调 用时的各种条件
牢记:this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包 含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的 其中一个属性,会在函数执行的过程中用到
看个实例,理解为什么要用 this,有时候,我们需要实现类似如下的代码:
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify(context);
console.log(greeting);
}
var me = {
name: "Kyle"
};
speak(me); //hello, 我是 KYLE
这段代码的问题,在于需要显示传递上下文对象,如果代码越来越复杂,这种方式会让你的代码看起来很混乱,用 this 则更加的优雅
var me = {
name: "Kyle"
};
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call(this);
console.log(greeting);
}
speak.call(me); // Hello, 我是 KYLE
下面我们来看在函数上下文中的绑定规则,有以下四种
最常用的函数调用类型:独立函数调用,这个也是优先级最低的一个,此事 this 指向全局对象。注意:如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定 到 undefined,如下所示
var a = 2; // 变量声明到全局对象中
function foo() {
console.log(this.a); // 输出 a
}
function bar() {
'use strict';
console.log(this); // undefined
}
foo();
bar();
还可以我们开头说的:this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
先来看一个例子:
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
当调用 obj.foo() 的时候,this 指向 obj 对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调 用 foo()时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的
记住:对象属性引用链中只有最顶层或者说最后一层会影响调用位置
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
间接引用
另一个需要注意的是,你有可能(有意或者无意地)创建一个函数的“间接引用”,在这 种情况下,调用这个函数会应用默认绑定规则
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
另一个需要注意的是,你有可能(有意或者无意地)创建一个函数的“间接引用”,在这 种情况下,调用这个函数会应用默认绑定规则
赋值表达式 p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是 foo()而不是 p.foo() 或者 o.foo()。根据我们之前说过的,这里会应用默认绑定
在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把 this 间接(隐式)绑定到这个对象上。 那么如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,该怎么
做呢?
Javascript 中提供了 apply 、call 和 bind 方法可以让我们实现
不同之处在于,call() 和 apply() 是立即执行函数,并且接受的参数的形式不同:
而 bind() 则是创建一个新的包装函数,并且返回,而不是立刻执行
看如下的例子:
function foo(b) {
console.log(this.a + '' + b);
}
var obj = {
a: 2,
foo: foo
};
var a = 1;
foo('Gopal'); // 1Gopal
obj.foo('Gopal'); // 2Gopal
foo.call(obj, 'Gopal'); // 2Gopal
foo.apply(obj, ['Gopal']); // 2Gopal
let bar = foo.bind(obj, 'Gopal');
bar(); // 2Gopal
被忽略的 this
如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); // 2
利用这个用法使用 apply(..) 来“展开”一个数组,并当作参数传入一个函数。
类似地,bind(..) 可以对参数进行柯里化(预先设置一些参数)
function foo(a, b) {
console.log("a:" + a + ", b:" + b);
}
// 把数组“展开”成参数
foo.apply(null, [2, 3]); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind(null, 2);
bar(3); // a:2, b:3
当我们使用构造函数 new 一个实例的时候,这个实例的 this 指向是什么呢?
我们先来看下使用 new 来调用函数,或者说发生构造函数调用时,会执行什么操作,如下:
原理实现类似如下:
function create (ctr) {
// 创建一个空对象
let obj = new Object()
// 链接到构造函数的原型对象中
let Con = [].shift.call(arguments)
obj.__proto__ = Con.prototype
// 绑定this
let result = Con.apply(obj, arguments);
// 如果返回是一个对象,则直接返回这个对象,否则返回实例
return typeof result === 'object'? result : obj;
}
注意:let result = Con.apply(obj, arguments); 实际上就是指的是新对象会绑定到函数调用的 this
function Foo(a) {
this.a = a;
}
var bar = new Foo(2);
console.log(bar.a); // 2
我们之前介绍的四条规则已经可以包含所有正常的函数。但是 ES6 中介绍了一种无法使用 这些规则的特殊函数类型:箭头函数
箭头函数不使用 this 的四种标准规则,而是根据定义时候的外层(函数或者全局)作用域来决 定 this。也就是说箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this
function foo() {
// 返回一个箭头函数
// this 继承自 foo()
return (a) => {
console.log(this.a);
}
};
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是 3 !
foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的 this 绑定到 obj1, bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。(new 也不 行!)
判断是否为箭头函数,是则按照箭头函数的规则
否则如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象
如下图所示:
来自:https://www.cnblogs.com/gopal/archive/2022/09/19/16707377.html
JavaScript中有很多令人困惑的地方,或者叫做机制。但是,就是这些东西让JavaScript显得那么美好而与众不同。比方说函数也是对象、闭包、原型链继承等等,而这其中就包括颇让人费解的this机制。
this的绑定过程之前的调用栈 和 调用位置,this绑定规则:1、默认模式,2、隐式绑定,3、显式绑定,4、new绑定
在 JavaScript 中,this 这个特殊的变量是相对比较复杂的,因为 this 不仅仅用在面向对象环境中,在其他任何地方也是可用的。 本篇博文中会解释 this 是如何工作的以及使用中可能导致问题的地方,最后奉上最佳实践。
this关键字在js中的指向问题不管是工作还是面试中都会经常遇到,所以在此对它进行一下总结:全局作用域中、闭包中指window、函数调用模式:谁调用就指谁、构造函数中,this指实例对象、apply/call改变this的指向、bind改变this指向等
如果这很难明白,为什么我们不停止使用它呢?认真的思考一下?如果你读过 将90%的垃圾扔进垃圾桶后,我如何重新发现对JavaScript的爱, 当我说扔掉它时,你不会感到惊讶,this被丢弃了
this的用法:直接在函数中使用 谁调用这个函数this就指向谁 ,对象中使用, 一般情况下指向该对象 ,在构造函数中使用
this的四种绑定策略:默认绑定、隐式绑定、显示绑定、new绑定。首先要明确的是一般情况下,this不是函数被定义时绑定,而是函数被调用时被绑定 ,那么函数中的this有四种绑定方式:
this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象.this永远指向函数的调用者。随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
apply和call的区别就是传的参数形式不一样。call是一个一个的传,apply可以将参数以数组的形式传进去。而bind是传入第二个和后面的参数,且绑定this,返回一个转化后的函数。
以函数形式调用,this指向window;以方法形式调用,this指向调用方法的那个对象;构造函数调用,this指向实例的对象;使用window对象的方法使,指向window;多重场景改变this指向
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!