当前位置:  开发笔记 > 编程语言 > 正文

"this"关键字如何在函数中起作用?

如何解决《"this"关键字如何在函数中起作用?》经验,为你挑选了3个好方法。

我刚刚在JavaScript中遇到了一个有趣的情况.我有一个类,其方法使用object-literal表示法定义多个对象.在这些对象中,this正在使用指针.从程序的行为,我推断出this指针指的是调用方法的类,而不是文字创建的对象.

这似乎是随意的,尽管这是我期望它工作的方式.这是定义的行为吗?跨浏览器安全吗?有没有任何理由可以解释为什么它超出"规范如此说明"的方式(例如,它是否是一些更广泛的设计决策/哲学的结果)?简化代码示例:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}

Alan Storm.. 557

从我的另一篇文章拆解,这里有更多的比你想要知道这个.

在开始之前,这是关于Javascript最重要的事情,并且当它没有意义时重复自己.Javascript没有类(ES6 class是语法糖).如果看起来像一个类,这是一个聪明的把戏.Javascript具有对象功能.(这不是100%准确,函数只是对象,但将它们视为单独的东西有时会有所帮助)

这个变量被附连到功能.当你调用一个函数,这个被赋予了一定的价值,这取决于你如何调用该函数.这通常称为调用模式.

有四种方法可以在javascript中调用函数.您可以将函数作为方法,函数,构造函数apply来调用.

作为一种方法

方法是附加到对象的函数

var foo = {};
foo.someMethod = function(){
    alert(this);
}

当作为一种方法被调用,将被绑定到对象的功能/方法是的一部分.在这个例子中,这将绑定到foo.

作为一个功能

如果你有一个独立的函数,这个变量将被绑定到"全局"对象,几乎总是在浏览器的上下文中的窗口对象.

 var foo = function(){
    alert(this);
 }
 foo();

这可能是绊倒你,但不要感觉不好.很多人认为这是一个糟糕的设计决定.由于回调是作为函数而不是作为方法调用的,因此您可以看到看似不一致的行为.

很多人通过这样做来解决这个问题

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

你定义一个变量指向这个.Closure(一个它自己的主题)保持这一点,所以如果你把bar称为回调,它仍然有一个引用.

注意:在use strict模式中,如果用作功能,this则不绑定到全局.(是的undefined).

作为构造函数

您还可以将函数作为构造函数调用.根据您正在使用的命名约定(TestObject),这也可能是您正在做的事情,也是您绊倒的原因.

使用new关键字将函数作为构造函数调用.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

当作为一个构造函数调用时,一个新的对象将被创建,并且将被绑定到该对象.同样,如果你有内部函数,他们正在使用回调,你会被调用它们的功能,并且将被绑定到全局对象.使用那个=这个技巧/模式的var.

有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的骨骼,作为创建类似于类的东西的方法.

使用Apply方法

最后,每个函数都有一个名为"apply"的方法(是的,函数是Javascript中的对象).应用可以让你决定的价值来决定将是,也可以让你在参数数组传递.这是一个无用的例子.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

注意:在[严格模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)中,对于函数调用,`this`将是`undefined`. (8认同)


Jonny Buchan.. 35

函数调用

函数只是一种Object.

所有Function对象都有call和apply方法,这些方法执行它们被调用的Function对象.

调用时,这些方法的第一个参数指定this在执行函数期间由关键字引用的对象- 如果它是null或者undefined全局对象window,则用于this.

因此,调用一个函数......

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

...带括号 - foo()- 相当于foo.call(undefined)foo.apply(undefined),实际上foo.call(window)or 相同foo.apply(window).

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

其他参数call作为函数调用的参数传递,而单个附加参数apply可以将函数调用的参数指定为类似Array的对象.

因此,foo(1, 2, 3)相当于foo.call(null, 1, 2, 3)foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

如果函数是对象的属性......

var obj =
{
    whereAmI: "obj",
    foo: foo
};

...通过对象访问函数的引用并用括号调用 - obj.foo()- 相当于foo.call(obj)foo.apply(obj).

但是,作为对象属性保存的函数不会"绑定"到这些对象.正如您在obj上面的定义中所看到的,由于函数只是一种Object类型,因此可以引用它们(因此可以通过引用传递给函数调用或通过函数调用的引用返回).当传递对Function的引用时,没有关于它哪里传递的附加信息,这就是为什么会发生以下情况:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

对函数引用的调用baz不会为调用提供任何上下文,因此它实际上是相同的baz.call(undefined),因此this最终引用window.如果我们想要baz知道它属于obj,我们需要在baz调用时以某种方式提供该信息,这是第一个参数call或者apply和闭包发挥作用的地方.

范围链

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

执行Function时,它会创建一个新范围并引用任何封闭范围.当在上面的例子中创建匿名函数时,它会引用它所创建的范围,这是bind范围.这被称为"封闭".

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

当您尝试访问变量时,将使用此"范围链"查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推,直至达到全球范围.当返回匿名函数并bind完成执行时,匿名函数仍然具有对bind范围的引用,因此bind范围不会"消失".

鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作原理,以及为什么在this调用它时具有特定值的"预绑定"函数传递函数的原因是:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"


Rakesh Pai.. 9

这是定义的行为吗?跨浏览器安全吗?

是.是的.

有没有任何理由可以解释为什么它是这样的...

this推理的含义非常简单:

    如果this在构造函数内部使用,并且使用new关键字调用该函数,则引用this将要创建的对象.this即使在公共方法中也会继续表示对象.

    如果this在其他任何地方使用,包括嵌套的受保护函数,则它引用全局范围(在浏览器的情况下是窗口对象).

第二种情况显然是一个设计缺陷,但通过使用闭包很容易解决它.



1> Alan Storm..:

从我的另一篇文章拆解,这里有更多的比你想要知道这个.

在开始之前,这是关于Javascript最重要的事情,并且当它没有意义时重复自己.Javascript没有类(ES6 class是语法糖).如果看起来像一个类,这是一个聪明的把戏.Javascript具有对象功能.(这不是100%准确,函数只是对象,但将它们视为单独的东西有时会有所帮助)

这个变量被附连到功能.当你调用一个函数,这个被赋予了一定的价值,这取决于你如何调用该函数.这通常称为调用模式.

有四种方法可以在javascript中调用函数.您可以将函数作为方法,函数,构造函数apply来调用.

作为一种方法

方法是附加到对象的函数

var foo = {};
foo.someMethod = function(){
    alert(this);
}

当作为一种方法被调用,将被绑定到对象的功能/方法是的一部分.在这个例子中,这将绑定到foo.

作为一个功能

如果你有一个独立的函数,这个变量将被绑定到"全局"对象,几乎总是在浏览器的上下文中的窗口对象.

 var foo = function(){
    alert(this);
 }
 foo();

这可能是绊倒你,但不要感觉不好.很多人认为这是一个糟糕的设计决定.由于回调是作为函数而不是作为方法调用的,因此您可以看到看似不一致的行为.

很多人通过这样做来解决这个问题

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

你定义一个变量指向这个.Closure(一个它自己的主题)保持这一点,所以如果你把bar称为回调,它仍然有一个引用.

注意:在use strict模式中,如果用作功能,this则不绑定到全局.(是的undefined).

作为构造函数

您还可以将函数作为构造函数调用.根据您正在使用的命名约定(TestObject),这也可能是您正在做的事情,也是您绊倒的原因.

使用new关键字将函数作为构造函数调用.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

当作为一个构造函数调用时,一个新的对象将被创建,并且将被绑定到该对象.同样,如果你有内部函数,他们正在使用回调,你会被调用它们的功能,并且将被绑定到全局对象.使用那个=这个技巧/模式的var.

有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的骨骼,作为创建类似于类的东西的方法.

使用Apply方法

最后,每个函数都有一个名为"apply"的方法(是的,函数是Javascript中的对象).应用可以让你决定的价值来决定将是,也可以让你在参数数组传递.这是一个无用的例子.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);


注意:在[严格模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)中,对于函数调用,`this`将是`undefined`.

2> Jonny Buchan..:

函数调用

函数只是一种Object.

所有Function对象都有call和apply方法,这些方法执行它们被调用的Function对象.

调用时,这些方法的第一个参数指定this在执行函数期间由关键字引用的对象- 如果它是null或者undefined全局对象window,则用于this.

因此,调用一个函数......

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

...带括号 - foo()- 相当于foo.call(undefined)foo.apply(undefined),实际上foo.call(window)or 相同foo.apply(window).

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

其他参数call作为函数调用的参数传递,而单个附加参数apply可以将函数调用的参数指定为类似Array的对象.

因此,foo(1, 2, 3)相当于foo.call(null, 1, 2, 3)foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

如果函数是对象的属性......

var obj =
{
    whereAmI: "obj",
    foo: foo
};

...通过对象访问函数的引用并用括号调用 - obj.foo()- 相当于foo.call(obj)foo.apply(obj).

但是,作为对象属性保存的函数不会"绑定"到这些对象.正如您在obj上面的定义中所看到的,由于函数只是一种Object类型,因此可以引用它们(因此可以通过引用传递给函数调用或通过函数调用的引用返回).当传递对Function的引用时,没有关于它哪里传递的附加信息,这就是为什么会发生以下情况:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

对函数引用的调用baz不会为调用提供任何上下文,因此它实际上是相同的baz.call(undefined),因此this最终引用window.如果我们想要baz知道它属于obj,我们需要在baz调用时以某种方式提供该信息,这是第一个参数call或者apply和闭包发挥作用的地方.

范围链

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

执行Function时,它会创建一个新范围并引用任何封闭范围.当在上面的例子中创建匿名函数时,它会引用它所创建的范围,这是bind范围.这被称为"封闭".

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

当您尝试访问变量时,将使用此"范围链"查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推,直至达到全球范围.当返回匿名函数并bind完成执行时,匿名函数仍然具有对bind范围的引用,因此bind范围不会"消失".

鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作原理,以及为什么在this调用它时具有特定值的"预绑定"函数传递函数的原因是:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"



3> Rakesh Pai..:

这是定义的行为吗?跨浏览器安全吗?

是.是的.

有没有任何理由可以解释为什么它是这样的...

this推理的含义非常简单:

    如果this在构造函数内部使用,并且使用new关键字调用该函数,则引用this将要创建的对象.this即使在公共方法中也会继续表示对象.

    如果this在其他任何地方使用,包括嵌套的受保护函数,则它引用全局范围(在浏览器的情况下是窗口对象).

第二种情况显然是一个设计缺陷,但通过使用闭包很容易解决它.

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有