资讯专栏INFORMATION COLUMN

Javascript的this理解

王笑朝 / 1073人阅读

摘要:一默认绑定默认绑定默认绑定是指在非严格模式下,且没有使用别的绑定规则时,根据函数调用调用栈的上下文来绑定对象全局对象。把函数传入语言内置函数。使用时尽量避免混用,否则会造成难以维护的后果。

在理解javascript的this之前,首先先了解一下作用域。

作用域分为两种:

1、词法作用域:引擎在当前作用域或者嵌套的子作用域查找具有名称标识符的变量。(引擎如何查找和在哪查找。定义过程发生在代码书写阶段)
2、动态作用域:在运行时被动态确定的作用域。

词法作用域和动态作用域的区别是:词法作用域是在写代码或定义时确定的;动态作用域是在运行时确定的。

this的绑定规则

this是在调用时被绑定,取决于函数的调用位置。由此可以知道,一般情况下(非严格模式下),this都会根据函数调用(调用栈)的上下文来绑定对象。

一、默认绑定

默认绑定:默认绑定是指在非严格模式下,且没有使用别的绑定规则时,this根据函数调用(调用栈)的上下文来绑定对象(全局对象)。(严格模式下则绑定undefined)

举个栗子:

function foo() {
    console.log(this.a);
};
function bar() {
    var a = 3;
    foo();
}
var a = 2;
bar();  //调用栈在全局作用域,this绑定全局对象

运行结果为: 2
//加上"use strict"运行结果则会变成this is undefined

这里的函数调用时,使用了默认绑定,函数调用(调用栈)的上下文是全局作用域,因此this绑定了全局对象(global)。

eg2:
function foo() {
    console.log(this.a)
};
var a = 2;
(function() {
    "use strict"
    foo();
})();

运行结果为: 2

这里需要注意:对于默认绑定,决定this绑定对象的不是调用位置是否处于严格模式,而是函数体是否处于严格模式(函数体处于严格模式则this绑定undefined;否则this绑定全局对象)。另外:严格模式和非严格模式虽然有可能可以绑定,但是最好不混用。

间接引用一般也是会应用默认绑定规则。

eg:
function foo() {
    console.log(this.a);
};
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo();   //3
(p.foo = o.foo)();  //2

赋值表达式 p.foo = o.foo的返回值是直接引用目标函数foo。

二、隐式绑定

隐式绑定:由上下文对象调用,绑定到上下文对象。

举个栗子:

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

这段代码中,foo()被当做引用属性添加到obj对象中,obj调用这个引用属性函数时,会使用该引用属性上下文,this会被绑定到obj对象。(这个函数严格来说不属于obj对象,只是作为引用属性)。属于隐式绑定。

而下面foo()函数的直接执行,并不是obj对象引用,所以上下文对象是全局对象。故this绑定了undefined。属于默认绑定。

对象引用链中只有上一层或者说最后一层在调用位置中起作用。

注意:

隐式绑定的函数会丢失绑定对象。此时它会应用默认绑定,将this绑定到全局对象或者undefined上,取决于是否是严格模式。
eg:

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

运行结果:"biubiubiu"

解析:看似bar是obj.foo的一个引用,实际上bar是直接引用了函数foo,是一个单纯的函数调用,故实为默认绑定。

参数传递就是隐式赋值,因此传入函数时也会被隐式赋值。
eg:

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

运行结果: "biubiubiu"

解析:实际上参数也是隐式赋值,但是参数传入函数中,并在函数中执行。此时也是直接引用了函数foo,因此也是单纯的函数调用,采用了默认绑定。

把函数传入语言内置函数。(与上面情况基本相似,将自己声明函数改成语言内置函数)回调函数丢失this的情况比较常见,况且还有调用回调函数的函数可能还会修改this。

三、显式绑定

显式绑定:直接将this绑定到指定对象上。Javascript中绝大多数函数和自己所创建的函数都可以使用这两种显式绑定的方法。

1、.call()
2、.apply()
这两种绑定方法,第一个参数是this绑定的对象。(如果传入的参数是原始值(字符串类型、布尔类型、数字类型),这个原始值就会被转成对象形式(new String、new Boolean、new Number)这个称为:装箱)

举个栗子:

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

运行结果: 2

然鹅,显示绑定并不能解决绑定丢失的问题。这个时候来了一位新朋友 -- 硬绑定(bind)。

3、.bind() (硬绑定是常见场景,故es5提供了该内置方法 Function.prototype.bind。)
bind()会返回一个新编码函数,把this绑定在指定参数上,并调用函数。

举个栗子:

function foo(e) {
    console.log(this.a + e);
    return this.a + e;
};
var obj = {
    a: 2
}
var bar = foo.bind(obj); //新编码函数
var b = bar(3); // 2 3
console.log(b); // 5

bind()还有一个功能将除了第一个用于绑定this的参数之外的其他参数传给下层的函数部分应用,是“柯里化”的一种)。

这里涉及到一个概念:把null或者undefined作为this的绑定对象传入call、apply、bind,这些值在调用的时候会被忽略,实际应用默认绑定规则。
应用场景:
1、使用apply()展开一个数组,并作为参数传递给一个函数。
2、bind()对参数进行柯里化(预先设置一些参数)。

举个栗子:

function foo(a,b) {
    console.log("a:" + a + ",b:" + b);
};
//数组“展开”成参数
foo.apply(null,[2,3]);  //a:2,b:3
//bind()柯里化
var bar = foo.bind(null,2);
bar(3);  //a:2,b:3

解析:传入一个参数作为this绑定对象,如果不传则使用占位符(null),此时会使用默认绑定规则。

上面这个例子可能会产生一定的副作用,如果需要运用这种场景并且更加安全。可以创建一个空对象(可以用任意喜欢的名字来命名)。

var ∅ = Object.create(null);
//上面这个例子就可以改写为:
foo.apply(∅,[2,3]); //a:2,b:3
var bar = foo.bind(∅,2);
bar(3);  //a:2,b:3

注意:硬绑定之后不能使用隐式绑定和显式绑定对this进行修改
在这里介绍一种软绑定的方法softBind()检查this绑定到全局对象或者undefined后,绑定this到指定的默认对象。绑定后效果和硬绑定一样,但是保留隐式绑定或者显式绑定修改this的能力。

四、new绑定

Javascript中的new机制与面向类语言的完全不同。在Javascript中,构造函数只是一些使用new操作符时被调用的函数,不属于一个类,也不会实例化一个类。称为对函数的“构造调用”。

举个栗子:

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

使用new的过程会创建一个全新的对象,this会绑定这个新对象。如果函数没有返回其他对象,则new表达式函数调用会返回该新对象。(这个新对象会连接prototype)

四种绑定规则的优先级为:new>显式>隐式>默认 箭头函数

箭头函数是根据外层作用域(函数或全局)来决定this。(词法作用域取代this机制)
箭头函数this会绑定调用时的对象,且箭头函数的绑定无法修改(new也不行)。

其实可以理解为,箭头函数的this在词法上继承的是它所在的作用域(函数或全局)的this,而它继承的函数作用域的this绑定的是在该函数调用上下文对象,所以箭头函数的this间接的绑定在调用上下文对象。

简述: 箭头函数this(绑定作用域this)-- 作用域this(绑定在调用上下文对象)。

故:箭头函数this == 调用的上下文对象

举个栗子:

function foo() {
    setTimeout(function() {
        //这里的this在词法上继承自foo()
        console.log(this.a);
    },100);
};
var obj = { a: 2 };
foo.call(obj);  //2

其实这个栗子也等价于:

function foo() {
    var that = this;  //lexical capture of this
    setTimeout(function() {
        console.log(self.a)
    },100);
}
...与上面一样

所以,有两种风格:this风格(四种规则)词法作用域风格(that = this和箭头函数)可供使用。使用时尽量避免混用,否则会造成难以维护的后果。

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

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

相关文章

  • 我对JavaScriptthis一些理解

    摘要:匿名函数的执行环境具有全局性,因此它的对象通常指向。如果对此有疑惑,可以看知乎上的答案知乎匿名函数的指向为什么是作为对象方法的调用,指向该对象当函数作为某个对象的方法调用时,就指这个函数所在的对象。 因为日常工作中经常使用到this,而且在JavaScript中this的指向问题也很容易让人混淆一部分知识。 这段时间翻阅了一些书籍也查阅了网上一些资料然后结合自己的经验,为了能让自...

    focusj 评论0 收藏0
  • 理解 JavaScript this

    摘要:回调函数在回调函数中的指向也会发生变化。在闭包回调函数赋值等场景下我们都可以利用来改变的指向,以达到我们的预期。文章参考系列文章理解闭包理解执行栈理解作用域理解数据类型与变量原文发布在我的公众号,点击查看。 这是本系列的第 5 篇文章。 还记得上一篇文章中的闭包吗?点击查看文章 理解 JavaScript 闭包 。 在聊 this 之前,先来复习一下闭包: var name = Nei...

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

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

    ghnor 评论0 收藏0
  • 加深对 JavaScript This 理解

    摘要:使用来调用函数,会自动执行下面操作创建一个全新的对象。所以如果是一个函数的话,会是这样子的创建一个新对象连接新对象与函数的原型执行函数,改变指向新的对象所以,在使用来调用函数时候,我们会构造一个新对象并把它绑定到函数调用中的上。 欢迎来我的博客阅读:《加深对 JavaScript This 的理解》 我相信你已经看过很多关于 JavaScript 的 this 的谈论了,既然你点进来...

    PiscesYE 评论0 收藏0
  • 理解 JavaScript call()/apply()/bind()

    摘要:理解文章中已经比较全面的分析了在中的指向问题,用一句话来总结就是的指向一定是在执行时决定的,指向被调用函数的对象。与和直接执行原函数不同的是,返回的是一个新函数。这个新函数包裹了原函数,并且绑定了的指向为传入的。 理解 JavaScript this 文章中已经比较全面的分析了 this 在 JavaScript 中的指向问题,用一句话来总结就是:this 的指向一定是在执行时决定的,...

    duan199226 评论0 收藏0
  • javascriptthis理解

    摘要:的关键字总是让人捉摸不透,关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用,因为函数的调用场景不同,的指向也不同。其实只要理解语言的特性就很好理解。个人对中的关键字的理解如上,如有不正,望指正,谢谢。 javascript的this关键字总是让人捉摸不透,this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用,因为函数的调用场景不同,this的指向也不...

    jimhs 评论0 收藏0

发表评论

0条评论

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