在方法中,this 表示该方法所属的对象;如果单独使用,this 表示全局对象;在函数中,this 表示全局对象;在函数中,在严格模式下,this 是未定义的(undefined);在事件中,this 表示接收事件的元素。
this的绑定规则:
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
3.1 默认绑定
独立函数调用就是所谓的默认绑定,独立函数调用我们可以理解为函数没有绑定在某个对象上进行调用。
<script>
function fn() {
console.log(this);
}
// 独立的函数调用
// 在浏览器中This表示window
fn();
</script>
<script>
function fn() {
console.log(this);
}
function an() {
console.log(this);
fn();
}
function kn() {
an();
console.log(this);
}
kn(); // 输出三个window,因为都是独立函数调用
</script>
<script>
var obj = {
name: 'wc',
fn: function () {
console.log(this);
}
}
obj.fn(); // {name: 'wc', fn: ƒ}
var gn = obj.fn;
gn(); // window
</script>
3.2 隐式绑定
通过某个对象进行调用,也就是它的调用位置中,是通过某个对象发起的函数调用。
<script>
function fn() {
console.log(this);
}
var obj = {
name: 'wc',
fn: fn
}
// fn中的this是谁呢?就看点前面是谁
obj.fn(); // {name: 'wc', fn: ƒ}
</script>
<script>
var obj = {
name: 'wc',
fn: function () {
console.log(this);
}
}
var obj2 = {
name: 'xq',
gn: obj.fn // 注意这里函数没有执行,所以传入的是一个地址
}
obj2.gn() // obj2
</script>
可以将上面代码进行一个变形:
<script>
var obj = {
name: 'wc',
fn: function () {
return this;
}
}
var obj2 = {
name: 'xq',
gn: obj.fn() // 这里函数执行了,所以gn对应的是obj的值
}
console.log(obj2.gn) // obj
obj2.gn() // 这里会报错,因为gn不是函数
</script>
3.3 显示绑定
隐式绑定有一个前提条件:
必须在调用对象的内部有一个对函数的引用(比如一个属性),如果没有这样的引用,在进行调用时,会有找不到函数的错误,正是通过这个引用,间接的将this绑定到了这个对象上。
如果我们不希望在对象内部包含整个函数的引用,同时又希望在整个对象上进行强制调用,该怎么做呢?
-
JavaScript的所有函数都可以使用call和apply方法(这个和prototype有关)
- 它们的第一个参数是相同的,后面的参数,apply为数组,call是单独参数输入。
-
这两个函数的第一个参数都要求是一个对象,调用这个函数时,会将this绑定在这个传入的对象上。
因为上面的过程,我们明确绑定了this的指向,所以称为显示绑定。
<script>
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2); // window 3
let obj = { name: 'wc' }
// call做了什么?
// 1)让fn执行 2)改变函数中this的指向
// 第二个函数开始传递的是实参
fn.call(obj, 1, 2); // obj 3
// apply就是实参使用数组传入
fn.apply(obj, [1, 2]); // obj 3
// bind的作用:
// 1)只会改变函数中this的指向,不会让函数执行
// 2)返回一个改变了this指向的新函数
let newFn = fn.bind(obj,1,2);
newFn(); // 这个实参也可以写到这里
</script>
如果call、apply和bind后面跟的是基本数据类型,如下:
<script>
function fn() {
console.log('fn...');
console.log(this);
}
fn.call('hello'); // 把'hello'封装成一个新对象
fn.call(undefined) // this指向window
fn.call(null) // this指向window
</script>
3.4 new绑定
function Fn() {
// new做了什么
// 1)在类中创建一个对象
// 2)让类中的this绑定到对象
// 3)返回对象
this.name = 'wc';
this.age = 18
}
let f1 = new Fn();
console.log(f1); // Fn {name: 'wc', age: 18}
3.5 内置函数中的this绑定
<script>
setInterval(function () {
console.log(this); // window
}, 2000)
</script>
事件中的this:
<button>点击</button>
<script>
let btn = document.querySelector('button');
btn.onclick = function () {
console.log(this); // button
}
</script>
数组方法中的this:
<script>
let name = ['wc', 'nih', 'jll'];
name.forEach(function () {
console.log(this); // window
});
name.forEach(function () {
console.log(this); // {name: '666'}
}, { name: '666' });
</script>
3.6 优先级
显示绑定优先级高于隐式绑定
<script>
let obj = {
name: 'wc',
fn: function () {
console.log(this);
}
}
let newObj = { name: 'xq' }
// 显示绑定优先于隐式绑定
obj.fn.call(newObj) // this指向newObj
</script>
new绑定优先级高于隐式绑定
<script>
let obj = {
name: 'wc',
fn: function () {
console.log(this);
}
}
let res = new obj.fn() // fn {}
console.log(res); // fn {}
</script>
new绑定优先级高于bind绑定,注意bind返回是函数,所以可以new,call和apply返回的是函数执行结果,不能new。
<script>
function fn() {
console.log(this);
}
var gn = fn.bind({ name: 'wc' })
let kn = new gn(); // fn {}
</script>
3.7 特殊情况
3.7.1 忽略显示绑定
当显示绑定的参数为null和undefined的时候,忽略显示绑定
<script>
function fn() {
console.log(this);
}
fn.call({ name: 'wc' }) // { name: 'wc' }
fn.call(null) // window
fn.call(undefined) // window
</script>
3.7.2 间接函数引用
<script>
var obj = {
name: 'wc',
fn: function () {
console.log(this);
}
}
var obj2 = {
name: 'xq'
};
// obj2.gn = obj.fn;
// obj.gn(); // obj2 这里是隐式绑定
(obj2.gn = obj.fn)(); // 立即执行函数,这里this 表示window
</script>
3.8 箭头函数的this
<script>
var fn = () => {
// 箭头函数中的this需要向外找一级
console.log(this);
}
var obj = { name: 'wc' };
fn.call(obj) // window
</script>
<script>
var obj = {
name: 'wc',
fn: () => {
console.log(this);
}
};
obj.fn() // window
</script>
3.9 练习题
<button>点击</button>
<script>
let btn = document.querySelector('button');
function f() {
console.log(this);
}
btn.onclick = f(); // window 这里f()是直接执行,无返回值,相当于undefined
</script>
<script>
let wc = {
name: 'wc',
age: 100,
eat: function () {
console.log('eat...');
console.log(this);
}
}
let mm = wc.eat;
mm(); // eat... window
</script>
<script>
var num = 10;
var obj = { num: 20 }
obj.fn = (function (num) {
this.num = num * 3;
num++;
console.log(num);
return function (n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num) // IIFE,输出21
var fn = obj.fn;
fn(5); // 这里是独立函数调用,所以this是window,输出22
console.log(window.num); // 65
console.log(obj.num); // 20
</script>
更改一下上面的代码就不是独立函数调用了
<script>
var num = 10;
var obj = { num: 20 }
obj.fn = (function (num) {
this.num = num * 3;
num++;
console.log(num);
return function (n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num) // IIFE,输出21
obj.fn(10); // 隐式调用,这里的this是obj
// 但是num仍然是闭包的num,所以输出22
console.log(num); // 60
console.log(obj.num); // 30
</script>