变量和变量声明提升
变量的命名规范
以字母、数字、下划线、美元符号构成,不能以数字开头,不能是关键字和保留字。
// 合法变量
var a;
var a_;
var _;
var $000;
// 非法变量
var 2year;
var function;
var a@3;
变量的默认值
变量默认值是undefined
var a;
console.log(a); // undefined
var a, b, c = 4;
console.log(a); // undefined
console.log(b); // undefined
console.log(c); // 4
变量声明提升
JS有一个独一无二的“预解析”阶段。在预解析阶段,会提升所有变量声明。
注意:只提升声明,不提升值。
console.log(a); // undefined
var a = 10;
注意,面试题会结合变量作用域考察。
var m = 5;
function fun() {
console.log(m); // undefined
var m = 3;
}
fun();
注意,提升无视if语句
var a = true;
function fun() {
if (!a) {
var a = 3;
}
console.log(a);
}
fun();
注意,if语句会在预解析阶段被无视。所以a此时是局部变量。
当程序执行时,if语句判断的是if(!undefined),所以if语句能够执行,a此时被赋值为3。程序正确答案是输出3。
运算符
数学运算符
+、-、*、/、%
== 是不带类型判断相等,有隐式转换
console.log(5 == '5'); // true
console.log(null == undefined); // true
console.log('1' == true); // true
逻辑运算符
!、&&、||
短路计算:口诀“借假钱,花真钱”
console.log(3 && 8); // 8
console.log(0 && 8); // 0
console.log(undefined && 8); // undefined
console.log(null && 8); // null
console.log(3 || 8); // 3
console.log(0 || 8); // 8
console.log(undefined || null); // null
console.log('' || 'ABC'); // 'ABC'
注意,JS中没有连比,就是判断一个变量a是否介于18到70之间,正确写法:
a >= 18 && a <= 70
赋值运算符
JS中,一个等号表示赋值,不是表示相等。表示相等用==运算符。等号=是赋值。
- += 是累加器那个符号;
- *= 是累乘器那个符号。
重点是a++和++a的区别。给大家一个面试题:
var a = 0;
while(++a < 3) {
console.log(a);
}
和
var a = 0;
while(a++ < 3) {
console.log(a);
}
有什么区别?
讲解:
整体而言,第一段程序是先自增1再比;第二段程序,是先比再增1。
第一段程序:
while(++a < 3),此时会先把a进行加1操作,然后和3进行比较大小。
也就是说,a一开始是0。然后此时++a让a变为1,1和3进行了比较,确实1小于3,所以输出1;
然后此时++a让a变为2,2和3进行了比较,确实2小于3,所以输出2;
然后此时++a让a变为3,3和3进行了比较,3不满足小于3,所以不进入循环体了。
第二段程序:
var a = 0;
while(a++ < 3) {
console.log(a);
}
while(a++ < 3),此时会先把a和3进行比较大小,然后自增1。
也就是说,a一开始是0。先比再增1。0和3进行了比较,确实0小于3,然后a++让a变为1,所以输出1;
然后,先比再增1。1和3进行了比较,确实1小于3,然后a++让a变为2,所以输出2;
然后,先比再增1。2和3进行了比较,确实2小于3,然后a++让a变为3,所以输出3;
还有这一题:
var a = 3;
var b = 4;
console.log(a++ + ++a + b++ + ++b);
是3、5、4、6相加,答案是18。
类型
JS类型划分
基本类型值:number、string、boolean、undefined、null
引用类型值:数组、对象、函数、正则表达式、DOM对象
typeof的检测特殊结果
记住这些:
- 注意
typeof undefined值是 undefined 。 - 注意
typeof null值是 object 。人认为null不是对象,是基本类型值,是null类型。 - 注意 typeof
[]值是 object 。
这里有一个引申问题,就是如何判断一个值是数组,不能用typeof判断值是不是数组。因为typeof检测
结果是object。怎么办?用 Array.isArray() 。
Array.isArray([]) // true
Array.isArray([3, 4, 5, 6]) // true
Array.isArray({a: 1}) // false
隐式转换
隐式转换,记忆规律,还不如记忆特殊情况。
隐式转换底层调用的是Number、String、Boolean。
其他东西转为数字
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number('3年') // NaN
Number('123') // 123
意义:做数学运算的时候,能够进行隐式转换
console.log(1 + true); // 2
console.log(1 + null); // 1,因为null会被转为0
console.log(1 + undefined); // NaN
其他东西转为布尔
Boolean(undefined) // false
Boolean(NaN) // false
Boolean(1) // true
Boolean(6) // true
Boolean(0) // false
Boolean('') // false
Boolean('false') // true,只要字符串不是空,就是true
意义:if语句的圆括号中,会用Boolean隐式转换
if (6) { // 能进入 }
if ('') { // 不能进入 }
if ('a') { // 能进入 }
if (undefined) { // 不能进入 }
判等规则
不总结,记忆特殊值即可。
0 == false // true
1 == true // true
6 == true // false
undefined == null // true
NaN == NaN // false,记住,NaN不自等。
'1' == true // true,对于字符串来说,只有'1'能等于true,别的字符串都不等于true
'0' == false // true,对于字符串来说,只有'1'能等于false,别的字符串都不等于true
if语句
跳楼现象
if语句满足一个分支,就不再执行其他分支了,就退出if语句体了。
var a = 3;
if (a == 3) {
a++;
} else if (a == 4) {
a++;
}
console.log(a); // 4
单行if可以省略大括号
注意,如果if语句没有else,也没有else if。并且if的大括号中,只有一条语句,可以省略大括号。
比如:
var a = 3;
if (a > 2) {
console.log('你好');
}
等价于:
var a = 3;
if (a > 2) console.log('你好');
如果语句多于1条。就不能用省略大括号这个技巧。比如:
var a = 3;
if (a > 444) console.log('你好'); console.log('哈哈哈');
程序运行结果是输出哈哈哈。因为JS认为,只有 console.log('你好'); 被if语句限制了。
即,只有一条语句能够省略大括号。
switch-case语句
注意case穿透。
var a = 2;
switch (a) {
case 1:
console.log('A');
case 2:
console.log('B');
case 3:
console.log('C');
case 4:
console.log('D');
break;
case 5:
console.log('E');
}
输出B、C、D。因为case穿透。如果一个满足了,那么会无条件执行后续所有的。直到遇见break。
循环语句
for循环
现在,语法已经不是问题了。 for(var i = 0; i < arr.length; i++) 已经能够嗷嗷写了。
最大的就是就是算法题目了。
算法:累加器
基本累加器
最基本的计算1+2+3+……+100已经算是基本题目了。
var sum = 0;
for (var i = 1; i <= 100; i++) {
sum += i;
}
console.log(sum);
不是基本题目的累加器有哪些呢?
车厢法
车厢法,真谛:寻找后一项和前一项的关系。
比如题目:
计算2+22+222+2222+……+2222222
答案:
var sum = 0;
var temp = 0;
for (var i = 1; i <= 7; i++) {
temp = temp * 10 + 2;
sum += temp;
}
console.log(sum);
比如题目:
答案:
var sum = 0;
var temp = 1;
for (var i = 1; i <= 3; i++) {
temp = temp * (i / (i + 1))
sum += temp;
}
console.log(sum);
i / i + 1 法
比如题目:
计算1/2 + 2/3 + 3/4 …… + 9/10
此时用i/i+1法。
// 1/2 + 2/3 + 3/4 …… + 9/10
var sum = 0;
for (var i = 1; i <= 9; i++) {
sum += i / (i + 1);
}
console.log(sum);
算法:穷举法
列出来,一个一个试验,看看是不是满足条件。满足就输出。
输出1~100所有“逆数”大于它本身的数。比如35,它的逆数是53。再比如67,逆数就是76。6的逆数就是6。70的逆数就是7。
还记得以前学习的“我爱JS”变为“SJ爱我”。思路就是字符串,变为数组,逆序,变为字符串。
for(var i = 1; i <= 100; i++) {
var nishu = Number(String(i).split('').reverse().join(''));
if (nishu > i) {
console.log(i);
}
}
while循环
没有范围的时候,用while循环。
寻找最小的,除以3余1 除5余2 除7余4 除13余6的数字
答案:
var n = 0;
while (true) {
n++;
if (n % 3 == 1 && n % 5 == 2 && n % 7 == 4 && n % 13 == 6) {
console.log(n);
break;
}
}
要记住,while(true)非常好用,结合break使用。二阶段考试题,不会提示你用什么循环。但是,只要
你看见“寻找最小的”这样的字眼,就是while循环。因为你不知道范围。
数组
数组的方法
必须死记硬背:
变异方法,就是会改变数组的:
push()
pop()
unshift()
shift()
splice()
reverse()
sort()
非变异方法:
slice()
join()
includes()
indexOf()
数组的常见算法
记住,所有关于数组的算法,一定要遍历数组的每一项。没有例外!
数组,生来为遍历!
for(var i = 0 ; i < arr.length; i++) {
}
数组求和:
var arr = [3, 4, 6, 2, 1, 3];
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
console.log(sum);
数组求最大值:
var arr = [3, 4, 6, 2, 1, 3];
var max = arr[1];
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
console.log(max);
数组去重:
var arr = [3, 4, 6, 6, 6, 6, 2, 1, 3];
var result = [];
for (var i = 0; i < arr.length; i++) {
if (!result.includes(arr[i])) {
result.push(arr[i]);
}
}
console.log(result);
数组交集:
var arr1 = [3, 4, 6, 2, 1];
var arr2 = [5, 3, 9, 6];
var result = [];
for (var i = 0; i < arr1.length; i++) {
if (arr2.includes(arr1[i])) {
result.push(arr1[i]);
}
}
console.log(result);
求数组的连续最长子串:
var arr = [3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6];
var temp = arr[0];
var count = 1;
var zy = arr[0];
var zy_c = 1;
for (var i = 1; i <= arr.length; i++) {
if (arr[i] == temp) {
count++;
} else {
if (count > zy_c) {
zy = temp;
zy_c = count;
}
temp = arr[i];
count = 1;
}
}
console.log(zy, zy_c);
这个算法,记住,用A4认认真真,写写画画一遍。算法这个东西,确实很神奇,只要理解了,头脑中有了,终身忘不掉。
相邻项差值最大(减肥)
var arr = [50, 49, 51, 49, 48, 45, 36, 41, 43, 39];
var max = 0;
// 遍历数组,每一项都和下一项做减法。如果减法差值大于了max,那么它就是max。
for (var i = 0; i <= arr.length - 2; i++) {
if (arr[i] - arr[i + 1] > max) {
max = arr[i] - arr[i + 1];
}
}
console.log(max.toFixed(2));
对象
var xiaoming = {
name: '小明',
age: 12
}
我们学习对象的意义,是后端给我们的数据,叫做JSON结构,就是数组+对象的形式。要会进行处理。
说白了,对象一般不纯考面试题,而是结合数组,进行一些算法题。算法和数组的算法是一样的,只不过增加了对象的成分。
比如,给你全班考试成绩,问你谁是总分状元?
var arr = [
{ name: '小A', a: 3, b: 5, c: 1 },
{ name: '小B', a: 5, b: 2, c: 2 },
{ name: '小C', a: 1, b: 3, c: 7 },
{ name: '小D', a: 5, b: 5, c: 3 },
{ name: '小E', a: 7, b: 5, c: 4 }
];
var max = 0;
var name = '';
for (var i = 0; i < arr.length; i++) {
if (arr[i].a + arr[i].b + arr[i].c > max) {
max = arr[i].a + arr[i].b + arr[i].c;
name = arr[i].name;
}
}
console.log(max, name);
函数
函数作用域
我们的JS中,用var定义的变量,作用域是定义时函数内部。
function fun() {
var a = 5; // a是在fun里面定义的,所以a是局部变量
}
fun();
console.log(a); // 报错,因为a是局部变量
注意遮蔽现象:
var a = 666;
function fun(a) {
a++;
console.log(a); // 输出5,局部变量a,因为形式参数a把全局的a遮蔽了。
}
fun(4);
console.log(a); // 输出666。它没有被加1。因为函数里面是局部变量。
var a = 666;
function fun() {
var a = 5; // a是在fun里面定义的,所以a是局部变量
console.log(a); // 输出5。局部的a把外部的a遮蔽了。
}
fun();
注意:JS中如果有这样的写法 var a = b = 3 。那么此时会这样顺序执行:
① 先将3的值赋给b,同时认为b会被定义为全局变量。为啥?没为啥,因为JS特性。
② 再将3的值赋给a,同时由于a前面有var,所以它是局部变量。
这种叫做“一个值连等赋给两个变量,那么中间那个变量,就是全局变量了”。
给我们的启示是,如果想一个值赋给两个变量,只能拆开写:
var a = 3, b= 3;
不能:
var a = b = 3;
var a = 666;
function fun() {
var a = b = 3;
console.log(a); // 输出3,因为局部的a把全局遮蔽了
}
fun();
console.log(b); // 输出3,不报错,因为JS特性,b是全局变量了。
闭包
- 闭包是什么:函数本身,和定义时所处的外部环境,合称为“闭包”。
- 表现:函数如果被挪到了其他地方执行,那么仍然能够使用定义时候的作用域。
- 功能:1、制作记忆性的程序,比如调用一次inner()就让内部a加1; 2、能够私有化变量,让a被保管的安全一些,只能让它增加,不能减少。
- 缺点:容易造成内存泄露。比如外部函数已经设置为null了,闭包还在。
例子:
function fun() {
var a = 3;
return function() {
console.log(a);
}
}
var inner = fun();
var a = 8;
inner();
函数整体提升
函数提升整体,但是 var fun = function(){} 只提升定义。
要会这种恶心题:
fun(); // C
function fun() {
console.log('A');
}
fun(); // C
var fun = function () {
console.log('B');
}
fun(); // B
function fun() {
console.log('C');
}
fun(); // B
var fun = function () {
console.log('D');
}
fun(); // D
递归
我们慢慢学吧,DOM时期、JS高阶、面试题时期,都是Danny老师讲。“师承教育”。
现在只需要会一个题目,递归求阶乘:
function jiecheng(n) {
return n == 1 ? 1 : n * jiecheng(n - 1);
}
函数的算法
如果能简化问题,那么写封装函数。
var arr = [
{ name: '小A', h: 1.75, w: 50 },
{ name: '小B', h: 1.65, w: 60 },
{ name: '小C', h: 1.73, w: 70 },
{ name: '小D', h: 1.65, w: 72 },
{ name: '小E', h: 1.85, w: 60 }
];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].name + '的情况是' + bmi(arr[i].h, arr[i].w));
}
function bmi(h, w) {
var m = w / (h * h);
if (m > 27) {
return '肥胖';
} else if (m > 24) {
return '正常';
} else if (m > 18) {
return '偏瘦';
} else {
return '太瘦了';
}
}