【人人都能读标准】前言:为什么你学不精JavaScript?

本文是《人人都能读标准》的前言,探讨了学习JavaScript时遇到的作用域、this值解析等常见困惑,指出由于标准的晦涩,导致业界对这些基础概念的理解五花八门。作者强调了阅读ECMAScript标准的重要性,标准能够提供概念的具体化、机制明确化和内容完整性的解析。文章提出阅读标准的三大难点:英语、符号系统和标准自有的概念,并提供了解决方案,帮助读者克服阅读障碍,掌握JavaScript的底层原理。
摘要由CSDN通过智能技术生成

本文为《人人都能读标准》—— ECMAScript篇的前言。我在这个仓库中系统地介绍了标准的阅读规则以及使用方式,并深入剖析了标准对JavaScript核心原理的描述。


我先从一个JavaScript的基础概念 —— 作用域,讲起。

你可以在任何搜索引擎或者技术论坛中搜索「js 作用域」,然后在搜索结果中,查看不同的技术文章对作用域这个概念的定义。你会发现,在不同的作者眼中,作用域似乎都不大一样:

某技术文章1:作用域是据名称来查找变量的一套规则。

某技术文章2:作用域指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限。

某技术文章3:作用域,是指变量的生命周期(一个变量在哪些范围内保持一定值)。

某技术文章4:作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。

(不贴出具体链接,是因为有的定义重复出现,我不能确定哪一篇是原创的)

有的作者认为作用域是“一套规则”,有的作者认为作用域是“一个区域”,有的作者认为作用域是“生命周期”,还有的作者认为作用域是一个叫作“可访问性”的抽象概念(这显然是一个对英文资料的生硬翻译)。

造成这种现象的核心原因在于:作用域是一个看不见摸不着的东西,我们只能在执行代码的过程中,感受到他在起作用,但我们从未见过它真正的模样。于是这些作者,只能依据个人开发经验,总结归纳并提炼出他们脑海当中“作用域”的样子。而每个作者不同的经验、不同的视角以及不同的知识储备,最终导致了他们说出来的‘作用域“是五花八门的。

等等,这不就是现实版本的“盲人摸象”吗?


我们已经“盲”了太久

另一个典型例子就是“this值的解析”。如果你像上面一样查看this值相关的技术文章,你会发现,这些文章好像一直都在非常贴心地为你总结着各种各样关于this机制的“一般性规律”,但几乎每一种所谓的一般性规律,都伴随着大量他们知道的以及他们不知道的例外情况,用起来就像一台破旧的电视机,三天两头出问题。

举一条广为流传甚至是流传最广的关于this值的“规律”:

this 永远指向最后调用它的那个对象

this-example

同样的意思另一个说法是:

函数最后由谁调用,this值就指向谁。

比如,下面的代码中,对象o调用了函数a,所以函数a中的this指向对象o。

const o = {
   
  a: function(){
   console.log(this)}
}
o.a() // o

好像没什么问题。一般来说,作者此时会开始“洋洋自得”地使用这条“规律”解释一个关于this值的奇怪现象:

function a(){
    
  console.log(this)
}
a() // window

作者解释:之所以输出为全局对象window,是因为此时函数a实际上是由window对象调用的,即window.a()

看到这里,你是不是觉得这是一条穿透this值本质的规律?可它实际上并不是!

第一,这条规律不适用于模块代码:

<script type="module">
  function a(){
     
    console.log(this)
  }
  a() //undefined
</script>

第二,这条规律不适用于箭头函数:

const o = {
   
  a: () => {
   console.log(this)}
}
o.a() // window

第三,这条规律不适用于严格模式的函数:

function a(){
   
  "use strict"
  console.log(this)
}
a() // undefined

第四,这条规律不适用于构造器函数:

const o = {
   
  a: function(){
   console.log(this)}
}

new o.a() // 输出即不是window、也不是o,是一个新创建的对象

第五,这条规律不适用于函数有显式绑定的情况:

const o = {
   
  a: function(){
   console.log(this)}.bind({
   c:1})
}

o.a() // {c:1}

最后,甚至,这条规律中最让作者得意的那一部分,也是错的:

let a = function(){
   console.log(this)}
console.log(window.a) // undefined
a() // window
window.a() // 报错:window.a is not a function

我只要使用let来声明函数a,函数a就不会出现在window上,此时直接调用window.a()是会报错的,但执行函数a的结果依旧输出为window。也就是说,“输出为全局对象”这个事情,跟函数是不是在被全局对象调用根本没有半毛钱关系。 实际上,这是“发明”这条规律的作者所不知道的其他机制在起作用。

也就是说,搞了这么久,这条规律正确的说法应该是:

除了模块代码、箭头函数、严格模式、new表达式、显示绑定、普通调用的情况以外,this永远指向最后调用它的那个对象!

看吧,有时候你也不能怪程序员不好找对象,他们对“永远”的理解跟正常人不太一样。


我们看不见“作用域”,所以我们对作用域的样子众说纷纭;我们看不见this值的底层解析机制,所以我们对其机制的揣测各不相同,且漏洞百出;但有时候,由于我们看不见,在某些地方我们又会达成奇怪的一致,即便是错误的一致。比如,关于执行上下文的类型。

网络上所有的技术文章,关于执行上下文的类型,或者说创建执行上下文的方式,都整齐划一,异口同声 —— 3种,就连最近号称要让前端程序员回家种

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值