欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于JavaScript中this的指向,你知晓几分?请速来围观!

程序员文章站 2022-06-03 09:39:14
恢复内容开始 一、this是什么东东? this是指包含它的函数作为方法被调用时所属的对象。这句话理解起来跟卵一样看不懂,但是如果你把它拆分开来变成这三句话后就好理解一点了。 1.包含它的函数 2.作为方法被调用时 3.所属的对象 二、this的绑定规则 1、默认绑定 2、隐式绑定 先看两段例子 下 ......

---恢复内容开始---

一、this是什么东东?

  this是指包含它的函数作为方法被调用时所属的对象。这句话理解起来跟卵一样看不懂,但是如果你把它拆分开来变成这三句话后就好理解一点了。

  1.包含它的函数

  2.作为方法被调用时

  3.所属的对象

二、this的绑定规则

  1、默认绑定  

var x = 0;
function num(){
    this.x = 1;
}
console.log(this.x);//0
num();
console.log(this.x);//1

//当没有使用let或者var时,声明的变量是全局变量,指向window,
//在这一形态下,其函数内部的this是与全局作用域时一样,直接指向window,执行完num()后,更改了x的值,所以其形态 依然等价于window。
      function foo(){
      console.log(this.a)     }     var a = 2;     foo(); //2     console.log(this.document === document); // true     // 在浏览器中,全局对象为 window 对象:     console.log(this === window); // true     this.a = 3;     console.log(window.a); // 3     this指向全局对象。在非严格模式中,当funcion被不带任何修饰的函数直接引用进行调用的,只能使用默认绑定,无法应用其他规则  

 

    

  2、隐式绑定

    先看两段例子   

function foo(){
    console.log(this.a)
}

var obj = {
    a:2,
    foo:foo
}

obj.foo() //2
//隐式绑定————调用位置使用obj上下文来引用函数,可以说函数被调用时obj对象“拥有”或者“包含”它,它就指向谁。
 

 

function foo(){
    console.log(this.a)
}

var obj2 = {
    a:42,
    foo:foo
}

var obj1 = {
   a:2,
   obj2:obj2,
   foo:foo }
obj1.foo() //2 obj1.obj2.foo() //42 //此时可以控制台查看obj1,obj2对象里究竟包含了什么
//当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象
//对象属性引用链中只有最后一层在调用位置中起作用

下面思考这一段会输出什么呢?

function foo(){
    console.log(this.a)
}

var obj = {
    a:2,
    foo:foo
}

var bar = obj.foo;  //这里bar将引用foo函数本身,所以不带有函数对象的上下文,this会直接指向window
bar() //?

//为什么没有隐式绑定?这种情况称为隐式丢失。

//因为bar=obj.foo 而obj.foo指向foo 也就是bar = function foo(){console.log(this.a)} foo中的this指向window,
//在window中并没有对a进行定义,so,结果是undefined

接下来再看一段会是什么结果呢?(参数传递时的隐式赋值)

function foo(){
    console.log(this.a)
}

function doback(fn){
    fn()
}

var obj = {
    a:2,
    foo:foo
}

var a = 'global';

doback(obj.foo) //?    显然答案是global,但是为什么呢?请继续往下看!

//隐式丢失--被隐式绑定的函数会丢失绑定对象然后应用默认绑定。
//最后函数执行 doback(obj.foo)时,会进行参数传递,也就是 fn = obj.foo,就和上一个例子一样了。既this指向的是window。

  

  3、显示绑定

function foo(){
    console.log(this.a)
}

var obj = {
    a:2
}
foo.call(obj) //2

//显式绑定--第一个参数是一个对象,接着在调用函数时将其绑定到this,通过foo.call(obj),我们可以在调用foo时强制把他的this绑定到obj上
function foo(){
    console.log(this.a)
}

var obj = {
    a:2
}

var a = '3333333';

var bar = function(){
    foo.call(obj)
}

bar() // 2
bar.call(window) //2

// 硬绑定后bar无论怎么调用,都不会影响foo函数的this绑定
// 通过创建函数bar(),并在内部调用foo.call(obj),强制把foo的this绑定到obj上。硬绑定的bar之后无论如何调用函数,都只会在obj上调用foo。

我们来看一下他的应用:
function foo(num) {
    console.log( this.a, num);
    return this.a + num;
}

var obj = {
    a: 2
};

var bar = function() {
    return foo.call( obj, ...arguments ); // 将obj对象硬编码进去
   //return foo.apply( obj, arguments ); // 也可以使用apply将obj对象硬编码进去
};

var b = bar( 3 ); // 2 3
console.log( b ); // 5

 


function fn1(){
   console.log(1);
}
function fn2(){
    console.log(2);
}

fn1.call(fn2);     //输出 1
 
fn1.call.call(fn2);  //输出 2  这个暂时没有搞懂,但是东鸽子大师中有讲解,感兴趣的小伙伴可以看看。

  

  4、new绑定

    我们不去深入了解构造函数,但要知道new来调用函数,或者说发生构造函数调用时,执行了哪些

    当代码 new foo(...) 执行时:

 

    (1) 创建一个新对象,它继承自foo.prototype.

    (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);new foo 等同于 new foo(),只能用在不传递任何参数的情况。

    (3) 执行构造函数中的代码(为这个新对象添加属性) ;

    (4) 返回新对象, 那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。

 

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

var bar = new foo(2);  //创建一个新对象bar,它继承了foo.prototype.  也就是bar这个对象有a这个属性,且传进去的是2,使用new来调用foo(..)时,会构造一个新对象,并把它绑定到foo(..)调用中的this上
console.log(bar.a) //2 

 

 

三、下面做一个小练习看看你学会了吗?

例一:

function foo(a){ console.log(this.a) } var obj1 = { a:2, foo:foo } var obj2 = { a:3, foo:foo } obj1.foo() //? obj2.foo() //? obj1.foo.call(obj2) //? obj2.foo.call(obj1) //?





答案:
obj1.foo() //2 隐式绑定
obj2.foo() //3 隐式绑定

obj1.foo.call(obj2) //3 显式绑定
obj2.foo.call(obj1) //2 显式绑定

通过答案得出:显示绑定 > 隐式绑定
显示绑定优先级更高,所以在判断时 应当 优先考虑 是否 存在 显示绑定

 

 

 

例二:

function foo(someting){ this.a = someting } var obj1 = { foo:foo } var obj2 = {}
obj1.foo(2)
var bar = new obj1.foo(4)
console.log(obj1.a)//? console.log(bar.a)//?







答案:
console.log(obj1.a) //2  
console.log(bar.a)  //4

通过答案得出: new绑定 > 隐式绑定
显示绑定优先级更高,所以在判断时 应当 优先考虑 是否 存在 new绑定

 

 

需要注意的是:new和call/apply无法一起使用,因此无法通过new foo.call(obj)来直接测试

function foo(someting){ this.a = someting } var obj1 = {} var bar = foo.bind(obj1) //不知道bind()方法的同学可以直接点击此处查看最骚的就是你同学贡献的详解。 bar(2) console.log(obj1.a)//? var baz = new bar(3) console.log(obj1.a)//? console.log(baz.a)//?






答案:2 2 3

通过答案得出:new绑定 > 显示绑定
new修改了硬绑定调用bar(..)中的this,因为使用了new绑定,得到了一个名字为baz的新对象,并且baz.a的值为3.
所以 new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

 

 

 

 

在此特别鸣谢同事jason大哥的share!!!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

---恢复内容结束---