第一天
1.什么是js
JS:javascript 简称JS它是一门编程语言
前面学习了html 和html,css对比
- 相同:html,css,js代码都可以在浏览器中运行,html,css,js它们的运行环境是浏览器
- 不同点:html,css 不叫编程语言 js是编程语言 js的运行环境不只浏览器,还可以在其它的环境中支行。
js能做什么:
1)开发网站
2)开发app
3)小程序
4)游戏开发 小程序游戏 网页游戏
5)写后端 node.js
6)嵌入式 c
7)区块链
JS的三种写法
- 把js写在html文件中 学习时通常就写在script标签中(内部写法)
- 把JS写在JS文件中,然后在html文件中通过script标签引入 写项目时通常会把JS写在一个单独的文件中(外部写法)
- 把JS代码写在开始标签中,当成开始标签的属性(行内写法)
基本语法
1)JS是区分大小写 var a = 1; var A = 2;
2)忽略空白符(空格 换行 tab)
3)语句分号可加可不加
4)注释 单行注释 多行注释 注释是给程序员看的
5)标识符 和 关键字 var a = 110; var 关键字 a变量名标识符
注释
// 单行注释
/* 多行注释 */
2.js中的变量
数据:一个软件打开后,界面上有很多的数据,也叫状态,这个状态可以保存在
两个地方,一个是内存,一个是硬盘
项目运行起来,只有把数据加载到内存中,才使用使用数据。
内存中的数据,一断电,数据就没了,
还有一个地方,也可以保存数据,是硬盘,硬盘上的数据断电是不会丢失的。
内存空间地址:就是一块内存空间的地址,可以通过地址操作它对应空间中的数据,使用地址来操作非常不方便,指针。
个人理解:变量的含义是一个用来储存数据的杯子 并不是数值本身 如果杯子里面没有水 就会打印出undefined 如果没有杯子就会报错 找不到杯子 杯子就是内存中的一个空间
如何定义变量
我们想要使用变量 第一步我们就要声明变量
声明变量的语法是var ,let ,const通常用来定义一个常量(不会改变的量)
var 会涉及到提升
提升的理解:
(预编译过程)
全局代码中: 提升到代码段最前面
加var的全局变量 仅仅提升是声明
加functon的全局函数 提升的是定义 定义 = 声明+赋值
函数内部:
加var的也会提升,提升到函数体的最前面
var,和let的区别
1.let声明的变量没有提升
console.log(a);
let a = 110; // Cannot access 'a' before initialization
2)let 配合 {} 也可以形成块级作用域
// if(true){
// var a = 110; // 全局变量
// // b只能在当前的{}中被访问到 出了块就访问不了
// let b = 666; // let + {} 形成块级作用域
// }
// console.log(b); // b is not defined
3)使用let声明的变量不会挂载到GO(全局对象)上
// let a = 110;
// console.log(window.a); // undefined 访问一个对象上没有属性,得到的就是und
4)使用let不能重复声明
// let a = 1;
// let a = 2;
// console.log(a); // Identifier 'a' has already been declared
项目中用let声明变量 不要用var来定义变量 const和let的一样 但cinst必须赋值 用来声明常量
变量的分类:
1)全局变量
2)局部变量
分界点是:函数
只要把变量写在函数里面就是局部变量,只要写在函数外面就是全局变量。
全局变量和局部变量有什么特点:
1)全局变量可以在函数内部都能访问到
2)局部变量只能在函数内部访问到
3.js中的数据类型
由于学习了变量 为了更加合理使用内存空间 基本上所有的编程语言中都提出数据类型的概念,研究针对不同的数据,分配不同的空间(个人理解杯子大小)
JS中的数据类型
基本数据类型
number:数字
1)number是一个数据类型,这个数据类型对应的值有无数个。
2)在JS中number数据类型是不分整数和小数 都是number
3)可以通过typeof查看一个变量值的数据类型
4)最大值 和 最小值
5)number可以通过不同进制显示 进制 10进制 2进制 16进制 8进制
6)NaN Not a Number 不是一个数字
7)JS中不要对小数运算 要运算先转成整数
在JS中,说到数据类型,主要指变量值的数据类型。
string:字符串
1)在JS中 使用‘’ “”把字符串包起来 不包 JS会给它当成变量
2)单引号不要嵌套单引号 双引号不要嵌套双引号 外单内双 外双内单
3)string数据类型对应的数据有无数个
SyntaxError表示语法错误
boolean 有两个值true false 布尔类型
1)boolean数据类型对应的值就两个 true false
2)true 和 True 不一样的 JS是区分大小写的
<script>
var b = true;
console.log(b);
console.log(typeof b);
</script>
undefiend 没有值
1)undefiend是一个数据类型,这种数据类型对应的值是undefiend
2)什么时候会出现undeined?
答:一个变量没有赋值 它的值是undefiend 这个值的类型是undefiend
null 没有值
引用数据类型
object 对象
array 数组
function 函数
隐式类型转化
+ 叫运算符 123操作数 "abc"也叫操作数
一个运算符如果有两个操作数,这个运算符叫二元运算符,也叫二目运算符,还叫双目运算符
/+ - =
如果一个运算符只有一个操作数,这个运算符叫一元,单目运算符
有:typeof ++ --
+是双元运算符 运算符你要保证两侧操作数的数据类型要一致
// var res = 123 + "abc"; // 123隐式转化成字符串
// console.log(res); // 123abc
// console.log(typeof res); // string
强制类型转化
/* console.log(parseInt(3.14)); // 把小数转成整数
console.log(parseInt("3.14abc")); // 尝试把小数或非数字转成整数
console.log(parseFloat(3))
console.log(parseFloat("3.14abc"))
console.log(Number("abc123")) // NaN
console.log(Number("123abc")) // NaN
console.log(Number("123")) // 123
console.log(String(123456)) // 123456*/
第二天
内存空间的理解
因为JavaScript具有自动垃圾回收机制 所以内存空间的概念在JS的学习中非常重要
1. 栈和堆
我们可以粗浅的理解为JavaScript的所有数据都保存在堆内存中。JavaScript的执行上下文执行上下文在逻辑上实现了堆栈。因此理解堆栈数据结构的原理与特点任然十分重要。
ECStack 执行上下文栈
EC(G) 全局执行上下文 存储全局的加var 加function的数据存储在VO中
VO 变量对象 存储的全局的加var 或 加function
EC(f) 函数执行上下文 存储局部的加var 或 加funciton的局部数据
AO 活动对象 存储局部的加var 或 加function的局部数据
GO 全局对象
默认情况下,里面就存储了很多东西
全局的数据,加Var的全局数据,加functon的全局数据,不加var的变量都会存储到GO中的
引用数据类型与堆内存
js的应用数据类型 他们的值大小是固定不变的 应用数据类型的值保存在堆内存的对象 我们不能直接访问操作对象堆中的对象 实际上是操作对象的引用而不是实际对象 我们可以理解为保存在变量中的一个地址
习题1
var a = 12;
var b = a;
b = 13;
console.log(a);
变量对象中的数据发生了复制行为 a b的值都等于12 但是他们互相独立不影响 所以我们修改b的值a并不会变化 所以输出12
习题2
var a = {n:12};
var b = a;
b.n = 13;
console.log(a.n);
习题2 中的变量对象也发生了复制行为 但是数组储存在堆中 a只是把引用的地址复制给了b 他们两地址一样引用一样的堆数据 所以改变b中n的值其实是改变公用堆中的数据 所以a的值也变成了13
习题
var a = { m:666 };
var b = a;
b = { m:888 };
console.log(a.m);
习题三和习题二一样 ab引用了同一个地址 但是 b = { m:888 };开辟了一个新的堆 把新堆的地址赋给B 所以ab他们地址不同 数值不影响 所以a的值不变
除此之外,我们还可以以此为基础,一步一步的理解JavaScript的执行上下文,作用域链,闭包,等重要概念。
初步理解栈内存
内存空间管理
因为JavaScript具有自动垃圾收集机制,所以我们在开发时好像不用关心内存的使用问题,内存的分配与回收都完全实现了自动管理。但是根据我自己的开发经验,了解内存机制有助于自己清晰的认识到自己写的代码在执行过程中发生过什么,从而写出性能更加优秀的代码。因此关心内存是一件非常重要的事情。
2.对执行上下文的理解
代码进行完预编译 函数执行代码 时就会进入一个执行上下文 EC(f) 有多少个函数执行就会产生多少个EC(f) 内存空间有限 执行完就会自动出栈(从ECstack中退出销毁)所以栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。所以先进去的后才来
初步了解ECStack
单线程
同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
全局上下文只有唯一的一个,它在浏览器关闭时出栈
函数的执行上下文的个数没有限制
每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
练习案例
对变量对象VO及活动对象AO的理解
变量对象的创建
建立对象 检查当前上下文中的参数 建立属性和属性值
检查当前上下文的函数声明 (function)变量对象以函数名建立属性 属性值指向该函数所在的内存地址的引用 如果存在会被覆盖
检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改
未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象(也就是AO),所在函数执行完毕 所在EC中的AO也被销毁
第三天
对闭包的初步认知
函数对执行上下文 按理会出栈销毁 但是有些函数里面 嵌套一个函数 并且里面的函数引用外部的变量 所以就形成了一个无法被释放的栈空间 这个无法被释放的栈空间 我们叫他为闭包
好处:延长了一个局部变量的生命周期
坏处:占用了内存空间 可能造成了内存泄漏
习题
var i = 5;
function fn(i) {
return function (n) {
console.log(n+(++i));
}
}
var f = fn(1);
f(2);
fn(3)(4);
fn(5)(6);
f(7);
console.log(i);
因为函数f()中在引用函数fn所以fn形成了一个闭包 调用fn()函数fn把返回值赋给f()
所以第一次输出4 因为fn()的EC栈里有i 值为2 所以2+2=4 因为fn(3)又重新调用了函数fn 所以产生了一个新的ec(f)并且里面i值为4
所以第二次输出8 同理 第三次输出12 由于前两次产生的ec都以出栈并消耗 调用f(7)时只能去父EC中找i 他的值为3 所以第三次输出10 但是全局的i没有收到影响 第四次输出5 指导程序执行完毕 闭包fn()才被销毁
作用域链:像上述所说的一样 描述找数据的链 就是作用域链 数据的查找机制–(保证了当前执行环境对符合访问权限的变量和函数的有序访问。)