257

我刚刚在JavaScript中遇到一个有趣的情况。我有一个类,它具有一个使用对象-双边表示法定义多个对象的方法。在这些对象中正在使用指针。根据程序的行为,我推断出指针指的是调用方法的类,而不是由文本创建的对象。

这似乎是武断的,尽管这是我所期望的工作方式。这是定义好的行为吗?跨浏览器安全吗?为什么它超出了“规范这么说”的范围(例如,它是某种更广泛的设计决策/哲学的结果),背后有什么理由吗?Pared down代码示例:

//在类定义中,它本身是一个对象文字,我们有这个函数:onRender:函数(){this.menuItems=此.menuItems.concat([{text:'按模块分组',rptletdiv:这个},{text:'按状态分组',rptletdiv:这个}]);//等}
4

7个答案7

重置为默认值
575

从我的另一篇帖子中被蚕食了,这里有很多你想知道的.

在我开始之前,这里是关于Javascript最重要的一点要记住的,当它没有意义时,请重复一遍。Javascript没有类(ES6语法甜头). 如果某个东西看起来像一个类,那是一个聪明的技巧。Javascript具有物体功能(这并不是100%准确,函数只是对象,但有时将它们视为单独的东西会有所帮助)

这个变量附加到函数。无论何时调用函数,根据调用函数的方式,给定特定值。这通常称为调用模式。

在javascript中有四种调用函数的方法。您可以将该函数作为方法,作为功能,作为建造师,和应用.

作为一种方法

方法是附加到对象的函数

变量foo={};foo.someMethod=函数(){警报(此);}

当作为方法调用时,将绑定到函数/方法所属的对象。在本例中,它将绑定到foo。

作为一个函数

如果你有一个独立的功能变量将绑定到“全局”对象,几乎总是窗口对象。

var foo=函数(){警报(此);}foo();

这可能是你绊倒的原因,但别难过。许多人认为这是一个糟糕的设计决策。因为回调是作为函数而不是作为方法调用的,所以您会看到不一致的行为。

很多人通过这样的方式来解决这个问题

变量foo={};foo.someMethod=函数(){var that=这个;功能栏(){提醒;}}

定义变量那个它指向.闭包(一个主题)保持那个所以,如果您调用bar作为回调,它仍然有引用。

注:在使用严格模式(如果用作功能),未绑定到全局。(它是未定义).

作为建造师

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

使用新关键字将函数作为构造函数调用。

函数Foo(){this.confusing=“见鬼去吧”;}var myObject=新Foo();

当作为构造函数调用时,将创建一个新的Object,并且将绑定到该对象。同样,如果您有内部函数并且它们被用作回调,那么您将把它们作为函数调用,并且将绑定到全局对象。使用var=这个技巧/模式。

一些人认为constructor/new关键字是Java/传统OOP程序员用来创建类似类的东西的一种方式。

使用Apply方法

最后,每个函数都有一个名为“apply”的方法(没错,函数是Javascript中的对象)。应用可以确定将是,并且还允许您传入参数数组。这里有一个无用的例子。

函数foo(a,b){警报(a);警报(b);警报(此);}var args=['ah','be'];foo.apply('omg',args);
5
  • 8
    注:在严格模式,未定义用于函数调用。 评论 2015年7月8日18:05
  • 1
    函数声明,例如函数myfunction(){},是“as A method”的特例,其中“this”是全局范围(窗口)。
    – 理查德
    评论 2015年7月23日6:54
  • 1
    @理查德:除了严格模式,还有与范围无关。你是说全球对象. 评论 2016年7月18日13:45
  • @alan型。在“作为构造函数”的情况下this.confusing=“见鬼去吧”;一样var conmissing=“见鬼耶”;? 所以两者都允许myObject.融合? 如果不是为了让你能使用,那就太好了为内部工作创建属性和其他变量。
    – 温特
    评论 2017年9月20日4:23
  • 但我再次猜测,工作可以在函数和传递给构造函数的值之外完成:函数Foo(思想){this.confusing=思想;}然后var myObject=new Foo(“见鬼”);
    – 温特
    评论 2017年9月20日19:21
36

函数调用

函数只是对象的一种类型。

所有Function对象都有呼叫应用执行调用它们的Function对象的方法。

调用时,这些方法的第一个参数指定将由函数执行期间的关键字-如果无效的未定义,全局对象,窗口,用于.

因此,调用函数。。。

其中AmI=“窗口”;函数foo(){return“this is”+this.whereAmI+“with”+arguments.length+“+argument”;}

…带括号-foo()-等于foo.call(未定义)foo.apply(未定义),这是有效地一样foo.call(窗口)foo.apply(窗口).

>>>foo()“这是一个有0个参数的窗口”>>>foo.call()“这是包含0个参数的窗口”

的其他参数呼叫作为参数传递给函数调用,而单个附加参数传递给应用可以将函数调用的参数指定为类数组对象。

因此,foo(1、2、3)等于foo.call(空,1,2,3)foo.apply(空,[1,2,3]).

>>>foo(1,2,3)“这是带有3个参数的窗口”>>>foo.apply(空,[1,2,3])“这是带有3个参数的窗口”

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

var对象={其中AmI:“obj”,foo:foo};

…通过对象访问对函数的引用并用括号调用它-对象.foo()-等于foo.call(对象)foo.apply(对象).

然而,作为对象属性持有的函数并没有“绑定”到这些对象。正如您在定义中所看到的对象如上所述,因为函数只是对象的一种类型,所以可以引用它们(因此可以通过引用传递给函数调用或通过引用从函数调用返回)。传递对函数的引用时,没有关于传递位置的附加信息因此,会发生以下情况:

>>>baz=obj.foo;>>>baz();“这是包含0个参数的窗口”

调用我们的函数引用,巴兹,不为调用提供任何上下文,因此它实际上与baz.call(未定义),所以最终引用窗口.如果我们愿意巴兹知道它属于对象,我们需要在以下情况下以某种方式提供该信息巴兹调用,这是呼叫应用和闭包开始发挥作用。

范围链

函数绑定(func,context){返回函数(){函数应用(上下文、参数);};}

当一个函数被执行时,它会创建一个新的作用域并引用任何封闭作用域。在上例中创建匿名函数时,它引用了创建它的范围,即绑定的范围。这被称为“关闭”

[全局范围(窗口)]-其中AmI、foo、obj、baz|[绑定范围]-func,上下文|[匿名范围]

当您尝试访问变量时,会遍历此“范围链”以查找具有给定名称的变量-如果当前范围不包含该变量,则查看链中的下一个范围,依此类推,直到到达全局范围。当返回匿名函数并绑定执行完成后,匿名函数仍然引用绑定的范围,所以绑定的范围不会“消失”。

鉴于以上所述,您现在应该能够理解以下示例中的范围是如何工作的,以及为什么要在具有特定值的“预绑定”周围传递函数的技术当它被称为works时:

>>>baz=绑定(obj.foo,obj);>>>baz(1,2);“这是带有2个参数的obj”
1
  • 传递对函数的引用时,不会携带有关传递位置的附加信息谢谢你@insin。 评论 2012年3月18日12:03
9

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

对。是的。

有什么理由可以解释为什么吗就是这样。。。

的含义很容易推断:

  1. 如果是在构造函数内使用的,该函数是用新的关键字,指的是将要创建的对象。即使在公共方法中,也将继续表示对象。
  2. 如果在其他任何地方使用,包括嵌套受保护的函数,它指的是全局范围(在浏览器中是窗口对象)。

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

4

在这种情况下,内部绑定到全局对象而不是绑定到外部函数的变量。这就是语言的设计方式。

请参阅道格拉斯·克罗福德(Douglas Crockford)的《JavaScript:好的部分》(JavaScript:The Good Parts),以获得更好的解释。

0
4

我找到了一个关于ECMAScript此

此值是与执行相关的特殊对象上下文。因此,可以将其命名为上下文对象(即在其中激活执行上下文的对象)。

任何对象都可以用作上下文的此值。

此值是执行上下文的属性,但不是变量对象的属性。

这个特性非常重要,因为和变量相反,这个值从不参与标识符解析过程。也就是说,当在代码中访问它时,它的值直接从执行上下文中获取,并且没有任何范围链查找。在输入上下文时,此值仅确定一次。

在全局上下文中,该值是全局对象本身(也就是说,这里的该值等于变量对象)

对于函数上下文,每个函数调用中的此值可能不同

参考Javascript-核心第3章-本

1
  • "在全局上下文中,该值是全局对象本身(也就是说,这里的该值等于变量对象)全局对象是全局执行上下文的一部分,(es4)“变量对象”和ES5环境记录也是如此。但它们与全局对象是不同的实体(例如,不能直接引用环境记录,规范禁止这样做,但全局对象可以这样做)。
    – 罗布·G
    评论 2015年4月13日6:03
0

这里的所有答案都很有帮助,但我仍然很难弄清楚是什么在我的例子中,指向了涉及对象破坏的。所以我想使用我的代码的简化版本再添加一个答案,

让testThis={x: 12、,y: 20,添加({a,b,c}){设d=a+b+c()控制台.log(d)},测试(){//结果是NaN这个。添加({a: 这个.x,b: 这个。是的,c: ()=>{//这里是test这里不是对象文字返回this.a+this.b},})},测试2(){//64如预期此.添加({a: 这个.x,b: 这个。是的,c: ()=>{返回this.x+this.y},})},测试3(){//NaN公司此.添加({a: 这个.x,b: 这个。是的,c: 函数(){//这是全局对象返回this.x+this.y},})},}

如下文所述Javascript-破坏对象-“this”设置为全局或未定义,而不是对象它实际上与对象析构化无关,只与c()的调用方式有关,但在这里不容易理解。

MDN公司说“箭头函数表达式最适合非方法函数”,但箭头函数在这里起作用。

0

在JS中:

有三种类型的功能,其中有不同的含义。最好通过示例对其进行解释:

  1. 施工单位

//在构造函数中,这是指新创建的对象//每个函数都可以是JavaScript中的构造函数,例如。功能狗(颜色){this.color=颜色;}//构造函数通过在函数调用前放置new来调用const myDog=新狗(“red”);//原木狗有红色console.log('Dog has color'+myDog.color);

  1. 正常功能或方法

//浏览器示例:console.log(this===窗口)//真函数myFn(){console.log(这个===窗口)}我的Fn();//记录为true//该值取决于上下文对象。//在这种情况下,调用函数的上下文是全局的。//对于浏览器中的全局上下文,上下文对象是窗口。常量myObj={fn:myFn}myObj.fn()//日志为false//在这种情况下,调用函数的上下文是myObj。//因此,将记录false。myObj.fn2=函数myFn(){console.log(this===myObj)}myObj.fn2()//记录为true//在这种情况下,调用函数的上下文是myObj。//因此,记录true。

  1. 事件侦听器

事件处理程序函数内部将引用检测到事件的DOM元素。请参阅此问题:在事件处理程序中使用此

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.