资讯专栏INFORMATION COLUMN

JavaScript this 讲解

zhangwang / 2502人阅读

摘要:作为方法进行调用,该上下文是方法的拥有者作为全局函数调用,其上下文永远是也就是说,该函数是的一个方法作为构造器进行调用时,其上下文对象则是新创建的对象实例。

精确把握 JavaScript 中的 this

this 是 JavaScript 中的一个关键字,当一个函数被调用时,除了传入函数的显式参数以外,名为 this 的隐式参数也被传入了函数。this 参数指向了一个自动生成的内部对象,这个内部对象被称为函数上下文。与其他面向对象的语言不同的是, JavaScript 中的 this 依赖于函数的调用方式。所以,想要明白 this 的指向问题,还必须先研究函数在 JavaScript 中是如何被调用的。

调用方式 1、作为函数进行调用

这说法有点奇怪,函数当然是被作为函数进行调用的,但我们说一个函数“作为函数”被调用,只是为了区别于其他的调用方式。先看一个简单的例子:

function func1() {
    console.log(this === window); // true
}
func1();

var func2 = function () {
    console.log(this === window); // true
}
func2();

function func3() {
    "use strict";
    console.log(this); // undefined
}
func3();

上面例子中的第一第二个 this 在非严格模式下指向全局上下文,即 window 对象,第三个在严格模式下则为 undefined。

所以,当函数被作为函数进行调用时,在非严格模式下,函数的上下文是 window 对象,而在严格模式下,函数上下文为 undefined。

调用方式 2、作为方法进行调用

当一个函数被赋值给一个对象的一个属性,并使用引用该函数的这个属性进行调用函数时,那么函数就是作为该对象的一个方法进行调用的。看下面的例子:

var obj = {
    func: function() {
        console.log(this === obj); // true
    }
};
obj.func();

上面的例子中,函数 func 的调用对象为 obj,所以函数上下文便是 obj。由此可见,将函数作为对象的一个方法进行调用时,该对象就是函数上下文,并且在函数内部可以用 this 来访问这个对象。

此时,我们再看一下第一种调用方式,即“作为函数”调用。“作为函数”进行调用的函数是定义在 window 上的,调用时也不需要 window 的引用,其实方式 1 中例子中的 func1() 就是 window.func1(),所以例子中的函数上下文便是 this。

调用方式 3、作为构造器进行调用

将函数作为构造器时,函数的声明与其他调用方式的函数声明一致。将函数作为构造器调用的例子如下:

function Student() {
    this.getContext = function() {
        return this;
    }
}
var stu = new Student();
console.log(stu.getContext() === stu); // true

将函数作为构造器调用时,便会通过这个函数生成一个新对象,这时,this 指向这个新创建的对象。

从上面几种调用方式来看,函数调用方式之间的主要差异是:作为 this 参数传递给执行函数的上下文对象之间的区别。作为方法进行调用,该上下文是方法的拥有者;作为全局函数调用,其上下文永远是 window (也就是说,该函数是 window 的一个方法);作为构造器进行调用时,其上下文对象则是新创建的对象实例。

下面的一种调用方式可以显式地指定上下文。

调用方式 4、使用 apply() 和 call() 方法进行调用

JavaScript 的每个函数都有 apply() 和 call() 函数,可以利用任何一个函数都可以显式指定任何一个对象作为其函数上下文。

通过 apply() 方法来调用函数,我们要给 apply() 传入两个参数:一个作为函数上下文对象,另一个作为函数参数所组成的数组。call() 方法的使用方式类似,唯一不同的是给函数传入的参数是一个参数列表,而不是单个数组。

function func() {
    var result = 0;
    for(var i = 0; i < arguments.length; i++) {
        result += arguments[i];
    }
    this.result = result;
}
var obj1 = {};
var obj2 = {};
func.apply(obj1, [1, 2, 3]);
func.call(obj2, 4, 5, 6);

console.log(obj1.result === 6); // true
console.log(obj2.result === 15); // true

在上面的代码中,func.apply(obj1, [1, 2, 3]); 将函数的上下文定义为 obj1,并且传入 1、2、3 三个参数,func.call(obj2, 4, 5, 6); 将函数的上下文定义为 obj2,并且传入 4、5、6 三个参数。

那 apply 和 call 基本相同,那么我们该用哪一个呢?其实 apply 和 call 的区别仅仅在于调用时传入的参数不同,其他完全一样。所以,在选择时,主要看传入的参数。如果已知参数已经在数组里了则用 apply 即可,或者参数是动态生成的,可以把参数 push 进一个数组,然后再用 apply 调用。当参数数量已知,或者在参数里有很多无关的值则用 call 方法调用。

其他补充 利用 bind() 改变函数上下文

先看下面例子:

var obj1 = {
    a: 1
};
var obj2 = {
    a: 2,
    func: function() {
        console.log(this.a);
    }.bind(obj1)
};

obj2.func(); // 1

ECMAScript 5 引入了 Function.prototype.bind,其会创建一个绑定函数,当调用这个绑定函数时,函数上下文将会是 bind() 方法的第一个参数。上面的例子中,将 obj1 设置为函数上下文,所以利用 func 来调用函数时,函数的上下文为 obj1,而不是它的调用者 obj2。

利用 Array 的 5 个方法改变函数上下文

5 个方法分别是:

Array.prototype.every(callbackfn [, thisArg ])

Array.prototype.some(callbackfn [, thisArg ])

Array.prototype.forEach(callbackfn [, thisArg ])

Array.prototype.map(callbackfn [, thisArg ])

Array.prototype.filter(callbackfn [, thisArg ])

当调用以上 5 个方法时,传入的参数除了回调函数以外,还可以传入另外一个可选地参数,即函数上下文,代表回调函数中的函数上下文。如果省略该参数,则 callback 被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined。看下面的例子:

var arr = ["segmentfault"];
var obj = {};
arr.forEach(function(ele, ind) {
    console.log(this === window); // true
});
arr.forEach(function(ele, ind) {
    console.log(this === obj);    // true
}, obj);
测试

在网上找了几个代码小例子,来测试对上面内容的理解,答案在最下面。

1、
if (true) {
    // this
}
2、
var obj = {
    someData: "a string"
};

function myFun() {
    // this
}

obj.staticFunction = myFun;
obj.staticFunction();
3、
var obj = {
    myMethod : function () {
        // this
    }
};
var myFun = obj.myMethod;
myFun();
4、
function myFun() {
    // this
}
var obj = {
    someData: "a string"
};
myFun.call(obj);
答案

1、window

2、obj

3、window

4、obj

注:以上代码均在浏览器环境中测试。

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

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

相关文章

  • js插件讲解_javascript原生日历插件讲解

    摘要:效果图如下代码日第一个参数代表是否是今天的日历,如果第一个参数为,还需要传递俩个参数,年和月。如何制作控制月份的控件方法如下得到页面上的对象控件。 效果图如下:showImg(https://segmentfault.com/img/bV0rKL?w=379&h=221); html代码 - 2017/12 + 1234...

    canger 评论0 收藏0
  • js插件讲解_javascript原生日历插件讲解

    摘要:效果图如下代码日第一个参数代表是否是今天的日历,如果第一个参数为,还需要传递俩个参数,年和月。如何制作控制月份的控件方法如下得到页面上的对象控件。 效果图如下:showImg(https://segmentfault.com/img/bV0rKL?w=379&h=221); html代码 - 2017/12 + 1234...

    qpal 评论0 收藏0
  • js插件讲解_javascript原生日历插件讲解

    摘要:效果图如下代码日第一个参数代表是否是今天的日历,如果第一个参数为,还需要传递俩个参数,年和月。如何制作控制月份的控件方法如下得到页面上的对象控件。 效果图如下:showImg(https://segmentfault.com/img/bV0rKL?w=379&h=221); html代码 - 2017/12 + 1234...

    AlphaWatch 评论0 收藏0
  • Javascript 设计模式 应用级讲解

    摘要:继承关键字传给父类执行实际应用就是提取公共部分,复用代码。关于封装,完全开发对子类开放对自己开放,目前还不支持。 showImg(https://segmentfault.com/img/remote/1460000016136611?w=800&h=355); showImg(https://segmentfault.com/img/remote/1460000016136612);...

    chengtao1633 评论0 收藏0
  • 正在失业中的《课多周刊》(第3期)

    摘要:正在失业中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。是一种祸害译本文浅谈了在中关于的不好之处。浅谈超时一运维的排查方式。 正在失业中的《课多周刊》(第3期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的...

    robin 评论0 收藏0

发表评论

0条评论

zhangwang

|高级讲师

TA的文章

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