一、this的指向
- 默认绑定:指向window,独立调用也指向window
1.1. 默认指向window
<script>
console.log(this === window); //true
</script>
1.2. 作为普通函数调用,this指向window
// 函数的独立调用 this默认指向window
function test() {
console.log(this === window); //true
}
test();在这里插入代码片
- 隐式绑定:作为对象方法被调用时,谁调用就指向谁
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
foo: foo
}
// 隐式丢失:
var bar = obj1.foo; //foo函数并没有执行 他是先赋值再执行的,所以是独立调用的,指向window
bar(); //window
foo(); //window
// 隐式绑定(对象调用)
obj1.foo(); //2
obj2.foo(); //3
// 显示绑定
obj1.foo.call(obj2); //3
obj2.foo.call(obj1); //2
// ------------显示绑定 > 隐式绑定
- 显示绑定:利用call、apply、bind更改this指向
var a = 0;
function foo(a, b, c, d, e) {
console.log(a, b, c, d, e);
console.log(this);
}
var obj = {
a: 2,
foo: foo
}
obj.foo(); //undefined undefined undefined undefined 指向obj
var bar = obj.foo
bar(); //undefined undefined undefined undefined 指向window
obj.foo(1, 2, 3, 4, 5); //1 2 3 4 5 指向obj
bar.call(obj, 1, 2, 3, 4, 5); //1 2 3 4 5 指向obj
bar.call(1, 2, 3, 4, 5); //2 3 4 5 undefined 指向Number
bar.call(false, 2, 3, 4, 5); //2 3 4 5 undefined 指向Boolean
bar.call(undefined, 2, 3, 4, 5); //2 3 4 5 undefined 指向window
bar.apply(null, [1, 2, 3, 4, 5]); //1 2 3 4 5 指向window
bar.bind(obj)(1, 2, 3, 4, 5) //1 2 3 4 5 指向obj
- new绑定:当作为构造函数被调用时,new foo()指向实例之后的对象
new在构造函数中做了哪些事?
1、在内存中创建一个新的对象
2、让this指向这个新对象
3、在构造函数里面的代码,给这个新对象添加属性和方法
4、返回这个新对象,所以构造函数里面一般不需要return
构造函数里面的this
指向new创建的实例化对象(没有return的情况下);
如果构造函数内出现了return 并且是一个object对象,那么最终的运算结果返回这个对象;
只要构造函数不返回数据或者返回基本数据类型,this仍然指向实例。
// //new 注重返回值!
function Person() {
var a11 = {};
a11.a = 1;
return a11;// {a:1}
// return 1;//Person{}
// return 的值为引用值
// return {}; //{}
}
var person = new Person();
console.log(person);
// new是用来实例化构造函数的
// 构造函数与函数的区别 构造函数需要new
// bar和foo是构造函数 baz是new出来的实例对象
// new一个函数,生成一个实例化对象,这种函数叫构造函数
function foo(b) {
this.a = b;
console.log(this);
}
var obj1 = {};
var bar = foo.bind(obj1); //bind 改变this指向,使得this指向obj1 相当于obj1.a
bar(2); //this -> {a:2}
console.log(obj1.a); //2
// baz就是实例对象
var baz = new bar(3); // new一个对象,使得this指向baz;此时是baz.a this -> foo{a:3}
console.log(obj1.a); //2
console.log(baz.a); //3
// ------------new绑定 > 显示绑定
二、 补充
- 箭头函数
箭头函数内部并没有this指向,箭头函数的this指向是由外层函数的作用域来决定的
var a = 0;
function foo() {
var that = this;
console.log(this); //this指向obj {a:1 , foo:f}
//箭头函数
var test = () => {
console.log(this); //this指向obj {a:1 , foo:f}
}
return test;
}
var obj1 = {
a: 1,
foo: foo
}
var obj2 = {
a: 2,
foo: foo
}
// obj1.foo() //返回test 是个函数 指向obj1
// 箭头函数当中没有arguments对象
obj1.foo()(); //默认绑定规则(独立调用)对箭头函数无效 //上面两个this都指向obj
var bar = foo().call(obj2) //上面两个this都指向window 显示绑定 无效
- 定时器函数、立即执行函数等
var a = 0;
function foo() {
console.log(this); //window
}
function bar(fn) {
console.log(this); //window
fn(obj); //window
new fn() //foo{}
fn.call(obj) //obj {a: 2, foo: ƒ}
}
// api 接口中指明的
// 回调函数; 父函数; 子函数;
// 回调函数:当函数是参数时,那么我们认为当前的这个参数为回调函数
var arr = [1, 2, 3]
arr.forEach(function(item, idx, arr) {
console.log(this); //window
// 这个参数是可选的
})
arr.sort(function(a, b) {
return a - b;
})
setInterval(function() {
console.log(this); //window
})
var obj = {
a: 2,
foo: foo,
}
// 预编译的过程中实参被赋值为形参 ——> (值的拷贝过程,浅拷贝)
bar(obj.foo)
// 父函数是有能力决定子函数的this指向的
三、大厂面试题
var name = 'window'
var obj1 = {
name: '1',
fn1: function() {
console.log(this.name)
},
fn2: () => console.log(this.name),
fn3: function() {
return function() {
console.log(this.name)
}
},
fn4: function() {
return () => {
console.log(this.name)
}
}
}
var obj2 = {
name: '2'
}
obj1.fn1(); // 1 (隐式绑定)
obj1.fn1.call(obj2); // 2 (显示绑定优先级大于隐式绑定)
obj1.fn2(); // window (箭头函数不绑定作用域,上层作用域是全局)
obj1.fn2.call(obj2); // window
obj1.fn3()(); // window (独立函数调用)
obj1.fn3.call(obj2)(); // window(独立函数调用)
obj1.fn3().call(obj2); // 2 (最终调用返回函数式, 使用的是显示绑定)
obj1.fn4()(); // 1 找父作用域为fn4的作用域是obj1 (箭头函数不绑定this, 上层作用域this是obj1)
obj1.fn4.call(obj2)(); // 2(上层作用域被显示的绑定了一个obj1)
obj1.fn4().call(obj2); // 1(上层找到obj1)
function Foo() {
getName = function() {
console.log(1);
}
return this;
console.log(this);
}
Foo.getName = function() {
console.log(2);
}
Foo.prototype.getName = function() {
console.log(3);
}
// 表达式
var getName = function() {
console.log(4);
}
// 函数声明 他会提升,预编译的时候会在前面,所以在执行时会替换成表达式的getName
function getName() {
console.log(5);
}
//
Foo.getName(); //2
getName(); //4
Foo().getName(); //1 (Foo执行完时候,getName全局变量,它进行重写)
getName(); //1 因为foo执行完成之后,getName重写了
new Foo.getName(); // 2 new的是Foo对象的一个属性
new Foo().getName(); // 3 new Foo()实例之后的对象
new new Foo().getName(); //3