this 是什么
this
是 js 中的一个关键字
它是函数运行时,在函数体内部自动生成的一个对象,它只有在函数内部时才可以使用
总结一下,this
就是函数运行时所处的环境对象(即执行期上下文)
执行期上下文:所有的函数在被调用时们都会创建一个执行期上下文,执行期上下文中记录着函数的调用栈、函数的调用方式等,而 this
就是其中的一个属性
this 是什么时候绑定的
既然 this
绑定的是函数运行时所处的环境对象,那么 this
是什么时候绑定的呢
this
是在函数被调用的时候绑定的
举个栗子:当函数 test
执行的时候,就会将this
的值绑定为 window
function test() {
console.log(this);
}
test(); //window
再举个栗子
function test() {
console.log(this);
}
var obj = {
name: '张三',
foo: test
};
obj.foo(); // obj
function test1() {
console.log(this);
}
var obj1 = {
name: '张三',
foo: function test1() {
console.log(this);
}
};
var foo = obj1.foo;
foo(); // window
这个栗子中,为什么执行的都是同一个函数test
,但this
的值一个是 obj
,另一个是却是window
呢
从这里貌似可以得出这样的结论:
this
值的绑定和函数定义时的位置没有关系this
值的绑定和函数调用的方式有关,谁调用函数,this
就绑定为谁(或者说是指向谁)this
是在运行时,才会别绑定的,光定义、不运行相当于不起作用
this 是如何绑定的
绑定的方法大概有以下这几种
- 默认绑定,即函数独立调用,
this
指向window
- 隐式绑定,即函数通过某个对象进行调用,那么
this
就指向这个调用的对象 - 显示绑定,即
call、apply、bind
new
绑定
默认绑定
函数单独的被调用,并没有绑定到那个对象上
// 1、函数直接被调用
function test() {
console.log(this)
}
test(); // window
// 2、一个函数调用另一个函数,函数只是在另一个函数里面被单独的直接调用,并没有绑定到那个对象上
function test1() {
console.log(this);
test2(); // window
}
function test2() {
console.log(this);
test3(); // window
}
function test3() {
console.log(this);
}
test1(); // window
//3、将函数作为参数传递到另一个函数中
// 这里的参数 fun 只是一个函数的引用,然后在 foo 函数里面 直接被调用, 类似于上面的2
function foo(fun) {
fun();
}
function bar() {
console.log(this);
}
foo(bar); // window
隐式绑定
函数的执行是通过某个对象调用的,则 this
被绑定到调用它的对象上
函数的引用和函数的调用要区分开
var obj1 = {
name: 'obj1',
foo() {
console.log(this);
}
};
// obj1对象直接调用这个 foo方法
// 对象里面的函数一般称为方法
// 函数被一个对象直接调用(执行),this 绑定的是这个对象
obj1.foo(); // obj1
var obj2 = {
name: 'obj2',
obj1: obj1
};
// 一个对象(obj2)引用另一个对象(obj1),执行obj1里的方法(foo),实际调用(执行)foo 的函数obj1,所以 this 指向的还是 obj1
obj2.obj1.foo(); // obj1
var obj3 = {
name: 'obj3',
obj1: obj1.foo,
fun() {
console.log(this);
}
};
// 这里保存的是这个函数的引用,调用函数的话就相当于 obj3 直接调用
// obj1: obj.foo 就相当于下面的 fun() {}
obj3.obj1(); // obj3
// 将 obj1 里的方法 foo 给了 bar ,再由 bar 调用,函数单独直接被调用,指向 window
var bar = obj1.foo;
bar(); // window
显示绑定
也就是平常所说的通过 call、apply、bind
直接改变 this
的指向
function foo() {
console.log(this);
}
var obj1 = {
name: 'obj1'
};
foo.call(window); // window
foo.apply(obj1); // obj1
var bar = foo.bind(123);
bar(); // 数字123的包装类,会在内部自动执行 Number(123)
使用这三个显示绑定 this
的注意点
- 使用过
bind
绑定this
之后,再通过call、apply
改变this
的绑定是无效的(例1) - 原函数
test
,对其使用bind
进行绑定后的返回的函数,再进行bind
绑定是无效的(例2) - 进行绑定时,如果
this
传的是null
或undefined
,那么此时绑定的this
是无效的(例3)
function foo() {
console.log(this);
}
var obj1 = {
name: 'obj1'
};
var obj2 = {
name: 'obj2'
}
// 例1
var bar = foo.bind(obj1);
bar(); // this 已经绑定为 obj1,再进行 call、或 apply进行绑定是无效的
bar.call(obj2); // obj1
bar.apply(obj2); // obj1
// 例2
var bar = foo.bind(obj1);
bar();
var bar1 = bar.bind(obj2);
bar1();
// 例3
foo.call(null); //window
foo.apply(undefined); // window
var fun = foo.bind(null);
fun(); // window
new 绑定
指向实例对象
function Person(name, age) {
this.name = name;
this.test = function test() {
console.log(this.name);
}
}
var person1 = new Person('李四', 18);
person1.test(); // 李四
var person1 = new Person('张三', 18);
person2.test(); // 张三
绑定的优先级
- 默认绑定的优先级最低
- 显示绑定的优先级高于隐式绑定的优先级
new
绑定的优先级高于显示绑定中bind
的优先级(new 、call 、apply 三者同时使用会报错)
function foo() {
console.log(this);
}
var obj1 = {
name: 'obj1',
foo: foo
};
var obj2 = {
name: 'obj2'
};
foo(); // widnow
obj1.foo(); // obj1
obj1.foo.call(obj2); // obj2
var bar = foo.bind(obj2);
bar(); // obj2
var bar1 = new bar(); // foo
箭头函数的 this
箭头函数的 this
是不进行绑定的,即就是 通过call、apply、bind
是不能改变其 this
指向的
箭头函数的 this
是由定义时外围最接近一层的非箭头函数的 this
来决定的,也就是平常所说的箭头函数的this
是从上层作用域中去找的,一直找不到的话即为window
var name = 'window';
var obj1 = {
name: 'obj1',
foo() {
return () => {
console.log(this.name);
}
}
};
var obj2 = {
name: 'obj2'
};
obj1.foo()(); // obj1
// 箭头函数的 this 是不能进行绑定的,它的 this 只会从上层作用域中去查找,所以给它进行绑定 this 是无效的
obj1.foo().call(obj2); //obj1
// 这里改变的是箭头函数上层作用域中的 this,也就是 foo 的 this,所以此时的 this指向的才是 obj2
obj1.foo.call(obj2)(); // obj2
关于 this 的几道题
自己做完,在控制台输出对答案就行了~~~
第一道
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss();
person.sayName();
(person.sayName)();
(b = person.sayName)();
}
sayName();
第二道
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
person1.foo1();
person1.foo1.call(person2);
person1.foo2();
person1.foo2.call(person2);
person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);
person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);
第三道
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1()
person1.foo1.call(person2)
person1.foo2()
person1.foo2.call(person2)
person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)
person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)
第四道
var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)
person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)
补充与总结
调用方式 | this 指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属的对象 |
事件绑定方法 | 绑定事件的对象 |
定时器函数 | window |
立即执行函数 | window |
箭头函数 | 上层作用域中去查找,直至到 window |