Javascript的this详解

在理解javascript的this之前,首先先了解一下作用域。

作用域分为两种:

1、词法作用域:引擎在当前作用域或者嵌套的子作用域查找具有名称标识符的变量。(引擎如何查找和在哪查找。定义过程发生在代码书写阶段)

2、动态作用域:在运行时被动态确定的作用域。

词法作用域和动态作用域的区别是:词法作用域是在写代码或定义时确定的;动态作用域是在运行时确定的。

this的绑定规则

this是在调用时被绑定,取决于函数的调用位置。由此可以知道,一般情况下(非严格模式下),this都会根据函数调用(调用栈)的上下文来绑定对象。

一、默认绑定

默认绑定:默认绑定是指在非严格模式下,且没有使用别的绑定规则时,this根据函数调用(调用栈)的上下文来绑定对象(全局对象)。(严格模式下则绑定undefined)

举个栗子:

function foo() { console.log(this.a); }; function bar() { var a = 3; foo(); } var a = 2; bar(); //调用栈在全局作用域,this绑定全局对象

运行结果为: 2

//加上"use strict"运行结果则会变成this is undefined

这里的函数调用时,使用了默认绑定,函数调用(调用栈)的上下文是全局作用域,因此this绑定了全局对象(global)。

eg2:

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

运行结果为: 2

这里需要注意:对于默认绑定,决定this绑定对象的不是调用位置是否处于严格模式,而是函数体是否处于严格模式(函数体处于严格模式则this绑定undefined;否则this绑定全局对象)。另外:严格模式和非严格模式虽然有可能可以绑定,但是最好不混用。

间接引用一般也是会应用默认绑定规则。

function foo() { console.log(this.a); }; var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); //3 (p.foo = o.foo)(); //2

赋值表达式 p.foo = o.foo的返回值是直接引用目标函数foo。

二、隐式绑定

隐式绑定:由上下文对象调用,绑定到上下文对象。

举个栗子:

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

这段代码中,foo()被当做引用属性添加到obj对象中,obj调用这个引用属性函数时,会使用该引用属性上下文,this会被绑定到obj对象。(这个函数严格来说不属于obj对象,只是作为引用属性)。属于隐式绑定。

而下面foo()函数的直接执行,并不是obj对象引用,所以上下文对象是全局对象。故this绑定了undefined。属于默认绑定。

对象引用链中只有上一层或者说最后一层在调用位置中起作用。

注意:

1.隐式绑定的函数会丢失绑定对象。此时它会应用默认绑定,将this绑定到全局对象或者undefined上,取决于是否是严格模式。

eg:

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

运行结果:"biubiubiu"

解析:看似bar是obj.foo的一个引用,实际上bar是直接引用了函数foo,是一个单纯的函数调用,故实为默认绑定。

2.参数传递就是隐式赋值,因此传入函数时也会被隐式赋值。

eg:

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

运行结果: "biubiubiu"

解析:实际上参数也是隐式赋值,但是参数传入函数中,并在函数中执行。此时也是直接引用了函数foo,因此也是单纯的函数调用,采用了默认绑定。

3.把函数传入语言内置函数。(与上面情况基本相似,将自己声明函数改成语言内置函数)回调函数丢失this的情况比较常见,况且还有调用回调函数的函数可能还会修改this。

三、显式绑定

显式绑定:直接将this绑定到指定对象上。Javascript中绝大多数函数和自己所创建的函数都可以使用这两种显式绑定的方法。

1、.call()
2、.apply()

这两种绑定方法,第一个参数是this绑定的对象。(如果传入的参数是原始值(字符串类型、布尔类型、数字类型),这个原始值就会被转成对象形式(new String、new Boolean、new Number)这个称为:装箱)

举个栗子:

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

运行结果: 2

然鹅,显示绑定并不能解决绑定丢失的问题。这个时候来了一位新朋友 -- 硬绑定(bind)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/3039a004f88607f9a2221b79c6079d09.html