`

javascript this详解

阅读更多

this是每一个想要深入学习Javascript的人必过的一关,我为this看过很多书查过很多资料,虽然对this有了一定的了解并且也经常使用this,但是如果有人问我  this是什么呀? 我依旧不能给别人一个完美的解释。最近一个小的机缘,让我重新对this有了认识,终于觉得自己可以把我认识到的this将给别人听了,所以现在迫不及待的来分享一下我的认识

说到this,最重要的就是this的指向了(这样说并不准确,因为this只是函数被调用时所创建的活动对象中的一个属性而已)。

有些人可能认为this指向的是自身,因为this这个单词的含义就是如此嘛,不过这种认识应该是错误的,不信?来看代码

function foo(num) {
    console.log("foo" + num);
    this.count++;
}
foo.count = 0;
for(var i = 0; i < 5; i++) {
    foo(i);
}
console.log(foo.count);
console.log(count);

 对于第二行的输出肯定大家都是知道答案的

foo0
foo1
foo2
foo3
foo4

那么第九行的输出是什么?(如果this指向的是自身的话foo被调用5次 foo.count应该是5才对,但是!)

 事实上this.count并没有 ++ 由此可以说明,this并不是指向自身。此处,this执行全局变量window

又会有人说,我知道他不是指向自身呀,他明明是指向作用域的嘛(好吧,很长一段时间我也是这么以为的)。不过事实上这也是不正确的,依旧,我们用一段代码来证明

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

 这段代码第六行的输出会是什么呢?是2吗?

运行结果是undefined

this并没有指向自身作用域.看到这里的你现在一定暴虐的想要知道那this到底是个什么鬼,那现在我们就开始一层层解析this是什么

想要知道函数在执行过程中是如何绑定this的,首先要知道函数的调用位置。可能很多人已经可以肯轻松的寻找出函数调用位置,那就轻轻松的看一下我的代码和你想出的答案是不是一样的吧

function baz() {
    //当前调用栈:baz
   //调用位置是全局作用域
    console.log("baz");
    bar();//bar的调用位置
}
function bar() {
    //当前调用栈:baz -> bar
   //调用位置是baz中
    console.log("bar");
    foo();//foo的调用位置
}
function foo() {
    //当前调用栈:baz -> bar -> foo
   //调用位置是bar中
    console.log("foo");
}
baz();//baz的调用位置

 看一下上面的代码,从 baz(); 开始,baz在全局之中,之后baz调用 bar(); 所以bar的调用位置在baz之中,再然后bar调用 foo(); foo的调用位置在baz之中。执行foo函数是的调用栈为baz -> bar -> foo 。

通过函数的互相调用找出调用栈,便可以找出函数的真正调用位置,可是如果代码量过大可能这样人工查找很容易出错,所以我们有更好的方法

 

 

上图右侧向上的箭头所滑过的就是foo函数执行的调用栈,栈中的第二个元素就是真正的调用位置。

 

找到调用位置之后我们来看一下this的绑定符合哪种绑定规则

 

1.隐式绑定: 看调用位置是否有上下文对象,即是否被某个对象拥有或包含(这里说的包含可不是用花括号包起来呦)。

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

 

上面又是一个函数调用里面输出this指向的一个值呢,先不说答案,先看一下,这段代码和上面指出this不是指向作用域的那段代码的区别是什么,我想大家都发现了,这次foo函数的调用是 obj.foo() 。foo函数被用作obj对象的一个方法来调用。很明显根据foo函数的声明位置来看他并不属于obj对象,但是~~哈哈,讲了好多回但是~~foo函数的调用位置是obj的上下文来引用函数,也就是说他的落脚点是obj对象,那~~他的this自然也就指向他的上下文对象。这样一分析,答案就显而易见了,this.a等同于obj.a就是2。

(ps:如果对象属性的引用是多层的,只有最后一层会影响调用位置)。

2.显式绑定: 

   上面的隐式绑定是通过调用一个对象中绑定了函数的属性来把this隐式的绑定在这个对象上的。所以显示绑定就是使用一些方法强制将函数绑定在某个对象上,这些方法就是 call(...)  apply(...) 这两种方法的工作方式是类似的,所以我们就以call()来作为例子分析一下他们的工作过程

 

这两个方法第一个参数是一个对象,而被绑定的函数的this就会绑定在这个对象上,看一段代码

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

 foo函数执行时使用了call函数,call函数的第一个参数是obj对象,则foo函数活动对象的this属性就被绑定在了obj对象上,所以,输出2.

3.new 绑定

 

 

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

 使用 new 来调用函数,即发生构造函数调用时,会执行下面操作

  1. 创建一个全新的对象
  2. 这个对象会被执行[__proto__]的链接(bar.__proto__=Foo.prototype)
  3. 这个新对象会绑定到函数调用的this上
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

 

 

当使用new操作符执行Foo函数时,就创建了一个全新的对象并且赋值给了变量bar。进行原型链接之后(这里不是我们今天讨论的范围)Foo函数活动对象的this属性被绑定在新创建的对象bar上,Foo函数并没有返回值,所以自动返回new创建的bar对象。

4.默认绑定    默认绑定就是不符合上面任何一种规则是的默认规则

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

 上面这段代码foo函数不带任何修饰的直接使用,不符合1-3中的任何一种绑定规则,所以只能使用默认绑定规则,而默认绑定规则就是在非严格模式下被绑定在全局对象上(严格模式下与foo函数的调用位置无关为undefined)。

 

好了,关于this的四种绑定规则都已经解释清楚。那有没有意外情况呢?

是不是意外情况就看你对this的理解有没有到位,有没有认真的分析了,下面放两种特殊情况让大家转动一下脑筋

1

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

 这段代码唯一的特殊点就是bar引用了obj的foo函数。而答案就变成了Global。这是为什么呢?

首先我们直接来输出一下bar

 

可以看到bar就是foo()函数的一个别名而已,虽然他是通过obj引用的foo()函数,但他只是引用到了foo函数,并没有引用到obj对象的执行上下文,所以他符合的是上面this规则的第四条默认绑定,所以this指向全局输出Global。

 

2

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

 这段代码是传入回掉函数。而toDO函数中fn的传递依旧只是传递了foo这个函数,并没有传递obj对象的执行上下文,所以在fn()调用的位置没有任何特殊绑定,符合this规则的默认绑定,this指向全局。不信?我们在toDO函数内输出一下fn看是不是foo函数

看来我说的是对的。

 

一切都解决之后你肯定又会问,那万一我遇见的函数一下子符合了两条规则怎么办?呃。。。我们来测一下绑定规则的优先级吧

 

不用说默认绑定的优先级是最低的,那先不用理他,先看一下隐式绑定和显示绑定谁的优先级更高

function foo() {
    console.log(this.a);
}
var obj1 = {
    a: 2,
    foo: foo
};
var obj2 = {
    a: 3,
    foo: foo
};
obj1.foo();
obj2.foo();

obj1.foo.call(obj2);
obj2.foo.call(obj1);

 

 

看来显式绑定轻松战胜隐式绑定,那显式绑定和new谁的优先级更高呢?好吧,由于new和call(),apply()无法同时使用,所以我没有写出他们之间的测试代码,不过查了些资料之后的结论是new的优先级高于显式绑定(那位大神可以举一个new优先级高于现实绑定的例子呢~如果有的话请贴进评论,小女子在此谢过~~)。

那总结以上经验就是想要判断this绑定规则,先看new,再看显式绑定,再看隐式绑定,都没有则使用默认绑定。

 

 

不过凡事都有例外,不能百分百下定论,比如说显式绑定是对象传入null或者undefined的话,则会忽略call或apply应用默认绑定。

 

还有很多种例外等着大家去发觉。不过还是一般情况占据多数,所以,只要正确寻找分析函数调用栈,找到函数调用位置,在函数调用位置上判断使用那种绑定规则,基本可以正确判断出this的指向。

 

再看一些特别的用法

function Person()
{
  this.username="huangwei";
  this.sayHello=function(){
    console.log("username:"+this.username);
  }
  console.log(this);
  return this;
}

var person=new Person();
person.sayHello();
console.log(person);

 运行结果:

 注释掉第八行(return this),执行结果相同.

 

 

参考:http://www.cnblogs.com/xiaoruo/p/4492453.html

http://gejiawen.github.io/2015/03/18/Javascript/%E5%8C%BA%E5%88%86JS%E4%B8%AD%E7%9A%84__proto__%E5%92%8Cprototype/

  • 大小: 4.1 KB
  • 大小: 10 KB
2
1
分享到:

相关推荐

    JAVASCRIPT THIS详解 面向对象

    本文将深入探讨JavaScript中`this`关键字的多种使用场景,并对比其他面向对象语言中的`this`。 首先,了解`this`在传统的面向对象编程语言中的用途。在C++、C#、Java等语言中,`this`关键字被用于指向当前对象的...

    Javascript的this详解

    JavaScript中的`this`关键字是一个非常重要的概念,它用于在函数执行时引用当前上下文的对象。在JavaScript中,`this`的绑定遵循四个主要规则:默认绑定、隐式绑定、显式绑定和new绑定。让我们逐一深入理解这些规则...

    详解Javascript 中的this指针

    ### 详解Javascript中的`this`指针 在深入探讨`this`指针之前,我们首先应当明确`this`在JavaScript中的基本概念与作用。`this`关键字在JavaScript中扮演了一个非常核心的角色,它是一个特殊的变量,用于引用调用...

    JavaScript中this关键字使用方法详解

    在JavaScript编程语言中,`this`关键字是一个至关重要的概念,它常常引发初学者的困惑,因为它的值在不同的上下文中可能会有所不同。`this`关键字主要用来引用对象的上下文,或者说是当前执行环境中的对象。在本文中...

    JavaScript使用详解,javascript入门教程

    这个"JavaScript使用详解"教程覆盖了JavaScript的各个方面,结合视频和PDF资料,为初学者提供了全面的学习资源。通过学习,你可以掌握JavaScript的基本语法和高级特性,为网页开发打下坚实基础。

    高手详解javascript中的this指针

    在JavaScript中,`this`关键字是一个至关重要的概念,它在不同上下文中有着不同的指向。`this`在JavaScript中并不像其他静态类型语言(如Java或C++)中的指针那样工作,而是根据函数调用的方式动态确定其值。以下是...

    javascript函数详解!

    JavaScript函数详解 JavaScript是一种强大的、基于原型的、动态类型的脚本语言,广泛应用于网页和网络应用开发。在JavaScript中,函数扮演着核心角色,它们不仅可以作为可执行的代码块,还可以作为值进行传递和存储...

    JavaScript使用详解.pdf

    ### JavaScript使用详解 #### 一、JavaScript简介 JavaScript是一种轻量级的编程语言,也是一种解释型脚本语言。它被设计用于向HTML页面添加交互性,例如响应用户的操作(如鼠标点击)、验证表单输入以及动态修改...

    详解javascript中的this对象.pdf

    ## JavaScript 中的 `this` 对象详解 JavaScript 是一种基于对象和事件驱动的动态类型语言,它允许开发者使用面向对象的编程范式。在这个过程中,`this` 关键字扮演了至关重要的角色。然而,`this` 在 JavaScript ...

    【JavaScript源代码】JavaScript函数this指向问题详解.docx

    JavaScript中的`this`关键字是一个非常重要的概念,它在不同的上下文中具有不同的指向。理解`this`的指向对于编写高效和可维护的JavaScript代码至关重要。在本文中,我们将深入探讨`this`在各种函数调用场景下的行为...

    【JavaScript源代码】JavaScript中的this指向问题详解.docx

    这里我们将按照7步口诀详解`this`的指向。 1. **箭头函数**: 箭头函数(`=&gt;`)的`this`不会被改变,它总是捕获其定义所在的作用域中的`this`值。这意味着箭头函数没有自己的`this`,它会继承外层作用域的`this`。...

    Javascript 严格模式详解

    ### JavaScript 严格模式详解 #### 引言 在JavaScript中,**严格模式**(strict mode)是ECMAScript 5引入的一种新特性,旨在改善语言的一些不足,提高代码质量和安全性,以及为未来的JavaScript版本铺平道路。...

    JavaScript 继承详解(六)

    在JavaScript中,继承是面向对象编程的一个核心概念,它允许一个对象获取另一个对象的属性和方法,从而实现代码的重用。Prototype.js是一个早期的JavaScript库,它为JavaScript提供了类和继承的模拟,使得在...

    JavaScript 继承详解(三)

    在JavaScript编程中,继承是面向对象编程的核心概念之一,它允许开发者构建出层级化的对象结构。在JavaScript中实现继承有多种方法,其中构造函数和原型链的方式是较为传统的一种,但在实际应用中存在一些问题和陷阱...

    JavaScript 继承详解(二)

    `this`关键字在JavaScript中是一个动态绑定的引用,它的值取决于函数调用的位置。在全局作用域中,`this`通常指向全局对象,在浏览器环境中就是`window`。在函数内部,`this`的值取决于函数是如何被调用的。如果函数...

    详解JavaScript中的this

    JavaScript 中的this 总是让人迷惑,应该是js 众所周知的坑之一。 个人也觉得js 中的this 不是一个好的设计,由于this 晚绑定的特性,它可以是全局对象, 当前对象,或者…有人甚至因为坑大而不用this。 其实如果...

    详解javascript中的this对象.docx

    在JavaScript中,`this`对象是一个至关重要的概念,它在不同上下文中扮演着不同的角色,帮助开发者实现面向对象的编程。`this`的值取决于函数调用的方式,而不是函数的定义位置。以下是对`this`对象的详细解释: 1....

    详解Javascript 中的this指针.doc

    在JavaScript中,`this`关键字是一个非常重要的概念,它用于引用当前上下文中的对象。由于JavaScript是一种基于原型的动态类型语言,`this`的值在不同的执行环境中可能会有所不同,这使得理解和使用`this`成为了一个...

Global site tag (gtag.js) - Google Analytics