this
this就是函数运行时所在的环境对象。
this的指向非常的灵活,会根据所处位置和调用的方式不同而发生变化。
this 永远指向一个对象(非严格模式);this 的指向取决于函数调用的位置,或通过 call、apply、bind 修改; this 指向跟调用有关,跟定义无关
全局作用域下的this
全局作用域下的this指向顶层globalThis,浏览器环境中实现为window
console.log(this === window && this === globalThis) //true
node环境中实现为global
console.log(this === global && this === globalThis) //true, node环境
严格模式this不同的地方
// 非严格模式
var name = 'window';
var doSth = function(){
console.log(this.name);
}
doSth(); // 'window'
这样的情况下this指向window,因为es5中全局变量是挂载到顶层对象中的(window),如果将上述代码中的var换成let
// 非严格模式
let name2 = 'window2';
let doSth2 = function(){
console.log(this === window);
console.log(this.name2);
}
doSth2() // true, undefined
这样的情况下,window.name2与window.doSth2都为undefined,this指向window
在严格模式中,表现有一些不同
// 严格模式
'use strict'
var name = 'window';
var doSth = function(){
console.log(typeof this === 'undefined');
console.log(this.name);
}
doSth(); // true,// 报错,因为this是undefined
默认绑定,类似于
doSth.call(undefined);
doSth.apply(undefined);
下面我们分几种情况讨论this的调用
函数中this
function fn(){
console.log(this) //window
}
对象中this
function test() {
console.log(this);
}
var obj = {};
obj.m = test;
obj.m(); // obj
构造函数中this
function Test() {
this.x = 1
console.log(this);
}
var obj = new Test();
obj.x = 1
箭头函数中的this
在 JavaScript 中,箭头函数是 ES6 新增的语法。与传统的函数不同,箭头函数中的 this 指向调用该函数的上下文环境(即当前作用域所属的对象)。 当前this指向obj
let obj = {
name: 'Jack',
func1: function() {
setTimeout(() => console.log(this.name), 1000);
}
}
obj.func1(); // Jack
事件绑定中的this
在 Web 开发中,经常需要将某个函数绑定到 DOM 元素的事件上。此时,函数中的 this 指向通常是绑定事件的元素。
例如,在下面的代码段中,this 指向 button 元素:
<button id="btn" onclick="console.log(this);">Click me</button>
this指向题目
接下来看几道题
function a(){
function b(){
console.log(this)
}
b()
}
a() //window
//this指向一个对象
const object = {
message: 'Hello, World!',
getMessage() {
const message = 'Hello, Earth!';
return this.message;
}
};
console.log(object.getMessage()); // 输出Hello World
const object = {
message: 'Hello, World!',
logMessage() {
console.log(this.message); // => undefined
}
};
setTimeout(object.logMessage, 1000);
// setTimeout使用object.logMessage作为回调,但仍把 object.logMessage 作为常规函数而非方法调用。常规函数中this指向window
棘手的length
var length = 4;
function callback() {
console.log(this.length); // => 4
}
const object = {
length: 5,
method(callback) {
callback();
}
};
object.method(callback, 1, 2);
使用 method() 内部的常规函数调用来调用 callback() 。因为在常规函数调用期间的 this 值等于全局对象,所以在 callback()函数中 this.length 为 window.length。
调用参数
var length = 4;
function callback() {
console.log(this.length); // 输出什么
}
const object = {
length: 5,
method() {
arguments[0]();
}
};
object.method(callback, 1, 2);
因为 arguments[0]() 是对 arguments 对象上 callback 的方法调用,所以 callback 内部的 this 等于 arguments。结果在 callback() 内部的 this.length 与 arguments.length 是相同的,都是3。
call,apply,bind
用法
function fn(food, drink) {
console.log(this.name)
console.log(`今天吃${food},喝${drink}`)
}
const obj = {
name: "obj"
}
fn.call(obj, "米饭", "可乐")
fn.apply(obj, ["米饭", "可乐"])
const fn2 = fn.bind(obj, "米饭", "可乐")
fn2()
手写实现call
Function.prototype.selfCall = function( context, ...params ) {
context == null ? context = window : null
!/^*(object|function)*$/.test( context.toString() ) ? context = Object(context) : null
let _this = this, result = null, UNIQUE_KEY = Symbol('UNIQUE_KEY')
context[UNIQUE_KEY] = _this
result = context[UNIQUE_KEY](...params)
delete context[UNIQUE_KEY]
return result
}
// 简化版,便于理解
Function.prototype.selfCall = function (context, ...params) {
const _this = this
context["KEY"] = _this
const res = context["KEY"](...params)
delete context["KEY"]
return res
}
- 判断context是不是没传,这里偷了个懒,
undefined == null是true - 判断context是不是引用数据类型的,不是的话转成引用数据类型
- 保留this指向,创建一个 Symbol key,不乱写一个是怕自己定义的与context上的冲突
- 把当前函数放到context中,即改变等会函数执行的this指向
- 执行函数,注意📢,这里函数执行是通过
context.UNIQUE_KEY()执行的,也就是函数的this指向context,拿到结果 - 返回结果
// bind:
Function.prototype.myBind = function (context, ...params) {
let _this = this
return function(...arg){
_this.selfCall(context, ...params, ...arg)
}
}
this绑定
默认绑定
就是我们正常书写代码,this 的指向处理(默认绑定作用于函数直接调用的情况下,此时this指向全局对象,但严格模式下this指向undefined。)
隐式绑定
在函数调用时机,this 的指向取决于当前上下文环境
显示绑定
通过使用 apply、call、bind 函数进行处理
this绑定当然也有说四种,把new绑定单独列出来,也有说五种的,单独把箭头函数列出来。分类本身不绝对,重要的是清楚this怎么绑定的。