一、JavaScript基础知识总结
1、JavaScript的基本介绍
JavaScript是一门编程语言,是现代前端开发必用的一种脚本语言,该语言学习起来相较容易。JavaScript可以在浏览器中运行,除此之外js作为后端编程语言时还可以在node.js中运行。
JavaScript还具有灵活,学习起来相对较为简单,可以更好的培养编写代码的人员的逻辑思维能力等特点。
2、JavaScript中重要的基础知识点
接下来,简单介绍一下个人认为JavaScript中比较重要且使用较多的基础知识点,而且接下来的内容有一部分是我在前期的学习过程中不怎么理解,容易混淆,或者是理解起来较为困难的知识点。
1)JS中的变量
变量就是内存中的一部分空间,由变量名和变量值组成。
变量名:是内存空间的别名,对变量名的操作就是对变量空间的操作
变量值:就是内存空间存储的数据
变量又分为局部变量和全局变量。
局部变量:顾名思义就是只能在部分区域或者说是只能在指定区域来使用的变量,局部变量大部分都存在在函数中,即是在函数中定义的变量;
全局变量:由上述定义对比可知全局变量则是在全局都可以使用的变量;
因此可以看出函数是局部变量与全局变量的分界点,全局变量就是在函数内外皆可访问的变量,而局部变量就是只能够在函数内部访问的变量。
2)使用 var let const 分别声明变量的异同
- 使用 var 来声明变量是最早最原始的声明函数的方法,虽然说现在已经接近淘汰的状态,但是我还是向大家稍微介绍一下;
<script>
var a = 110; //a是变量名,110是变量值;将 110 赋值给变量 a
console.log(a); //输出 110
var name = "zzz"; //当属性值是字符串是需要用双引号将其引住
console.log(name); //打印出 name 的属性值 zzz
</script>
- 使用 let 来声明变量是现在大家普遍使用的,就像是所有的程序员有个默认的规定一样,在声明变量是都使用 let 。
- 一般情况下使用 const 来声明js代码中的常量
***对于这三者在使用过程中的区别在下面讲述JS代码的编译阶段下进行讲述
3)JS中的数据类型、number数据类型以及数据类型转化
-
JS中的数据类型:基本数据的类型和引用数据类型
基本数据类型: number 数字 var a = 110; int a = 110; string 字符串 “ ” ‘ ’ JS中不分字符和字符串 都叫字符串 boolean true false 布尔类型 undefiend 没有值 null 没有值 引用数据类型: object 对象 array 数组 function 函数 在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中的数据类型转化
- 隐式数据类型转化:当一个运算符两侧的操作数类型不同时,在代码执行的过程中就悄悄地对其中一个操作数的类型进行了转化,如下列代码所示:
<script>
var res = 123 + "abc"; // 代码执行过程中将 “123” 隐式转化成 字符串
console.log(res); // 结果是 123abc
console.log(typeof res); // 查询结果 res 的数据类型位 string
</script>
<script>
var class = "true";
// if后面的小()里面要么放true 要么放false
// if后面小()里面放了一非布尔类型的值,也会进行隐式类型转化
if(class){
console.log("上课啦!!!"); // 因为 class = true, 所以输出值为 “上课啦!!!”
}
else{
console.log("没有上课~~~"); // 当 class = false 时,输出结果为“没有上课~~~”
}
</script>
注意
在JS中,下面的值转成false,其它值都转成true: 0 -0 "" undefiend null
- 强制数据类型转化
强制类型转化主要是运用代码把需要转化的操作数直接转化为需要的数据类型,例如以下几种函数:
- parseInt : 把小数或非数字转换为整数
- Number :数字型和非数字型(Number 和 NaN)
- String :把非字符型操作数转化为字符型
4)JS代码在执行时的阶段
JS代码执行分为两个阶段:每个阶段都有不同的工作
- 预编译阶段;
预编译阶段的任务就是把加var的变量进行提升,但是需要注意的是只会对变量声明进行提升,且是提升到代码段的最前面,变量的赋值是不会提升的;除此之外也会对使用function声明的函数进行提升,同样也是只提升的整个函数声明,函数体是不会提升的。
- 代码执行阶段;一行一行执行
*var let const 使用中的区别
<1> var 既可以定义变量,又可以定义常量;let 只定义变量,而 const 只定义常量;
<2> 在JS代码的预编译阶段,var 声明可以提升到代码段的最前面,而在官方说明中说 let 声明的变量没有提升(实际上 let 声明的变量也提升,仅仅是没有初始化);const 声明也不会提升;
<3> 使用 var 声明的全局变量可以存放在 GO 中,作为 window 的属性,而 使用 let 和 const 声明的变量和常量,不能够存放在 GO 中,作为 window 属性;
<4> var 声明变量时可以重复声明,而 let 和 const 声明变量和常量时不能够重复声明,示例代码如下:
<scrpit>
// 使用 var 声明变量
var a = 1;
var a = 2;
console.log(a); // a = 2;
// 使用 let 声明变量
let b = 3;
let b = 4;
console.log(b); // Identifier 'b' has already been declared
// 使用 const 声明常量
const c = 5;
const c = 6;
console.log(c); // Identifier 'c' has already been declared
</script>
5)执行上下文(Execute Context)以及详细的了解与之相关的几个结构ECStack EC(G) VO EC(f) AO GO
执行上下文就是给代码提供数据,且代码分为全局代码和函数代码。但是不管是全局代码还是局部代码,在代码执行时都会产生 EC栈,所以执行上下文分为全局执行上下文和函数执行上下文。
ECStack 执行上下文栈
EC(G):全局执行上下文,存储全局的加var,加function的数据;
VO:变量对象,存储的全局的加var 或 加function的全局数据;
EC(f):函数执行上下文,存储局部的加var 或 加funciton的局部数据;
AO:活动对象,存储局部的加var 或 加function的局部数据;
GO:全局对象
默认情况下,里面就存储了很多东西
全局的数据,加Var的全局数据,加functon的全局数据,不加var的变量都会存储到GO中。
***通过几个简单的小例子充分的了解上述内容
<script>
var a = 100; // 用 var 声明,数据分别存储在 VO 和 GO 中
b = 200; // 不使用 var ,只存储在 GO 中
console.log(a); // 100
console.log(window.a); // 100
console.log(window.b); // 200
</script>
<scrpit>
console.log(a); // a is not defined
function f() {
console.log(a); // undefine
// 只要加var 变量肯定会提升
// 加var的局部变量只会提升到函数内部最前面的
var a = 1;
console.log(a); // 1
}
</script>
二、使用JavaScript解决一些典型问题
1.
<script>
var a = 12,
b = 13,
c = 14;
function fn(a){ //此处的a是函数内部的局部变量
console.log(a, b, c); // 输出 10 13 14
a = 100;
c = 200;
console.log(a, b, c); // 输出 100 13 200
}
b = fn(10);
console.log(a, b, c); // 输出 12 undefine 200
</script>
***过程及解析
1)首先进入预编译阶段,对由var声明的变量a b c以及函数fn(a)都存储在全局执行上下文栈EC(G),并且进行提升;
2)进入到代码的执行阶段,首先对a b c进行赋值操作,因函数fn在预编译时已经进行过,所以第5-10行代码直接跳过进入第11行代码的执行操作;
3)第11行代码时调用fn函数,然后生成一个局部上下文栈EC(fn),再对形参进行赋值操作,即a = 10,再判断函数中是否有需要提升的的内容,
可以看出此函数中没有,最后进行代码的执行操作;
4)首先执行第6行代码,输出a b c的值,a的值函数体中有等于10,而 b 和 C 函数中没有,所以向父级中找,所以输出内容 “ 10 13 14”;
5)接下来执行第7行代码,找a,先在自己的EC中找,找到了,将100赋值给a;
6)再执行第8行代码,找c,现在自己的EC中找,找不到,就去父EC中找,将200赋值给父EC中的c;
7)执行第9行代码,因为函数本身的EC中有a ,所以输出结果a=100,因为函数自己的EC中没有b 和 c,所以去父EC中找,父EC中有b 和 c,
所以b=13,因此输出结果为 “100 13 200”;
8)函数调用结束,所以第11行代码执行结束,所以函数fn形成的局部EC栈被销毁,内存被回收;又因为fn没有返回值,所以b = fn(10) = undefine;
9)执行第12行代码,输出结果 “12 undefine 200”
2.
<script>
var ary = [12, 13]; // ary 是全局变量
function fn(ary){ // ary 是形参,相当于函数内部的局部变量
console.log(ary); // 输出[12,13]
ary[0] = 100; // 先找ary数组,而且这里的ary[0]指的是数组的第一个数,所以ary[0] 由12 换成 100
ary = [100]; // [100] 形成一个新的堆,然后对EC(fn)中的AO中ary所对应的堆地址进行更新
ary[0] = 0; // 修改新堆中的内容为0
console.log(ary); // 输出 [0]
}
fn(ary);
console.log(ary); // 输出 [100,13] ,EC(G)中存储的ary所对应的堆中存储的数据
</script>
3.
<script>
var i = 5; // i 是全局变量
function fn(i){
return function (n) { // return 返回地址
console.log(n+(++i));
}
}
var f = fn(1);
f(2); // 输出 4
fn(3)(4); // 输出 8
fn(5)(6); // 输出 12
fn(7); // 输出 10
console.log(i); //输出 5
</script>
三、经验总结
- 牢记代码执行的步骤,切不可跳步思考,非常容易出错;
- 分析代码在执行的过程中返回值到底是什么,赋值操作到底把值赋给了哪一个变量;
- 相对较繁琐的函数调用操作一般情况下前后调用的都不是一个函数;
- 部分情况下会输出函数体。