函数预编译过程 this->window
全局作用域 this->window
call/apply 改变函数运行时this指向
obj.func() func里面的this指向obj
函数执行,this指向才有意义,
每一次函数执行,就会产生一个this指向
默认绑定规则 函数的独立调用 this指向window
隐式绑定规则 作为对象方法调用,谁调用就指向谁;
显式绑定 call,apply,bind 改变this指向
new 绑定 指向实例化对象
箭头函数没有自己的this,可以拿父亲作用域中的this。它会捕获其所在(即定义的位置)的上下文的this值,作为自己的this值。
一、在独立的函数中使用 this 指代全局对象
在严格模式下,this指向undefined
非严格模式下,this指向window
function test(){
this.x = 1
console.log(this.x)
}
test(); // 1 this--->window
// 立即执行函数this都指向window
(function(){
console.log(this); // window
})();
二、作为对象方法调用,this 指代上级对象
谁调用了这个方法,this就指向谁
var a=0;
var obj = {
a:2,
func1 : function() {
console.log(this);
function test(){
console.log(this);// window
}
test();
}
}
obj.func1() // this--->obj
var a=0;
var obj = {
a:2,
func1 : function() {
console.log(this); // obj
function test(){
console.log(this); // window
}
return test;
}
}
obj.func1()()
// 相当于 obj.func1()-->test test()
var a=0;
function foo(){
console.log(this)
}
var obj={
a:2,
foo:foo
}
obj.foo() // obj
var bar = obj.foo;
bar()// window
// obj.foo 还没有执行 obj.foo=foo-->bar=foo-->bar执行
// 当方法被赋值的时候,会存在隐式丢失(变量赋值)
var a=0;
function foo(){
console.log(this);
}
function bar(fn){
fn();
}
var obj ={
a:2,
foo:foo
}
bar(obj.foo) // window
// fn-->obj.foo-->foo,fn是独立调用,所以foo是独立调用执行的
// 父函数有能力决定子函数的this指向
// 参数赋值
三、通过call,apply,bind
四、构造函数
- 在函数体最前面隐式的加上this = {}
- 执行this.xxx = xxx
- 隐式的返回this
function Person(){
var this={};
this.a=1;
return this; // this指的是函数实例化之后的结果
//return {}; // 值为引用值,会改变当前this指向,就不再指向实例化对象
}
var person=new Person()
五、箭头函数
箭头函数的this指向是由外层函数的作用域决定的。
var a=0;
function foo(){
var that=this;
console.log(this); // obj
var test=()=>{
console.log(this); // obj
}
test();
}
var obj={
a:1,
foo:foo
}
obj.foo();
默认绑定规则(独立调用)对箭头函数无效
var a=0;
function foo(){
console.log(this); // obj
var test=()=>{
console.log(this); // obj
}
return test;
}
var obj1={
a:1,
foo:foo
}
var obj2={
a:2,
foo:foo
}
obj1.foo()();
隐式绑定规则,无效
var obj1={
a:1,
foo:()=>{
console.log(this); // window
}
}
obj1.foo();
显式绑定,改变this指向无效
var a=0;
function foo(){
console.log(this); // window
var test=()=>{
console.log(this); // window
}
return test;
}
var obj1={
a:1,
foo:foo
}
var obj2={
a:2,
foo:foo
}
foo().call(obj2);
new 不能实例化箭头函数
var foo=()=>{
console.log(this); // 出错
}
new foo();
this的优先级
显式绑定优先级高于隐式绑定
function foo(){
console.log(this.a);
}
var obj1 ={
a:1,
foo:foo
}
var obj2 ={
a:2,
foo:foo
}
obj1.foo(); // 1
obj2.foo(); // 2
obj1.foo.call(obj2); // 2
obj.foo.call(obj1); // 1
new绑定优先级高于显式绑定
function foo(b){
this.a=b;
}
var obj1={};
var bar=foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
var baz=new bar(3); // new 把this指向为baz
console.log(obj1.a); // 2
console.log(baz.a); // 3
常见面试题
var name='222';
var a={
name:'111',
say:function(){
console.log(this.name);
}
}
var fun=a.say;//a.say代表这个function的函数引用
fun();//fun在全局范围内执行 ① this---> window 222
a.say();//② this--->a 111
var b = {
name:'333',
say:function(fun){
//这个方法里面的this--->b
fun();
}
}
b.say(a.say);//③ this--->window 222
b.say=a.say;//④
b.say();// this--->b 333
① 相当于function(){console.log(this.name);}
放到全局作用域执行,跟谁调用它没关系,此时this指向window,打印222
②a调用say方法执行,谁调用this就指向谁,this指向a,打印111
③a.say相当于一个参数传进去了,
b.say(function(){
console.log(this.name);
})
b.say调用 this指向b,
参数在这个fun()里面执行,谁都没调用fun(),只是fun在b的say里面正常执行,空执行相当于走的预编译环节,(谁.fun(),这样fun里面的this才是谁) 所以this—>window,并没有说是this.fun();
var b = {
name:'333',
say:function(fun){
function(){
console.log(this.name);
}
}
}
④相当于
var b = {
name:'333',
say:function(){
console.log(this.name);
}
}
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); // 2
obj1.fn3.call(obj2)(); // window
obj1.fn4()(); // 1
// 箭头函数执行,找父作用域fn4 obj1调用fn4
obj1.fn4().call(obj2); // 1
// obj1.fn4() 是箭头函数,箭头函数的call 不改变this指向,
obj1.fn4.call(obj2)(); // 2
// obj1.fn4.call(obj2) 父作用域中的this指向了obj2
function Foo(){
getName=function(){
alert(1);
}
return this;
}
Foo.getName=function(){
alert(2);
}
Foo.prototype.getName=function(){
alert(3);
}
var getName=function(){
alert(4);
}
function getName(){
alert(5);
}
// 构造函数上的getName
Foo.getName(); // 2
// 全局上的getName被覆盖了
getName(); // 4
// Foo()执行后的getName被重新重写
Foo().getName(); // 1
// Foo执行完成被重写
getName(); // 1
new Foo.getName(); // 2
// new Foo()
new Foo().getName(); // 3
new new Foo().getName(); // 3