this指针详解
JavaScript 中的 this 指向非常灵活,它可以根据函数的调用方式和所处的环境不同而发生变化。
在 JavaScript 中,this 关键字表示当前函数执行的环境。当一个函数被调用时,JavaScript 引擎会为该函数创建一个执行环境,其中包括了函数内部的变量和对象等。而 this 关键字则用来指代当前执行环境的上下文。
例如,当我们在一个函数内部使用 this 关键字时,它会指向当前函数执行的环境,比如一个 DOM 元素、一个全局对象或者是一个函数的实例等。如果我们想要在函数内部访问外部的变量或对象,就需要通过 this 关键字来引用它们。
此外,还有一些其他的与 this 相关的关键字,如 call()、apply()、bind() 等。它们可以用来改变函数执行环境、实现函数的高阶调用、绑定函数的上下文等。 context(上下文环境,webpack 钩子,compiler 对象,compilation 对象始终贯穿于整个钩子的)。
this 永远指向一个对象;
this 的指向取决于函数调用的位置,或通过 call、apply、bind 修改;**
this 指向跟调用有关,跟定义无关
下面我们具体介绍了 this 在不同场景下的指向:
全局作用域下的this
例如以下问题:
//demo1
function fun(){
console.log(this.s);
}
var obj = {
s:'1',
f:fun
}
var s = '2';
obj.f(); //1
fun(); //2
//demo2
var btn = document.getElementById('btn');
btn.onclick = function(){
this ; // this指向本节点对象
}
//demo3
var obj = {
fun:function(){
this ;
}
}
setInterval(obj.fun,1000); // this指向window对象
setInterval(obj.fun(),1000); // this指向obj对象
全局作用域
全局作用域指的是没有包含在任何函数中的代码块。如果在全局作用域中使用 this,它的指向就是浏览器的 Window 对象(在 Node.js 中则是 Global 对象)。
例如,下面的代码段中,this 指向全局对象 Window:
console.log(this); // Window
对象方法中的 this
在 JavaScript 中,一个对象可以包含多个方法。如果在对象的方法中使用 this,它的指向就是该对象本身。 例如,下面的代码段中,this 指向 obj 对象:
let obj = {
name: 'John',
sayName() { console.log(this.name); }
}
obj.sayName(); // John
构造函数中的 this
function Person(name, age) {
this.name = name;
this.age = age;
}
let p1 = new Person('Tom', 25);
console.log(p1); // Person {name: "Tom", age: 25}
call 和 apply 方法中的 this
JavaScript 中的函数对象具有 call 和 apply 两种方法。这两种方法允许开发者在调用函数时自定义 this 的值。
let foo = {
value: 1
}
function bar() {
console.log(this.value)
}
bar.call(foo)//1
手写call 最终完整版
Function.prototype.myCall = function(context, ...args){
if(typeof context==='undefind' || context==null){
context= window
} //等同于 var context = context||window;
let fnSymbol = Symbol();//每个从 Symbol() 返回的 symbol 值都是唯一的
context[fnSymbol] = this;
let result = context[fnSymbol](...args);//入参...args === [...arguments].slice(1);
delete context[fnSymbol];//删除目的是不污染原来数据
return result;
}
手写apply 最终完整版(与call不同的是:args参数是一个数组)
Function.prototype.myApply = function(context, args) {
if(typeof context ==='undefind' || context === null) {
context = window
}
let fnSymbol = Symbol();
let fn = context[fnSymbol](...args);
delete context[fnSymbol];
return fn;
}
手写bind 最终完整版(返回一个函数,不会执行)
1.改变this的指向为context
2.返回一个函数
Function.prototype.myBind = function(context, ...args1) {
var _this = this;
var fnTemp = new Function(){};
const fBound = return function(...args2) {
return _this.apply( this typeof fnTemp ? this :context, [...args, ...args2])
}
fnTemp.prototype = this.prototype;
fBound.prototype = new fnTemp();
return fBound;
}
模拟new 最终完整版
1.返回对象
2.访问构造函数上的属性
3.访问原型(prototype)上的属性
function objectFactory() {
var obj = new Object(),
var Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var result = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? result : obj;
};
箭头函数中的 this
在 JavaScript 中,箭头函数是 ES6 新增的语法。与传统的函数不同,箭头函数中的 this 指向调用该函数的上下文环境(即当前作用域所属的对象)。
例如,在下面的代码段中,this 指向 obj:
let obj = {
name: 'jinjin',
func: function() {
setTimeout(() => console.log(this.name), 1000);
}
}
obj.func(); // jinjin
函数作为参数和返回值时的 this
JavaScript 中的函数可以被传递给其他函数作为参数或返回值。如果一个函数作为参数被传递给另一个函数,那么其中的 this 指向可能会发生变化。这在很多框架中处理链式调用时常用。
例如,在下面的代码段中,this 指向全局对象 Window 而不是 obj:
let obj = { name: 'jinjin' };
function func2() {
console.log(this.name);
}
function func1() {
func2();
}
func1.call(obj); // undefined
同样地,在下面的代码段中,this 也不会指向 obj:
let obj = { name: 'Lily' };
function func2() {
console.log(this.name);
}
function func1() {
return func2;
}
let f = func1();
f(); // undefined
事件绑定中的 this
在 Web 开发中,经常需要将某个函数绑定到 DOM 元素的事件上。此时,函数中的 this 指向通常是绑定事件的元素。
例如,在下面的代码段中,this 指向 button 元素:
<button id="btn" onclick="console.log(this);">Click me</button>
this 可能出现的坑点
由于 JavaScript 中的 this 指向比较灵活,所以在实际开发中,可能会出现一些常见的坑点。
例如,在下面的代码段中,this 指向全局对象 Window 而不是 obj:
let obj = { name: 'jinjin' };
document.querySelector('#btn').addEventListener('click', function() {
console.log(this.name);
});
事件绑定时的 this 实际上是指向触发事件的元素,而不是 obj。
同样地,在下面的代码段中,this 也不会指向 obj:
let obj = { name: 'jinjin' };
setTimeout(function() {
console.log(this.name);
}, 1000);
由于 setTimeout 不属于任何对象,所以 this 的指向默认为全局对象 Window。
按绑定形式分类
this 绑定/指向方式总结为三点:
默认绑定:全局作用域下的 this,正常书写代码,this 指向处理
隐式绑定:this 取决于当前执行上下文环境
显式绑定:通过 call、apply、bind 改变 this 指向
this 与作用域相关问题面试常问的
谈谈你对 this 指向问题的理解
箭头函数的一些特性
手写实现 call、apply、bind 函数
new 创建一个对象时,做了哪些事情