1. これは問題を示しています
関数の呼び出し方法の違いによって方向が決まり、通常は関数の呼び出し元を指します。
1. 世界的に
グローバルに、これは常に window オブジェクトを指します。
// 全局下, this始终指向window对象
console.log(this); // window 相当于window.console.log(this); window
2.関数内
(1) 通常の関数として呼び出される場合
非厳密モードでは、これはウィンドウを指します
厳密モードでは、これは未定義を指します
// 函数中 this 关键字的指向问题
// 1. 作为函数被调用
// (1) 非严格模式下, 直接被调用 this指向Window
function fun(){
console.log('非严格模式下, 直接被调用==>',this); //非严格模式下, 直接被调用==> Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
fun() // Window
// (2)在函数中,严格模式下,直接调用函数, this指向undefined
function strictFun(){
'use strict'
console.log('严格模式下, 直接被调用==>',this); //严格模式下, 直接被调用==> undefined
}
strictFun() // undefined
(2) メソッドとして呼び出す場合(オブジェクトメソッド)
これはメソッドの所有者を指します
オブジェクトの内部メソッドの this は、それを呼び出す人を指します。
// 2. 作为方法被调用, this指向方法拥有者(当多层对象嵌套时, this指向被调用函数最近的对象) 当函数作为对象的某个属性时, 可称这个函数为方法
// 对象内部方法的this 谁调用就指向谁
let obj = {
uname: 'mm',
eat1(){
console.log('eat1===>', this);
},
eat2: function(){
console.log('eat2==>', this);
},
eat3: ()=>{
console.log('eat3==>',this);
},
objjj: {
name: 'aaa',
eat4(){
name: 'bbb'
console.log(this.name); // aaa
console.log('内层对象this',this);
}
}
}
obj.eat1() // obj
obj.eat2() // obj
obj.eat3() // window 箭头函数没有this, 他的this指向来源于父级this指向
obj.objjj.eat4() // objjj 被调用函数最近的对象
(3) コンストラクタとして呼び出される場合
通常はインスタンス化されたオブジェクトを指します
// 3. 作为构造函数来使用, 通常指向实例化对象
function User(){
this.uname = 'mmm';
this.age = 21;
console.log('构造函数User中的this===>',this);
}
let mm = new User()
console.log('mm===>',mm); // {uname: 'mmm', age: 21}
補充:
コンストラクターが new の処理中に何が起こるか:
空のオブジェクトを作成する
空のオブジェクトはこのパラメーターとしてコンストラクターに渡され、コンストラクターのコンテキストになります (例: コンストラクター内の this を mm に置き換えます)
新しく構築されたオブジェクトは new 演算子の戻り値として返されます (コンストラクターが返されたオブジェクトを表示するときに例外が発生します。コンストラクター内にオブジェクト型の戻り値があるかどうかを確認してください)。
// 构造函数内部有返回值,为非对象类型, 使用new关键字后,会走正常实例化的流程,将tt传递给this
function User1(){
this.uname = 'ttt';
this.age = 22;
console.log('构造函数User1中的this===>',this);
return 1;
}
let tt = new User1()
console.log('tt===>', tt); // {uname: 'ttt', age: 22}
// 构造函数内部有返回值,且为对象类型, new关键字创建对象后, 会直接返回构造函数内部的对象
function User2(){
this.uname = 'ttt2';
this.age = 22;
console.log('构造函数User2中的this===>',this);
return {
n: 'haha'
}
}
let tt2 = new User2()
console.log('tt2===>', tt2); //{n: 'haha'}
(4) アロー関数内
this ポイントは、親の this ポイント (非アロー関数の親) から取得されます。
アロー関数には this と引数はありません。 this ポイントは親の this ポイントから取得され、定義時に決定され、call()、apply()、bind を使用した場合でも、その後変更されることはありません。 () などのメソッドで方向を変更することはできません
// 箭头函数 箭头函数中没有this和arguments, 他的this指向来源于父级this指向,且被定义的时候就确定了,之后永远都不会改变,即使使用call()、apply()、bind()等方法改变this指向也不可以
let fn = ()=>{
console.log(this);
}
fn() // window
(5) 関数を即時実行する
これはウィンドウオブジェクトを指します
// 立即执行函数 this指向window对象
;(function(){
console.log('立即执行函数==>',this); //window
}())
(6) タイマー機能の場合
これはウィンドウオブジェクトを指します
// 定时器函数中 this指向window对象
setTimeout(function(){
console.log('定时器函数===>',this);
}, 1000)
3. イベントバインディングメソッド内
イベント バインディング メソッドでは、イベントにバインドされたオブジェクトを指します。
<!-- 事件绑定方法中, this指向绑定事件的对象 -->
<button>事件绑定方法</button>
<script>
document.querySelector('button').addEventListener('click',function(){
console.log('事件绑定方法===>',this); // <button>事件绑定方法</button>
})
</script>
4. クロージャー内
// 闭包中的this
const obj = {
name: '内---mm',
getName: function () {
return this.name
},
}
console.log('getName===>', obj.getName()) // 内---mm
const getNameFun = obj.getName
// getNameFun 是一个函数 调用时,没有其他调用者,那么调用者即为Window对象, const声明的变量在window上没有,故为undefined, 若为var声明则为var声明的变量值
console.log('getNameFun==>', getNameFun()) //undefined
const obj1 = {
name: '内1111---mm',
getName: function () {
return function () {
return this.name
}
},
}
// obj1.getName获得一个函数function(){ return function(){ return this.name }, ()执行一次,又得到一个函数function(){return this.name},再()执行返回this.name, 此时没有其他调用者调用
console.log('getName1===>', obj1.getName()()) //undefined
const getNameFun1 = obj1.getName()
console.log('getNameFun1==>', getNameFun1()) //undefined
const obj2 = {
name: '内1111---mm',
getName: function () {
//保存环境,保存this, 保存当前调用者 that常驻内存
const that = this
// 闭包 函数套函数, 内部函数可访问函数体外的变量
return function () {
return that.name
}
},
}
console.log('getName2===>', obj2.getName()()) //内1111---mm
const getNameFun2 = obj2.getName()
console.log('getNameFun2==>', getNameFun2()) //内1111---mm
2. 方向を変える
JavaScript は特に、関数内で this のポインティングの問題に対処するいくつかの関数メソッド (多くの場合、bind()、call()、apply()) を提供します。
1. call() メソッド
fun.call(thisArg, arg1, arg2, ...)
すぐに実行する
thisArg オプション パラメータ。非厳密モードで関数の実行時に使用される this 値。null または未定義として指定された場合、自動的にグローバル オブジェクトに置き換えられます。
arg1、arg2 で指定されたパラメータのリスト
オブジェクト (呼び出し可能関数) を呼び出すと、関数のこの時点を変更できます。
戻り値: メソッドの戻り値。戻り値がない場合は未定義を返します。
アプリケーション: 継承の実装
// call(thisArg, arg1, arg2...) thisArg可选参数, 函数运行时使用的this值, arg1,arg2指定的参数列表
function callThis(sex){
console.log(`name: ${this.name},sex: ${sex}`); // name: mm,sex: girl
return this.name
}
let person1 = {name:'mm'}
let res1 = callThis.call(person1,'girl')
console.log('res1===>', res1); // mm (函数内部自身的有返回值,返回了this.name, 若没有会返回undefined)
// 非严格模式下,则指定为null或undefined时会自动替换为全局对象
var num = 1
function showNum(){
console.log('num===>', this.num);
}
showNum.call(undefined) // 1
function showNum1(){
console.log('num===>', this.num);
}
showNum1.call(null) // 1
// 严格模式
"use strict";
var number = 2
function showNum2(){
console.log('number===>', this.number);
}
showNum2.call(undefined) // 报错 Uncaught TypeError: Cannot read properties of undefined (reading 'number')
2. apply() メソッド
fun.apply(thisArg, [argsArray])
すぐに実行する
thisArg オプション パラメータ。非厳密モードで関数の実行時に使用される this 値。null または未定義として指定された場合、自動的にグローバル オブジェクトに置き換えられます。
引数は配列 [argsArray] の形式でなければなりません
戻り値: メソッドの戻り値。戻り値がない場合は未定義を返します。
呼び出し可能な関数。関数のこの時点を変更できます。
用途: 多くの場合、配列に関連します。たとえば、数学オブジェクトを使用して配列の最大値と最小値を取得します。
// apply() 与 call()相同, 只是参数需要传递一个数组
function applyThis(sex){
console.log(`name: ${this.name},sex: ${sex}`); // name: tt,sex: boy
return this.name
}
let person2 = {name:'tt'}
let res2 = callThis.call(person2,['boy'])
console.log('res2===>', res2); // tt
3.bind()メソッド
fun.bind(thisArg, arg1, arg2, ...)
新しい関数が作成され、新しい関数が呼び出されたときに実行されます。
thisArg オプション パラメーター。非厳密モードで関数が実行されているときに使用される this 値。null または未定義として指定されている場合、バインドされた関数の構築に new 演算子が使用されている場合、自動的にグローバル オブジェクトに置き換えられます。 、この値は無視されます
arg1、arg2 は、ターゲット関数が呼び出されるときにパラメータ リストにあらかじめ設定されているパラメータであり、ターゲット パラメータが呼び出されるときにパラメータが渡され、パラメータの数は置き換えられるのではなく増加されます。
戻り値: 指定された this 値と初期パラメータを持つ元の関数のコピー (指定された this 値と初期化パラメータから変換された元の関数のコピーを返します (元の関数が this を変更した後に生成された新しい関数を返します))
関数は呼び出されず、関数の内部の this ポイントは変更できます。
アプリケーション: タイマー内の this ポインターを変更するなど。
// bind() 原函数的拷贝,并拥有指定的this值和初始参数
function bindThis(sex){
console.log(`name: ${this.name},sex: ${sex}`);
return this.name
}
let person3 = {name:'kkong'}
let res3 = bindThis.bind(person3,'boy')
console.log('res3===>', res3);
/* res3===> ƒ bindThis(sex){
console.log(`name: ${this.name},sex: ${sex}`);
return this.name
} */
// res3是个函数 原函数的拷贝,并拥有指定的this值和初始参数
res3() //name: kkong,sex: boy
// 使用new运算符构造绑定函数, 则忽略thisArg这个可选参数
new res3() // name: undefined,sex: boy
// 当目标参数调用时传入参数,会增加参数数量而不是替换
function Arg(){
console.log(Array.from(arguments).join());
}
var result1 = Arg.bind()
result1() // 空白
var result2 = Arg.bind(undefined,1,2)
result2() //1,2
var result3 = Arg.bind(undefined,3,4)
result3(5,6) //3,4,5,6
const a = new result3(7,8,9) //3,4,7,8,9
const b = new Arg() // 空白
// 改变函数内this指向
// 1. call() 可调用函数,可改变函数内this指向. 主要作用: 实现继承
function Father(uname){
this.uname = uname;
}
function Son(uname, uage){
Father.call(this,uname,uage); //调用函数Father,将其this修改为Son中的this,并且添加uage属性 应用: 实现继承
}
// 2. apply() 可调用函数,可改变函数内this指向 参数必须是数组(伪数组) 应用: 利用apply借助数学内置对象求最大/小值
var o = { };
function fn(arr){
console.log(arr);
}
fn.apply(o,['pink']);
var arr1 = [1,3,5,2,4]; //求数组最大/小值
console.log(Math.max.apply(null, arr1));//不需改变this指向写null
// 3. bind() 绑定
var fn1 = fn.bind(o); //不调用原函数, 可改变原函数内部this指向
fn1; //返回原函数改变this之后产生的新函数
// 点击按钮后禁用,三秒后开启使用 定时器函数不需要立即执行,改变this指向用bind
var btn = document.querySelector('button');
btn.onclick = function(){
this.disabled = true; //this指向btn 按钮禁用
setInterval(function(){
this.disabled = false; //开启按钮 定时器中this指向window
}.bind(this),3000); //此处this指向btn对象
}