资讯专栏INFORMATION COLUMN

JavaScript || this

ShowerSun / 2660人阅读

摘要:是中的一个属性解析函数调用位置函数在程序代码中被调用的位置,清楚调用位置才能明确的指向确定函数的调用位置最关键是分析调用栈为达到当前指向位置所调用的所有函数。

关于this

this是JavaScript的一个关键字,自动定义在所有函数中,难点在于this的指向。

this的指向在函数调用时进行绑定,它的context取决于函数调用时的各种条件,与函数定义位置无关

1 this的作用

this可以使不同的context对象重复使用已经存在、声明的函数,无需针对每个对象编写不同版本的函数

function identify() {
    return this.name.toUpperCase();
}
function speak() {
    var greeting = "Hello, i"m " + identify.call( this );    
    console.log( greeting );
}

var me = {name: "Kyle"};
var you = {name: "Reader"};

identify.call( me );         // KYLE
identify.call( you );       // READER

speak.call( me );              // "Hello, i"m KYLE"
speak.call( you );              // "Hello, i"m READER"
2 误解

this并不是指向函数本身

在任何情况下,this都不指向函数的词法作用域

3 this是什么?

this是在函数被调用时发生绑定,其指向取决于函数调用的位置。

当一个函数被调用是,会创建一个执行上下文(context)。其中包含函数的调用位置(调用栈)、函数的调用方式和传入的参数等信息。thiscontext中的一个属性

4 this解析 4.1 函数调用位置

函数在程序代码中被调用的位置,清楚调用位置才能明确this的指向

确定函数的调用位置:最关键是分析调用栈(为达到当前指向位置所调用的所有函数)。分析调用栈,可以得出真正的调用位置

function baz() {
    // 当前调用栈是:baz
    // 所以当前调用位置时全局作用域

    console.log("baz");
    bar(); // <-- bar的调用位置
}

function bar() {
    // 当前调用栈是:baz --> bar
    // bar的调用位置在baz中

    console.log("bar");
    foo(); // <-- bar的调用位置
}

function foo() {
    // 当前调用栈是:baz --> bar --> foo
    // foo的调用位置在bar中

    console.log("foo");
}
baz();   //  --> baz的调用位置
4.2 this的绑定规则

在分析清楚调用位置后,根据this绑定的四条规则决定绑定对象。四条规则分别对应四种不同的函数调用方式

总共有四条绑定规则,其优先级是:默认绑定 < 隐式绑定 < 显式绑定 < new绑定

默认绑定:作为独立调用的函数

隐式绑定:作为对象的方法调用的函数

显式绑定(硬绑定):使用call()apply()bind()方法,强制将对象绑定到函数的this

new绑定:

4.2.1 默认绑定

默认绑定指将函数作为独立的函数来调用,默认绑定将this绑定到全局对象

分析隐式绑定时,一个对象内部包含一个指向函数的属性,并且通过对象的属性间接引用函数,将this间接绑定到该对象上

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

a在全局作用域中声明,是全局对象的一个属性;

foo()使用不带任何修饰的函数进行调用,只能使用默认绑定规则;此时,非严格模式下this指向全局对象,所有this.a被解析为全局变量a

在严格模式中,this不能绑定到全局对象,this只能绑定到undefined

function foo() {
    "use strict";
    console.log(this.a);
}
var a = 2;
foo();  // TypeError: this is undefined

4.2.2 隐式绑定

判断函数的调用位置是否有上下文对象,隐式绑定将this绑定到调用方法的上下文对象上。

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo();  // 2   foo()的调用位置包含上下文对象obj,this隐式绑定到obj对象

对象属性引用链中,只有最后一层会影响调用位置

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    obj2: obj2
};

var obj2 = {
    a: 42,
    foo: foo

obj.obj2.foo();  // 42   实际是通过obj对象的obj2属性对象来调用foo()函数,this指向obj2

隐式丢失

被隐式绑定的函数会丢失绑定对象,然后应用默认绑定,非严格模式下将this绑定到全局对象。

隐式绑定丢失发生在将隐式绑定的函数赋值给另外的变量,通过改变了来调用函数

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo;   //  函数别名,传递引用
var  a = "global a";
bar();  // "global a",函数的调用位置,bar()其实是一个不带任何修饰的函数调用,所以应用默认的绑定规则

在函数中将回调函数作为参数传入时,参数传递是一种隐式赋值(传递引用),所以应用默认绑定规则

function foo() {
    console.log(this.a);
}
function doFoo(fn) {
    // fn是obj.foo函数本身的一个引用

    fn();   // fn的调用位置
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo;   //  函数别名,传递引用
var  a = "global a";
doFoo(obj.foo);  // "global a",传入的函数被隐式赋值,应用默认绑定规则

setTimeout(obj.foo, 100);   //"global a",使用语言本身的内置函数时道理相同

4.2.3 显式绑定

在JavaScript中,函数是对象,每个函数对象都定义有call()apply()bind()方法,bind()在ES5中。

call()apply()方法:

第一个方法是一个对象,将该对象绑定到this

可以直接指定绑定的对象,称为显示绑定

call()apply()区别在于其他参数

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
foo.call(obj);   // 2  通过foo.call(obj);,在调用foo()时强制将其this绑定到obj对象

显式绑定仍然会有绑定丢失问题:可以使用显示绑定的一个变形来解决这个问题;

硬绑定

创建一个函数bar(),在内部调用foo.call(obj),强制将foothis绑定到obj对象上,无论怎样调用bar()函数,都会手动在obj对象上调用foo,因此foothis指向不会改变

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
};
var bar = function() {
    foo.call(obj);   
}

bar();   //  2
setTimeout( bar, 100 ); // 2
// 硬绑定的bar不能再修改它的this指向
bar.call(window);   // 2

硬绑定的应用场景

创建一个包裹函数,传入所有的参数,并返回接收到的所有值

function foo(sth) {
    return this.a + sth;
}
var obj = {
    a: 2
};
var bar = function() {
    // 将arguments传入需要调用的函数
    return foo.apply(obj, arguments);   
}

bar(3);   //  2 + 3 = 5

创建一个可以重复使用的函数

function foo(sth) {
    return this.a + sth;
}
var obj = {
    a: 2
};
// 简单的辅助绑定函数
function bind(fn, obj) {
    return function() {
        return fn.apply(obj, arguments);
    }
}

var bar = bind(foo, obj);
bar(3);   //  2 + 3 = 5

硬绑定是一种非常常见的模式,ES5提供内置Function.prototype.bind方法:返回一个硬绑定的新函数,bind(obj)将参数obj设置为this的上下文,并调用原始函数。

function foo(sth) {
    return this.a + sth;
}
var obj = {
    a: 2
};

var bar = foo.bind(obj);
bar(3);   //  2 + 3 = 5

4.2.4 new绑定

JavaScript中的new机制与传统面向对象语言不同。JavaScript中构造函数只是使用new操作符调用的函数,使用new操作符调用函数时:

创建一个全新对象;

新对象被执行__proto__链接

新创建的对象被绑定到函数调用时的this

如果函数没有返回其他对象,new表达式中的函数调用自动返回新创建的对象

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

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

4.3 优先级

判断this的指向:找到函数的调用位置,并根据优先级判断应用的规则,默认绑定的优先级最低

显示绑定的优先级高于隐式绑定:在判断时优先考虑显示绑定

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

var obj1 = {
    a: 2;
    foo: foo
};
var objb = {
    a: 4;
    foo: foo
};

obj1.foo();  // 2
obj2.foo();  // 4

obj1.foo.call(obj2);   // 4
obj2.foo.call(obj1);   // 2

new绑定的优先级高于隐式绑定:

new绑定的优先级的高于显示绑定:

bar被硬绑定到obj1对象上,但是new bar(3)并未将obj1.a修改为4;

new bar(3)修改了调用bar()中的this,得到一个新对象

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

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

4.4 根据优先级判断函数调用位置应用的规则

函数是否在new中调用?如果是,this绑定新创建的对象

函数是否通过call()apply()显示绑定?或者bind()硬绑定?如果是,this指向绑定的对象。

函数是否在某个上下文对象中调用?如果是,this指向那个上下文对象

如果不是上述三种情况,使用默认绑定。严格模式下绑定到undefined,非严格模式下绑定到全局对象

4.5 绑定例外 4.5.1 被忽略的this

nullundefined作为this的绑定对象传入call()apply()bind()方法,在调用时被忽略,实际应用默认绑定规则。

使用null来忽略this绑定可能产生副作用:安全的做法是传入一个特殊对象,将this绑定到这个对象不会产生任何副作用。Object.create(null)

5 this词法

ES6中的箭头函数不能使用上述4种规则,而是根据外层(函数或全局)作用域来绝对this。箭头函数常用于回调函数中。

function foo() {
    // 返回一个箭头函数
    return (a) => {
        // this继承自foo()
        console.log(this.a);
    }
}
var obj = {
    a: 2
};

var obj2 = {
    a: 42
};

var bar = foo.call(obj1);  
bar.call(obj2);   // 2, 不是42

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86855.html

相关文章

  • JavaScript深入浅出

    摘要:理解的函数基础要搞好深入浅出原型使用原型模型,虽然这经常被当作缺点提及,但是只要善于运用,其实基于原型的继承模型比传统的类继承还要强大。中文指南基本操作指南二继续熟悉的几对方法,包括,,。商业转载请联系作者获得授权,非商业转载请注明出处。 怎样使用 this 因为本人属于伪前端,因此文中只看懂了 8 成左右,希望能够给大家带来帮助....(据说是阿里的前端妹子写的) this 的值到底...

    blair 评论0 收藏0
  • javascript技术难点(三)之this、new、apply和call详解

    摘要:第四点也要着重讲下,记住构造函数被操作,要让正常作用最好不能在构造函数里 4) this、new、call和apply的相关问题 讲解this指针的原理是个很复杂的问题,如果我们从javascript里this的实现机制来说明this,很多朋友可能会越来越糊涂,因此本篇打算换一个思路从应用的角度来讲解this指针,从这个角度理解this指针更加有现实意义。 下面我们看看在ja...

    ghnor 评论0 收藏0
  • JavaScript进阶之’this

    摘要:所以相同点是,在全局范围内,全局变量终究是属于老大的。只生效一次引入了。只生效一次在箭头函数中,与封闭词法环境的保持一致。我通常把这些原始函数叫做构造函数。在里面你可以嵌套函数,也就是你可以在函数里面定义函数。 showImg(https://img-blog.csdnimg.cn/20190522000008399.jpg?x-oss-process=image/watermark,...

    shenhualong 评论0 收藏0
  • Javascript 深入浅出This

    摘要:中函数的调用有以下几种方式作为对象方法调用,作为函数调用,作为构造函数调用,和使用或调用。作为构造函数调用中的构造函数也很特殊,构造函数,其实就是通过这个函数生成一个新对象,这时候的就会指向这个新对象如果不使用调用,则和普通函数一样。 this 是 JavaScript 比较特殊的关键字,本文将深入浅出的分析其在不同情况下的含义,可以这样说,正确掌握了 JavaScript 中的 th...

    Y3G 评论0 收藏0
  • 学习React之前你需要知道的的JavaScript基础知识

    摘要:和类在开始时遇到类组件,只是需要有关类的基础。毕竟,中的条件呈现仅再次显示大多数是而不是特定的任何内容。 在我的研讨会期间,更多的材料是关于JavaScript而不是React。其中大部分归结为JavaScript ES6以及功能和语法,但也包括三元运算符,语言中的简写版本,此对象,JavaScript内置函数(map,reduce,filter)或更常识性的概念,如:可组合性,可重用...

    bitkylin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<