this
this 的指向会被分为几种情况。
1. 全局环境下
console.log(this); // window
2. 普通函数中的 this 指向全局
严格模式下指向 undefined。
function fn(){
console.log(this); // window
}
fn();
//嵌套函数也指向window
即使是嵌套的函数, this 也是指向全局对象
function outer() {
function inner(){
console.log(this); // window
}
inner();
};
outer();
3. 通过对象调用, this 指向该对象
let obj = {
name: "zhangsan",
talk(){
console.log(this); // 整个 obj
}
}
obj.talk();
function foo() {
console.log(this);
}
foo(); // window
const obj = { name: "zhangsan" };
obj.talk = foo;
obj.talk(); // {name: "zhangsan"}
4. 事件监听器中的 this,指向事件源
btn.onclick = function(){
console.log(this); // <input type="button" value="click me">
}
5. 构造函数中的 this,指向 new 出来的实例对象
function Person(name) {
this.name = name;
console.log(this); // {name:"zhangsan"}
}
let obj = new Person("zhangsan");
之前讲到 new 经历的4个步骤中,就提到了“绑定 this 到空对象”身上。
6. 箭头函数的 this 看声明
箭头函数的 this 指向取决于当前箭头函数声明的环境(执行上下文)。
执行上下文又分为:全局执行上下文、函数级 别上下文、eval 执行上下文。
因为箭头函数没有自己的 arguments 和 this,它内部的this
是由词法作用域上下文确定。(换句话说,箭头函数需要获取函数定义时所在的 EC 的 this,箭头函数定义在哪个作用域中,它的 this 就继承当前作用域 this 的指向。)
1. 定义在全局指向 window
let fn = () = >{console.log(this)} // window
2. 对象中的箭头函数
var name = "小w";
let obj = {
name: "zhangsan",
talk: () => {
console.log(this.name); // 指向了 window,所以打印 小w
}
}
obj.talk();
/*
{} 不会生成作用域,即箭头函数声明在全局作用域中,this 指向全局
通过 var 声明的变量会被认作是 window 的属性 即 var a = 5; window.a == a
*/
3. 普通函数中的箭头函数 看调用
function foo(){
let bar = ()=>{
console.log(this); // window,箭头函数没有自己的 this,它的 this 使用 foo 的 this
}
bar();
}
foo();
修改 this 的指向
三个方法都可以实现修改 this 指向:
-
call()
-
apply()
-
bind()
改指向。call、apply 改变 this 指向的同时,立即自动调用函数。 bind 改变 this 指向并返回一个新函数,需要重新显式调用一次函数。
假设当前存在一个普通函数:
function talk() {
console.log(this); //普通函数,this 必定指向全局 window
console.log(this.girlfriend); // undefined
}
talk();
如果再存在一个对象,让这个对象去调用这个函数,必定就指向该对象:
const obj = {name: "zhangsan"};
obj.talk = talk;
obj.talk(); // zhangsan
但这种写法并不友好,并且存在局限性,所以通常通过另外 3 种方法去修改 this 的指向。这三个方法都是 函数的方法。
语法:
-
fn.call(target)
-
fn.apply(target)
-
fn.bind(target)()
改变 this 指向
function foo() {
console.log(this);
};
const obj = {
name: "zhangsan"
};
foo.call(obj); // zhangsan
foo.apply(obj); // zhangsan
foo.bind(obj)(); // zhangsan
当有参数传入
-
fn.call(target, 20);
-
fn.apply(target, [20]);
-
fn.bind(target)(20);
function foo(a) {
console.log(this, a);
};
const obj = {
name: "zhangsan"
};
foo.call(obj, 1);
foo.apply(obj, [2]);
foo.bind(obj)(3);
总结:各种this实例结果及推理
普通函数看调用,箭头函数看声明环境,var声明的变量能提升为window对象的键
普通函数看调用,箭头函数看声明环境,不是var声明的不能变量能提升为window对象的键,所以number没有值为undefined
第一个this为普通函数内部 看调用,所以最后面zhangsan.talk调用,即为const zhangsan{}这个大的对象,所以this为const zhangsan{},
又因为箭头函数看声明环境,生命在普通函数内部,所以第二个this的值为普通函数的 调用对象,所以也是const zhangsan{}这个大对象
foo(){}为语法糖,为普通函数,有因为普通函数调用为person对象,所以内部的箭头函数this指向为对象person,即this.name为person.name的值
因为两个箭头函数都声明在全局中,即this指向的为window对象
person.say赋值给左侧的student.say,即student对象也拥有了say这个方法,并且该方法是箭头函数,即声明在全局中,所以调用say()方法时是指向window对象,即this.name为字符串“window”
var name = 'john';
var obj = {
name: 'colin',
prop: {
name: 'rose',
getname: function () {
return this.name;
}
}
}
console.log(obj.prop.getname());//rose //普通函数看调用 prop的name
var test = obj.prop.getname; 把函数赋值给text
console.log(test()); //john 调用函数 指向全局 name = john