大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: ES进阶 修改讲义

为了解决JavaScript作用域的问题(复习),JavaScript开发人员煞费苦心:


名称空间(namespace)

其他成熟的工程化语言内置了名称空间(namespace)来解决这个问题,比如:

//虽然都是“源栈”,但他们显然是不一样的:
China.Chongqing.Luckystack
China.Bejin.Luckystack
US.NewYork.Luckstack

JavaScript只能模拟:

  1. 先定义一个唯一的全局变量(对象)
  2. 其他变量都写出是上述全局变量的成员(属性和方法)
  3. 还可以多层嵌套,最后形成名称空间一样的“样式”
var China = {};
China.Chongqing = {};
China.Chongqing.LuckyStack = {};
China.Chongqing.LuckyStack.wpz = function () {

JQuery等类库就是这样做的。(演示)


IIFE

全称:立即执行函数表达式(Immidiately Invoked Function Expression)

简单理解就是:声明一个匿名函数,然后立即就执行它。

所以语法:(function([parameter,...]){})([parameter,...]);

  1. ()就会让前面的函数立即执行
  2. 为什么需要把被执行函数包裹起来?
    • JavaScript解释器(ES标准)认为 function 关键字开头的语句是函数声明,后面就必须接函数名字(否则抛出一个语法错误信息);
    • 但只要function关键字前面有其他符号,解释器就会认为这是一个(可以匿名的)函数表达式
    • ()能够“欺骗”解释器,而且更有意义,^_^ (你用~/-啥的都行的)
演示:执行效果
    buttons[i].onclick = (function (i) {
        return function () {
            console.log('inner i = ', i);
            document.getElementsByTagName('h2')[0].style.color = buttons[i].style.color;
        }
    })(i);

这样,在每次事件绑定的时候,就把 i 值给“固定”了。此后事件触发的时候,就能够取到这时候被固定的 i 值。(实际上是利用IIFE形成了另外一个闭包)

此外,IIEF还可以用于:

  • 形成一个单独的作用域(模拟块级作用域 let )
  • 避免全局变量“污染”
演示:JQuery中IIEF的使用



strict模式(ES5)

把我们之前的代码稍作改动:

function scope() {
    /*var*/ sname = '李志博';   //注释掉var
@猜一猜@:会有什么结果?


如果在JavaScript的函数中声明变量,不使用var,该变量就具有全局作用域!——特性超级坑爹的一个“特性(bug)”,尤其是在代码review的时候,你根本不知道这是在:

  • 使用一个已声明的变量,还是
  • 要声明一个全局变量

所以从ES5开始,JavaScript就引入了所谓的“严格”模式,在代码顶部添加一行:

'use strict';  -- 如果浏览器不支持?

使用严格模式,就能强制JavaScript声明变量时必须使用:var;否则会报错。(以及其他约束)

演示:调试窗口报错


官方推荐总是使用严格模式。但是,如果

  • a.js 文件上声明了'use strict'
  • b.js 文件没有声明'use strict'且没有按照严格模式书写代码

  • 在html文件中先引用了a.js,然后再引用了 b.js

@想一想@:会出现什么情况?

所以,更多时候,我们不得不把'use strict'声明在函数顶部。




块级作用域(ES6)

JavaScript直到ES6,才引入了关键字:let。由let声明的变量,具有“块级(block)”作用域(同:C#等主流语法):

变量的作用域从声明开始,直到声明所在的花括号结尾为止。

不考虑兼容性,推荐总是使用 let!let的优势很多:

  • 没有什么变量提升,必须“先声明后使用”:
    {
        console.log(a);
        let/*var*/ a = 10;
    }
  • 声明的全局变量不会“挂”到window变量上

    


复习:变量和赋值

注意:命名规则,但是

  • 慎用$和_开头,他们已被JQuery和Underscore“占用”(增加可读性,避免无意义联想)
  • 不能使用关键字(比如let),但是,有些关键字不报错?(比如:NaN)……ʅ(‾◡◝)ʃ,这就是JavaScript
  • 推荐使用驼峰命名法(studentName/StudentName)

ES6中还引入了let和const


使用未声明的变量会报错:

alert(xyz);   //xyz从未声明

控制台中可以查看(注意:JavaScript的错误不会报到页面!)

此外,理论上变量应该“先声明,后使用”。

但是,JavaScript中有一个特性(其实是bug):


变量提升

即:JavaScript在执行时会把所有的变量声明(注意:仅仅是声明)提升到代码的顶部。

所有,如下代码不会报错,但是

alert(greet);
let greet = "源栈欢迎你!";  
其实等同于:
let greet;
alert(greet);
greet = "源栈欢迎你!";  

注意:提升的只是greet的声明,greet的赋值没有提升。

而JavaScript会在变量声明的时候默认给它赋一个undefined的值。所以使用未赋值的变量不会报错,只是其值为undefined!

@想一想@:JavaScript运行时究竟是:

  1. 预先扫描整个函数体的语句,把所有声明的变量“提升”到函数顶部。
  2. 还是碰到了未声明的变量就去代码中去查找呢?
理解:语法规则 vs 具体执行


这个特性是超级坑(error prone)的,会给我们代码的调试带来非常多的麻烦!

所以,和其他语言(如C#)不同,JavaScript不提倡“就近声明”,而是提倡“(用一个let)在代码顶部声明全部变量”:

let greet = "源栈欢迎你!",
    name,
    age;  //可以一次声明多个变量
这样可以:
  • 增加代码的可读性
  • 避免变量名冲突 (JavaScript同名变量名不会报错哟!)

换行:

  • 规范的要求是:行尾用分号(;)标识
  • 如果行尾没有分号,换行符会被JavaScript检查,视情形添加;或忽略(正确与否看人品……)
所以,我们总是要求行尾写上分号!

知识的另外一个作用:方便调试:养成习惯,先注释最后删除




ESlint

因为Javascript语言本身:

  • 可以允许非常多的“不规范”的写法
  • 没有编译时的错误检查

ESLint插件应运而生,广受欢迎。在VS2019中已经内置了ESLint检测,在书写JavaScript时会给我们错误提示(红波浪线)和警告(绿波浪线)。

检查是否开启:

  1. 打开 Tools > Options,导航到 Text Editor > JavaScript/TypeScript
  2. 启用Code validation:
  3. 激活Linting

演示:警告和错误



所有ES6新语法,默认use strict。


常量

const version = '1.0' 
关键字:const,语法特点:
  • 声明的同时必须赋值
  • 一旦赋值,就不能更改
  • 和let一样,块级作用域+严格模式

控制台演示:错误信息

注意:常量只是不能改变常量的“引用”,即:比如常量指向一个对象……

const zdh = { name : "周丁浩" }


@想一想:能不能修改 zdh.name='zdh'; ?


作业

设置一个常量password,保存你的密码

一起帮用户被分为5种,每种都有一个整数代号:

  • 0:访客
  • 1:注册用户
  • 2:文章发布者
  • 3:管理员
  • 4:超级管理员
写一段代码,用switch...case将代号转换成文字输出,但3和4都统称“管理员”即可



学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 ES进阶 中继续学习:

上一课: 已经是第一课了……

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码