《JavaScript高级程序设计》__ 语言基础(上)(1)

简介: 前言大家好,我是HoMeTown,web领域有一本神书大家应该都有看过,这本书我看过两遍,但是每次看都是粗粗的略过一些重要的知识点,甚至一些面试过程中的问题,在这本书里都能找到答案。

前言

大家好,我是HoMeTown,web领域有一本神书大家应该都有看过,这本书我看过两遍,但是每次看都是粗粗的略过一些重要的知识点,甚至一些面试过程中的问题,在这本书里都能找到答案。

工作这么多年,到现在为止对这本书都没有一个系统的知识点记录,这次想从头读一遍这一本JavaScript高级程序设计【第4版】,并把重要的知识点记录下来,同时加上自己的见解,这也是我第一次在掘金上记录分享读书笔记,共勉之!

关注专栏,一起学习吧~

区分大小写

在JavaScript中,一切都区分大小写,testTest是两个不同的变量

标识符

标识符可以由一个或多个字符组成,字符包含:

  • 第一个字符必须是一个字母,下划线_ 或者美元符号$
  • 其他的字符可以使字母、下划线、美元符号、数字

严格模式

strict modeECMAScript3的一些不规范写法会被处理,使用方法是 在脚本开头写上"use strict"

也可以指定一个函数下执行严格模式:

function func() {
  "use strict"
}
复制代码

严格模式下,这些操作是不被允许的:

"use strict";
// 不能给未声明的变量赋值,则会导致抛出 ReferenceError
function func1() {
  message = "hometown";
  console.log(message); //  Uncaught ReferenceError: message is not defined
}
// 不能定义名为 eval 和 arguments 的变量,否则会导致语法错误
function func2() {
  var eval = "1"; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode
}
func2();
// 不允许使用八进制的数字字面量
const num = 070
...持续更新...
复制代码

变量

var 声明变量

变量声明

var message = 'HoMeTown'
// ---- 也可以这样 ----
message = "HoMeTown" // 合法但不推荐
复制代码

作用域

在一个函数内声明变量,改变了会成为函数内部的局部变量,调用函数时,声明变量并赋值,随着函数执行完毕,该变量会被立即销毁,无法再次访问:

function func() {
  var message = "hometown";
  console.log(message); // hometown
}
func();
console.log(message); //var.html:16 Uncaught ReferenceError: message is not defined
复制代码

如果去掉前面的关键字var,那么变量message就会变成全局变量,外部也可以访问:

function func() {
  message = "hometown";
  console.log(message); // hometown
}
func();
console.log(message); // hometown
复制代码

两者的区别其实就在于,后者会直接在全局对象window上创建一个属性message,但是前者不会。

前者:

console.log(window.message) // undefined
复制代码

后者:

console.log(window.message) // hometown
复制代码

后者的写法也就等价于window.message = "hometown"

虽然可以通过省略var 操作符定义全局变量,但不推荐这么做。在局部作用域中定

义的全局变量很难维护,也会造成困惑。这是因为不能一下子断定省略 var 是不是有意而

为之。在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出 ReferenceError。

声明提升

一般情况下,直接使用一个未声明的变了,会抛出错误,像这样:

function func() {
  console.log(message); // var.html:30 Uncaught ReferenceError: message is not defined
}
func();
复制代码

使用var声明变量时,即使在变量使用之后才声明,也不会报错,像下面这段代码:

function func() {
  console.log(message); // undefined
  var message = "hometown";
}
func();
复制代码

这就是var声明变量提升(hoist)。也就是先把所有var声明全部都拉到顶部,然后再在当前位置赋值,例如上面的这段代码,可以理解为:

function func() {
  var message;
  console.log(message)
  message = "hometown"
}
func()
复制代码

这个东西前几年面试还问的挺多的。

let 声明变量

变量声明

let声明变量与var一样

let message = "hometown";
复制代码

块级作用域

letvar有一个显著的区别就是,let声明变量具有块级作用域,而var函数作用域,举一个例子:

if (true) {
  var message = "hometown";
  console.log(message); // hometown
}
console.log(message); // hometown
if (true) {
  let age = 18;
  console.log(age); // 18
}
console.log(age); // Uncaught ReferenceError: age is not defined
复制代码

上面这个例子中,可以看到age在if块之外使用时,抛出了异常,age 变量之所以不能在 if 块外部被引用,是因为它的作用域仅限于该块内部。块作用域函数作用域子集,因此适用于 var 的作用域限制同样也适用于 let。

不能重复声明

同一个块级作用域中不能使用let重复声明变量,只要这个变量之前声明过,就不行:

function func() {
  var message = "hometown";
  var message = "hometown";
  console.log(message); // hometown
}
func();
function func2() {
  // var age = 18; // Cannot redeclare block-scoped variable 'age'.
  let age = 18; // Cannot redeclare block-scoped variable 'age'.
  let age = 22; // Uncaught SyntaxError: Identifier 'age' has already been declared
  console.log(age);
}
func2();
复制代码

如果不在同一块级作用域,是可以声明的:

function func() {
  let message = "hometown";
  if (true) {
    let message = "inside";
    console.log(message); // hometown
  }
  console.log(message); // inside
}
func();
复制代码

其实这样也不行:

let age = 18; // Cannot redeclare block-scoped variable 'age'
var age = 22; // Uncaught SyntaxError: Identifier 'age' has already been declared
复制代码

因为这样varhoist,其实就等价于:

var age;
let age = 18;
age = 22;
复制代码

所以在使用let声明变量时,一定要确保当前块级作用域没有声明过该变量

暂时性死区

let声明的变量不会像var那样变量提升!也就意味着,只要在使用变量的时候,这个变量没有被声明过,那就会报错!即使你在后面声明了该变量:

console.log(message); // undefined
var message = "hometown";
console.log(age); // let.html:60 Uncaught ReferenceError: Cannot access 'age' before initialization
let age = 18;
复制代码

变量提升反过来对应的就是暂时性死区,我觉得变量提升简直就是个设计缺陷,书中也讲到,尽量避免使用var,多使用let & const,鬼知道使用var时不时的会带来一些什么问题。

不会添加到window

使用var在全局声明变量时,会直接在window上添加属性,使用let声明则不会:

var message = "hometown";
console.log(window.message); // hometown
let age = 18;
console.log(window.age); // undefined
复制代码

for循环中的let

let出现之前,for循环中的迭代变量会渗透到循环外部:

for (var i = 0; i < 5; i++) {}
console.log(i); // 5
复制代码

改为let以后,这个问题就好了:

for(let i = 0; i < 5; i++) {}
console.log(i); // Uncaught ReferenceError: i is not defined
复制代码

还有一个面试常见问题:

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0);
}
// 结果: 5 5 5 5 5
复制代码

这块其实有点涉及时间循环与宏任务的知识,for循环作为一个同步任务开始执行,然后循环产生一个宏任务setTimeout,循环完毕之后,此时用var声明的全局的i已经自增到了5,我们可以打个debugger,然后再Sources中看到,此时i在这个地方:

网络异常,图片无法展示
|

值为下图:

网络异常,图片无法展示
|

当我们修改为let声明后,我们可以看到:

网络异常,图片无法展示
|

每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。

const声明变量

与let一样,不同点是,const声明的变量不能被修改。

变量声明的最佳实践

  • 不使用var
  • const优先、let次之

数据类型

Js中有6中数据类型简单数据类型:

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Symbol

后面好像还加了一个BigInt

typeof

typeof null // object
复制代码

因为null在js中被认为是一个空对象的引用。

undefined

undefined只有一个特殊值undefined,当声明了一个变量,未对其进行初始化时,默认为undefined

但是不能直接使用未声明的变量:

let message;
console.log(message); // undefined
console.log(age); // 报错
复制代码

有趣的是,typeof undefined === undefinedtypeof 未定义的值 === undefined:

let message;
console.log(typeof message, typeof age) // undefined undefined
复制代码

所以,尽量在定义变量的时候进行初始化,这样进行typeof操作时,只要返回undefined就知道,这个变量一定是没有什么过,而不是声明了没有赋值。

null

null也只有一个特殊值null,null表示一个空对象指针,这也是为什么typeof null === 'object'的原因。

书中建议使用 null 初始化一个对象,比如:

let obj = null
复制代码

但是这个规则好像到typescript中不太奏效。因为在TypeScript中完全校验值的类型,null 与 {} 是两种不同的类型。

undefined是由null派生的,所以undefined == null返回true, 但是undefined === null返回false

书中推荐,永远不要给一个值赋值undefined,这样我们一旦遇到undefined就立马可以知道这个变量没有声明。可以给一个值赋值null,证明他是一个假值,一个空对象指针。

Boolean

Boolean值的可选值:true、false。

在JavaScript中,true不全等与1,false不全等于1。

Number

JavaScript采用的是IEEE 754格式表示整数和浮点数(双精度)。

整数

最基本的即十进制表示:

let num = 100
复制代码

八进制字面量的第一个数字必须是0

let num = 070 // 八进制的56
复制代码

如果后面的值超出了数值0 ~ 7,默认省去0,按照十进制处理:

let num = 079 // 按照十进制的79处理
复制代码

十六进制的字面量必须以0x开头:

let num = 0xA // 十六进制的10
复制代码


目录
相关文章
|
7月前
|
JavaScript 前端开发 Java
JavaScript语言
JavaScript语言
41 1
|
1月前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
52 4
|
2月前
|
XML 监控 JavaScript
JavaScript 语言对企业上网监控的技术支持
在数字化企业环境中,上网监控对企业信息安全和提升员工效率至关重要。JavaScript 作为广泛应用的脚本语言,提供了强大的技术支持,包括数据获取与分析、与服务器端交互、监控页面加载时间和网络活动,助力企业有效管理上网行为,保障信息安全。
33 6
|
4月前
|
Web App开发 JavaScript 前端开发
Node.js与Go语言的对比?
【8月更文挑战第4天】Node.js与Go语言的对比?
422 3
|
4月前
|
自然语言处理 JavaScript 前端开发
【走向世界】Vue.js国际化:打造无国界应用,让爱与信息跨越语言的边界!
【8月更文挑战第30天】本文详细介绍了Vue.js中实现国际化的多种方法及最佳实践。通过使用`vue-i18n`等第三方库,开发者能够轻松地为应用添加多语言支持,优化用户体验并扩大市场覆盖范围。文章涵盖从基本配置、动态加载语言包到考虑文化差异等方面的内容,帮助读者构建真正全球化且无缝多语言体验的应用程序。
133 0
|
4月前
|
JavaScript 前端开发 UED
探索JavaScript的历史:网络需求初现、语言创立与标准化的旅程
探索JavaScript的历史:网络需求初现、语言创立与标准化的旅程
|
6月前
|
JavaScript 前端开发 编译器
ECMAScript与JavaScript:一场语言的邂逅
ECMAScript与JavaScript:一场语言的邂逅
|
7月前
|
JavaScript 测试技术
【sgGoogleTranslate】自定义组件:基于Vue.js用谷歌Google Translate翻译插件实现网站多国语言开发
【sgGoogleTranslate】自定义组件:基于Vue.js用谷歌Google Translate翻译插件实现网站多国语言开发
|
6月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的java语言的考试信息报名系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的java语言的考试信息报名系统附带文章源码部署视频讲解等
57 0
|
7月前
|
JavaScript 前端开发 Java
javascript是弱类型语言,一个函数参数可以接收不同类型的变量作为它的该参数
javascript是弱类型语言,一个函数参数可以接收不同类型的变量作为它的该参数
63 0