有关 this 的绑定(一)

223 阅读5分钟

最近在读《你不知道的 JavaScript 》系列,感觉学到了不少东西。现在来记录下学到的 this 相关的知识(与其走马观花,不求甚解,不如集中精力,一次学习一点知识)。

阅读时间三分钟,适合在地铁上阅读。有不对的地方,欢迎留言指正。

  1. this 是在函数调用过程中进行绑定的,而非在函数定义过程中绑定。所以在寻找 this 的绑定对象时,我们首先必须找到调用位置。

  2. 独立函数调用时,this 默认绑定在全局对象上。

  3. 今天我们主要看下面三段代码,看完之后我们会对 this 的绑定有个最基本的了解。

  • 代码片段一
var log = function() {
    console.log.apply(console, arguments)
}

function foo() {
    log(this)
    log(this === window)
    log(this.a)
}

var a = 2
foo() 

以上代码片段中,foo()是直接使用不带任何修饰的函数引用进行调用的。所以 this 值得是 window。从控制台的打印结果我们也可以看到,第一行 log 输出为 window;第二行 log 输出为 true;第三行 log 输出为 2 。 下面附上控制台的输出截图:

window

  • 代码片段二
function foo() {
    'use strict'
    log(this)
    log(this === undefined)
    log(this.a)
}

var a = 2
foo() 

以上代码片段中,因为使用了严格模式,所以 this 无法绑定到 window,而是会绑定到 undefined 。执行第三个 log 时报错,错误内容是 undefined 没有 a 这个属性。从控制台的打印结果我们也可以看到,第一行 log 输出为 undefined,注意不是字符串 'undefined';第二行 log 输出为 true;执行到第三行 log 时报错(因为此时 this 绑定到 undefined 上,而执行 undefined.a 会报错)。 下面附上控制台的输出截图:

undefined

  • 代码片段三
function foo() {
    log(this)
    log(this === window)
    log(this.a)
}

var a = 2;

(function(){
    'use strict'
    foo()
})()

相信不少同学也看到过以上的代码,在上述代码中,我们先来解释几点东西:

  1. 在声明变量 a 并对变量 a 进行赋值时,若其后紧跟立即执行函数表达式时,则在变量声明并赋值中要加分号 ,否则会报错(否则会认为变量值 2 是一个函数;不加分号的报错内容是 ‘2 is not a function’ )
  2. 补充:立即执行函数表达式:一个函数外面套一个括号,其后紧跟一个括号;这样的模式称为立即执行函数表达式,英文IIFE(Immediately Invoked Function Expression)其中,函数外面包的括号使得函数成为了一个表达式,在末尾加一个括号可以立即执行这个函数
  3. 若直接执行不加括号,型如 function(){}(),则会报错语法错误(报错内容是:‘Unexpected token (’)相信大家有注意过 jQuery 代码中第一行,是一个感叹号再加一个函数,其实那个感叹号的作用跟立即执行函数表达式中的第一个括号的作用相同,都是把函数变成一个函数表达式;网上有网友说括号和感叹号性能不同,我也只知道这么多,其他的不知道了。(感叹号+函数,会变成一个布尔值,跟我们学习的 js 中的 ! 取反,我认为是相同的)
    boolean
  4. 在代码片段 3 中,混用了严格模式与非严格模式(在 foo 函数定义中使用了非严格模式,在立即执行函数表达式中使用了严格模式)。在开发过程中,我们不应该这样做。整个程序要么是严格模式要么是非严格模式。但是,因为我们很可能用到第三方的库,而第三方的库的模式(严格还是非严格)很可能与我们所用的模式不同,因此要注意这样的兼容性细节。
  5. 在代码片段 3 中,混用模式下,this 指的是全局对象 window。从控制台的打印结果我们也可以看到,第一行 log 输出为 window;第二行 log 输出为 true;第三行 log 输出为 2 。 下面附上控制台的输出截图:
    混合模式下window
  • 代码片段四
function foo() {
    'use strict'
    log(this)
    log(this === window)
    log(this.a)
}

var a = 2;

(function(){
    'use strict'
    foo()
})()

相信不少小伙伴对以上代码片段三仍抱有一些疑惑,我也是。所以,我们再来看下代码片段四。代码片段四都是在严格模式下,所以 this 绑定的是 undefined。从控制台的打印结果我们也可以看到,第一行 log 输出为 undefined,注意不是字符串 'undefined';第二行 log 输出为 false;执行到第三行 log 时报错(因为此时 this 绑定到 undefined 上,而执行 undefined.a 会报错)。 下面附上控制台的输出截图:

严格模式下undefined

最后,再说下代码片段中的自定义 log 的好处以及使用技巧:

  1. 少输入一些字符(少输入 console.,只需要输入 log,便可以在控制台进行输出)
  2. 若以后不用 log 时,直接将函数 log 置为空函数,而不用一行一行删除 log(或者不用一行一行查找 log 再将其注释掉)。删除代码中的 log 时,只需要这样定义 log 函数即可:
var log = function() {}
  1. 在使用 log 时,对每次的输出做上标记,这样我们在控制台就可以知道输出的内容到底是什么,而不用记忆。如:
log('这是一个字符串', 'hello world')

这样,我们在控制台就可以知道输出的内容是在哪里输出的。

  1. 在对一些不确定的数据输出时,可以给数据做一个标记,如在数据左右两侧各加一个‘*’(星号)。这样对于空数据我们就知道这个数据是空数据,而不是说我们没有获得数据或者说我们认为自己的程序出问题了。
log('data', '*' + 数据变量 + '*')

这样在控制台输出 ** 时,如果我们确定已经获得了数据,那看到 ** 我们就知道这个数据是空的,而不是我们没有获得等原因。

**提示我们是空数据

以上是最近的学习心得,欢迎一起交流,一起进步。