资讯专栏INFORMATION COLUMN

Function Definition, This and Bind in JavaScript

siberiawolf / 2192人阅读

I thought I know the Function definition, execution context and the behavior of this in JavaScript. However, I realized that actually I don"t or the knowlege is still not firmly grounded in my mind when I wrote some code similar to below snippet but have no instinct of the error.

var TestObj = {
    a: function() {
        console.log("A");
    },
    b: function() {
        console.log("B");
        this.a();
    }
};

TestObj.b();

var c = TestObj.b;
c();

The result will be as below, right?

B
A
B
A

You might suspiciously answer No but If your instint doesnot tell you that and why, then you don"t know JavasScript well either like me. The result actually is:

B
A
B
TypeError: Object [object global] has no method "a"

It is a little bit awkward or counterintuitive at first glance but it"s JavaScript. It"s the feature and amazing part. Let"s break it down piece by piece and see why.

Function definition

The TestObj includes two methods. The Function definition there actually creates two anonymous functions and then the references to the functions are assigned to the properties a and b. Those two functions are not owned by TestObj and just referred by the two properties of TestObj. This is the most important part causes the confusion. Hence, above code has not much difference than below except now we assign a name B for one of the function:

function B() {
    console.log("B");
    this.a();
};

var TestObj = {
    a: function() {
        console.log("A");
    },
    b: B
};
this

In ECMA-262 edition 5.1:

  

10.4.3 Entering Function Code
The following steps are performed when control enters the execution context for function code contained in
function object F, a caller provided thisArg, and a caller provided argumentsList:

  
  

If the function code is strict code, set the ThisBinding to thisArg.

Else if thisArg is null or undefined, set the ThisBinding to the global object.

Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).

Else set the ThisBinding to thisArg.
...

this is a special keyword refers to the binding object in the current execution context of the Function.

Once we invoke the Function through Object method, the this inside the Function body actually has been set to the TestObj instance. Hence, TestObj.b() logs B and A consecutively because this.a exists as a property of TestObj.

However, below statements mean differently.

var c = TestObj.b;
c();

Actually, variable c is just another reference pointing to Function B. Hence c() is same as B(). When directly invoking Function B, the this is bound to global object. Because there is no a defined in the global object, error occurs.

How to set a particular object as this to function

It"s commonly known that call and apply method can be called on the Function object providing a specific object as this, say:

var c = TestObj.b;
c.call(TestObj);

The result is desirable. However, this approach invokes the Function immediately. This is normally not the case that a Function has to be assigned to a Reference and passed around which is meant to be executed dynamically, like:

function dynamic(fn) {
  fn();
}

dynamic(TestObj.b);

In this case, we should not use fn.call(TestObj) or fn.apply(TestObj) because it"s a generic Function which should have no knowledge on the Function passed in. Hence, above is not working.

There is still another lifesaver though. The bind method of Function. This method can take the passed in Object like what call or apply does, but it returns a new Function whose this binding is set to the Object passed in. So, above code can be revised as:

function dynamic(fn) {
  fn();
}

dynamic(TestObj.b.bind(TestObj));

It"s fun, isn"t it?

[Edited on 2013/06/17]: Today, I saw another case which maybe confusing too.

var length = 3;

function logLength() {
  console.log(this.length);
}

var TestObj = {
    length: 2,
    b: logLength,
    c: function() {
        (function(fn) {
            arguments[0]();
        })(logLength);
    }
};

TestObj.b();
TestObj.c();

What do you think the console should log? Will it be 2 and 3? Actually, the result is 2 and 1. Because the TestObj.c() actually is calling the function logLength on the arguments Object, and then the this.length is referring to its own length, which is 1.

More fun, right?

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

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

相关文章

  • Learning Notes - Understanding the Weird Parts of

    摘要:标签前端作者更多文章个人网站 Learning Notes - Understanding the Weird Parts of JavaScript 标签 : 前端 JavaScript [TOC] The learning notes of the MOOC JavaScript: Understanding the Weird Parts on Udemy,including...

    inapt 评论0 收藏0
  • Laravel学习笔记之IoC Container实例化源码解析

    摘要:说明本文主要学习容器的实例化过程,主要包括等四个过程。看下的源码如果是数组,抽取别名并且注册到中,上文已经讨论实际上就是的。 说明:本文主要学习Laravel容器的实例化过程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...

    ningwang 评论0 收藏0
  • 基于JavaScript的一些函数式编程概念讲解

    摘要:以此类推,不定参数的方程也就被称为可变参数函数。一般来说,函数式编程中的值都被认为是不可变值。实现了函数的对象,即可以与其他对象进行对比判断是否属于同一类型,被称为。半群一个拥有,即将另一个对象转化为相同类型的函数,函数的对象称为。 原文地址译者的Github 系列文章地址本文原作者尚未全部完成,有兴趣的可以到原文或者译文地址关注更新 Functional Programming Ja...

    scola666 评论0 收藏0
  • vue源码解读-component机制

    摘要:通过的合并策略合并添加项到新的构造器上缓存父构造器处理和相关响应式配置项在新的构造器上挂上的工具方法缓存组件构造器在上总的来说是返回了一个带有附加配置相的新的的构造器。在函数中,构造器叫做,等待时候初始化。 身为原来的jquery,angular使用者。后面接触了react和vue。渐渐的喜欢上了vue。抱着学习的态度呀。看看源码。果然菜要付出代价。一步步单步调试。头好疼。看到哪里记到...

    1treeS 评论0 收藏0
  • 深入理解JavaScript内部原理: function(转)

    摘要:注意,下面一个立即执行的函数,周围的括号不是必须的,因为函数已经处在表达式的位置,解析器知道它处理的是在函数执行阶段应该被创建的,这样在函数创建后立即调用了函数。 本文是翻译http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/#introduction 概要In this article we will talk abou...

    OnlyLing 评论0 收藏0

发表评论

0条评论

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