this

2020-09-25  本文已影响0人  _daraDu

一、使用this的原因(why)

对于前端开发者来说,this关键字是JavaScript中最复杂的机制之一,不同的位置使用,指向是不一样的,所以它到底有什么作用呢?为什么要花大量的时间去研究他呢

function GetObj (name) {
    this.name = name
}
GetObj.prototype.showName = function (){
alert(this.name)  // this指代GetObj这个对象
}
let p1 = new GetObj('张三')
p1.showName()

二、了解this(what----this到底是什么)

1、对this的误解
function foo(num) {
   console.log('foo===', num)  // foo: 6 // foo: 7 // foo: 8 // foo: 9 
  // 记录 foo 被调用的次数 
  this.count++
}
foo.count = 0
for(var i = 0; i< 10; i++){
   if (i > 5) {         
      foo( i );    
   } 
}
 console.log( foo.count ); // 0 

this并没有指向自身的foo函数,而是指向了window。

function foo(){ 
    var a = 2; 
    this.bar(); 
} 
function bar() { 
    console.log(this); //window
    console.log(this.a); // undefined
} 
foo();

这个a作用域存在于foo函数中,所以在window这个作用域中找不到。但是由于var定义的a,在词法分析阶段会将a进行变量提升,所以window中会有一个a,但是没有值

2、与自然语言的对比

实际上js中的this和我们自然语言中的代词有类似性。比如英语中我们写"小明,你吃了么?"

注意上面的代词"小明",我们当然可以这样写:"小明,小明吃了么?" ,这种情况下我们没有使用this去重用代替小明。
主要和执行时候的上下文环境有关联

3、那this到底是什么呢?

this只跟函数的调用位置有关,是在函数被调用时发生绑定的;this的指向取决于函数在哪里被调用。

this指向的最终对象,跟调用位置以及应用的绑定规则有关

三、绑定规则(how--如何寻找函数的调用位置)

1、 默认绑定
function foo() {      
    console.log( this.a ); // this指向window
} 
var a = 2; 
 
foo(); // 2
function foo() {      
  "use strict";      
  console.log( this );  // undefined
  console.log( this.a );  // Uncaught TypeError: Cannot read property 'a' of undefined
} 
 "use strict";  
console.log( this ) // window
var a = 2; 
foo(); 
2、 隐式绑定
function foo() {      
    console.log( this.a );   // 指向obj
} 
var obj = {      
    a: 2,     
    foo: foo  //--->foo: function () {      console.log( this.a );  } 
}; 
obj.foo(); // 2
var obj = {
    y: function() {
        console.log(this === obj);   // true
        console.log(this);   // Object {y: function}
        fn();  // 嵌套的函数不是对象的方法,直接调用,所以this指向window

        function fn() {
            console.log(this === obj);   // false
            console.log(this);   // Window 全局对象
        }
    }
}

obj.y();  
function foo() {      
    console.log( this.a ); 
} 
var obj = {      
    a: 2,     
    foo: foo  
}; 
var bar = obj.foo; // bar是全局变量
var a = "oops, global"; // a 是全局对象的属性 
bar(); // "oops, global",bar() 其实是一个不带任何修饰的函数调用

2、将函数作为参数传入回调函数中

function foo() {      
    console.log( this.a ); 
} 
function doFoo(fn) {     // fn 其实引用的是 foo 
    fn(); // <-- 调用位置! 
} 
var obj = {     
    a: 2,     
    foo: foo  
}; 
var a = "oops, global"; // a 是全局对象的属性 
doFoo( obj.foo ); // "oops, global"
3、显式绑定
使用 call()和apply() 方法进行强制绑定
var x = 1;

var obj = {
  x: 2
}

function fn() {
    console.log(this);
    console.log(this.x);
}

fn.call(obj)
// Object {x: 2}
// 2
fn.apply(obj)     
// Object {x: 2}
// 2

fn.call()         
// Window 全局对象
// 1

fn.apply(null)    
// Window 全局对象
// 1

fn.call(undefined)    
// Window 全局对象
// 1
var x = 1
var obj = {
    x: 2
}

function sum (y, z) {
    console.log(this.x + y +z)
}
sum(3, 4) // 8
sum.call(obj, 3, 4) // 9
sum.apply(obj, [3, 4]) // 9
使用 bind() 方法进行强制绑定

调用 f.bind(someObject) 会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,新函数的 this 会永久的指向 bind 传入的第一个参数,无论这个函数是如何被调用的。

var x = 1
var obj1 = {
    x: 2
}
var obj2 = {
    x: 3
}

function fn () {
    console.log(this)
    console.log(this.x)
}
var a = fn.bind(obj1)
var b = a.bind(obj2)

fn() // window 1

a() // {x: 2} 2

b() // {x: 2} 2 

a.call(obj2) // {x: 2} 2

在上面的例子中,虽然我们尝试给函数 a 重新指定 this 的指向,但是它依旧指向第一次 bind 传入的对象,即使是使用 call 或 apply 方法也不能改变这一事实,即永久的指向 bind 传入的第一次参数。

4、new绑定

使用 new 关键字,通过构造函数生成一个实例对象。此时,this 便指向这个新对象

var x = 1;

function Fn() {
   this.x = 2;
    console.log(this);  // Fn {x: 2}
}

var obj = new Fn();   // obj和Fn(..)调用中的this进行绑定
console.log(obj.x)   // 2

绑定规则优先级

如果某个调用位置可以应用多条规则该怎么办?为了 解决这个问题就必须给这些规则设定优先级。
毫无疑问,默认绑定的优先级别最低。
隐式绑定和显示绑定谁的优先级别更高呢?

function fn(){
    console.log(this.a)
}
let obj1 = {
    a: 1,
    fn: fn
}
let obj2 = {
    a: 2,
    fn: fn
}
obj1.fn()//1
obj2.fn()//2
obj1.fn.call(obj2)//2
obj2.fn.apply(obj1)//1

隐式绑定和new()绑定谁的优先级别更高呢?

function foo(something) {      
    this.a = something; 
} 
var obj1 = {      
    foo: foo 
}; 
var obj2 = {}; 
obj1.foo( 2 );  
console.log( obj1.a ); // 2 

obj1.foo.call( obj2, 3 );  
console.log( obj2.a ); // 3 
 
var bar = new obj1.foo( 4 );  
console.log( obj1.a ); // 2  
console.log( bar.a ); // 4

但是 new 绑定和显式绑定谁的优先级更高呢

function foo(something) {      
    this.a = something; 
} 

var obj1 = {}; 

var bar = foo.bind( obj1 );  
bar( 2 ); 
console.log( obj1.a ); // 2 

var baz = new bar(3);  
console.log( obj1.a ); // 2  
console.log( baz.a ); // 3

bar 被硬绑定到 obj1 上,但是 new bar(3) 并没有像我们预计的那样把 obj1.a 修改为 3。相反,new 修改了硬绑定(到 obj1 的)调用 bar(..) 中的 this。因为使用了 new 绑定,我们得到了一个名字为 baz 的新对象,并且 baz.a 的值是 3。

当某个函数调用应用了这四种规则中的多条,那么优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

5、箭头函数中this指向

箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
箭头函数没有自己的this绑定。箭头函数中使用的this,其实是直接包含它的那个函数或函数表达式中的this

var a = 1;
let obj = {
    a: 2,
    fn1: () => {
        console.log('fn1',this.a) // 1 this指向window
        let fn3 = () => {
            console.log('fn3',this.a) // 1 this指向window
        }
        fn3()
        
        let fn4 = () => {
            console.log('fn4',this.a) // 1 this指向window
        }
        fn4.call(obj)
        // 由于箭头函数没有自己的this指针,通过 call() 或apply() 方法调用一个函数时,只能传递参数,他们的第一个参数会被忽略。
        fn4.apply(obj)
    },
    fn2: function() {
        console.log('fn2',this.a) // 2 this指向obj
    }
}
obj.fn1()
obj.fn2()

箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。- 从父作用域继承this
同 bind 一样,箭头函数也很“顽固”,无法通过 call 和 apply 来改变 this 的指向,即传入的第一个参数被忽略。

图片转自掘金小册-前端面试之道

image.png

总结

如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后 就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。
    一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑 定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null),以保护全局对象。
    ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这 其实和 ES6 之前代码中的 self = this 机制一样。

https://www.cnblogs.com/kidsitcn/p/10985338.html
https://zhuanlan.zhihu.com/p/71490991
https://zhuanlan.zhihu.com/p/28536635
嵌套函数指向

上一篇下一篇

猜你喜欢

热点阅读