JavaScript 中 this 关键字
- this 是什么
- this 的绑定规则
- this 的指向
this 是什么
this 是一个关键字,能够在函数执行过程中访问运行环境,它的值根据函数的调用方式和上下文而变化,所以 this 是动态的,动态指向当前函数的运行环境。
this 是一个对象,当函数执行时产生的内部对象,this 指向只与函数的执行环节有关,与函数的声明无关。
this 是一个指针型变量,在 JavaScript 中没有指针的概念,但是this真实地指向当前调用对象,能够在函数内部访问和操作当前对象的属性和方法。
this 的绑定规则
1、默认绑定:
当函数独立调用时,即没有任何上下文对象时,默认绑定规则会将 this
绑定到全局对象(在浏览器中是 window
对象,在 Node.js 中是 global
对象)。
在严格模式下,默认绑定会将 this
绑定为 undefined
。
function foo() {console.log(this);
}foo(); // 在浏览器中输出:window,在 Node.js 中输出:global
function foo(){console.log(this.a);
}var a=2;
foo(); //在浏览器中输出:2,在 Node.js 中输出:undefined
2、隐式绑定:
当函数作为对象的方法调用时,会将 this
绑定到调用该方法的对象上。
function foo(){console.log(this); // {name: 'zhangsan', sayHello: ƒ}console.log(this.name); // zhangsan
}var obj={name:'zhangsan',sayHello:foo //函数引用
}obj.sayHello()
下面方式是同样的:
const obj = {name: 'zhangsan',sayHello: function() {console.log('Hello, ' + this.name);}
};obj.sayHello(); // 输出:Hello, zhangsan
对象属性引用链只有在上一层或者说最后一层的在调用位置起作用,即只会指向上一个调用该函数的对象。
function foo(){console.log('指向的是:', this.name);}var obj1={name:'obj1',foo:foo //隐式绑定的函数
}var obj2={name:'obj2',obj1:obj1
}obj2.obj1.foo();//指向的是:obj1 绑定对象是obj1,而不是obj2
3、显示绑定
通过 call
、apply
或 bind
方法,可以显式地指定函数内部的 this
值。
call
方法接受一个指定的对象作为第一个参数,将该对象绑定到函数的this
。如果不传参数或传null、undefined 参数时都指向 Windows。
语法:function.call(thisArg, arg1, arg2, …) 其中,thisArg是要绑定的对象,它将代替调用函数中的this关键字。后面的参数arg1、arg2等是可选的参数,它们被传递给调用的函数。
function foo() {console.log('Hello, ' + this.name);
}var obj = {name: 'obj'
};// 将 obj 绑定到函数 foo 上面
foo.call(obj); // 输出:Hello, obj
apply
方法与call
类似,参数以数组形式接受。
语法:function.apply(thisArg, [argsArray])
。其中,thisArg是要绑定的对象,argsArray是一个数组类型的参数列表,它们被传递给调用的函数。
function foo(params) {console.log(params + ', ' + this.name);
}var obj = {name: 'obj'
};// 将 obj 绑定到函数 foo 上面,并向 foo 传递参数
foo.apply(obj,['Hello']); // 输出:Hello, obj
call和apply方法在函数调用时立即执行,而bind方法返回一个新的函数,需要手动调用。apply的参数需要以数组形式传入。
bind
方法会创建一个新函数,并将指定的对象绑定到新函数的this
。bind
方法不会立即调用函数,而是返回一个绑定了this
的新函数。
function foo() {console.log('Hello, ' + this.name);
}var obj = {name: 'obj'
};// bind 会创建一个绑定 this 的新函数进行返回,但不会立即调用
var newFoo = foo.bind(obj);
newFoo(); // 输出:Hello, Bob
4、new
绑定:当使用 new
关键字调用函数作为构造函数来创建新对象时,this
会绑定到新创建的对象。
function Person(name) {this.name = name;
}var zhangsan = new Person('Zhangsan');
console.log(zhangsan.name); // 输出:Zhangsan
5、箭头函数:箭头函数中的 this
继承自外部作用域,与函数的调用方式无关。
const obj = {name: 'Alice',sayHello: () => {console.log('Hello, ' + this.name);}
};obj.sayHello(); // 输出:Hello, undefined
this 的指向
1、函数的普通调用
当函数直接调用时,this 指向调用它的那个对象,一般使用默认绑定。
function foo() {let a = "function name";console.log('a', a); // function nameconsole.log('this.a', this.a); // window name
}var a = "window name";
foo();//调用A(),当前运行环境是window,this指向window对象
2、构造函数
构造函数的 this 永远指向实例化对象
严格模式下,如果构造函数不加new调用,this 指向的是 undefined 如果给他赋值,则会报错
var Func = function () {this.a = "我是构造函数的属性";this.fun = function () {console.log(this);}
}
let myFunc = new Func();
myFunc.fun();//Func { a: '我是构造函数的属性', fun: [Function (anonymous)] }
3、箭头函数
箭头函数不会创建自己的 this ,所以它没有自己的 this,它只会从自己的作用域链的上一层继承 this。
如果外部函数是普通函数,this 的指向取决于外部函数的绑定类型,外部函数!=定义箭头函数的外部对象。
function foo() {var a = "function"setInterval(() => {console.log(this.a);//浏览器每个1秒打印window; node.js每隔一秒打印undefined}, 1000)
}
var a = "window"
foo();//箭头函数外部函数是foo; foo没有任何绑定,则使用默认绑定