js基础知识大总结

227 阅读28分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

如果声明了一个变量 但是没有初始化的时候 那么这个变量的undefined

// var redRomance;
// console.log(redRomance);

变量命名规范

// 变量命名规范:只能以字母 _ $ 开头 后面可以接字母 _ $ 数字
//              区分大小写
//              不能使用系统征用的关键字
//              关键字:具有一些意义的词

js变量提升

console.log(a);
var a = 2;
console.log(a);

// js的变量提升
/**
 * var a;
 * console.log(a);
 * a = 2;
 * console.log(a);
 */

\n 和 \

// \n是在代码执行的时候 进行的换行
console.log('水晶头松了\n需要换一下');

// \是在代码编写的过程中 可以换行
// 如果页面的代码标签很多 那么我们把他放在一个字符串的时候 就需要使用\
console.log('今天开始降温了\
希望大家少穿点');

强制类型转换为string

1String()方法
   String(1)
   任何基本数据类型都可以用String()转换为stirng类型

2toString()方法
   var num = 1;
   num.toString();
   tostring方法是+运算符的底层实现,undefined和null没有tostring方法

字符串加上任何的东西都是字符串,在加号的两端 只要有一端是字符串,那么结果就是字符串

逻辑与(&&)和逻辑或(||)

逻辑与:第一个操作数的求值结果为false,就不会对第二个操作数求值了。
逻辑或:第一个操作数的求值结果为true,就不会对第二个操作数求值了。

因为js的小数运算 遵循的是二进制浮点数运算规范 所以小数运算不准确

var a = 0.1;
var b = 0.2;
var c = a + b;
console.log(c);

// 解决策略
var d = (1 + 2) / 10;
console.log(d);

数据类型转换为Number

//数据类型转换Number
console.log(Number('bb'));//NaN
console.log(Number('123bb'));//NaN
console.log(Number(''));//0
console.log(Number('  '));//0
Number(false);//0
Number(null);//0
Number(undefined);//NaN

infinity

console.log(1/0);// infinity  正无穷
console.log(-1/0);// -Infinity

infinity和数字的计算结果都是Infinity

infinity和自身计算
// + * 是Infinity
// - / 是NaN

总结:除了InfinityInfinity的计算的 + - * /之外 其余的都当做具体数值计算
console.log(Number.POSITIVE_INFINITY);
console.log(Number.NEGATIVE_INFINITY);

NaN

除了NaN和字符串相加是字符串之外,NaN和任意的数值计算都是NaNNaN和任意的数值进行比较 结果都是false
console.log(NaN == NaN);//false

isNaN()

判断的数据 不是一个数字 如果不是就是true 如果是 就是false

Boolean()

// 只有6种情况是返回的是false
// ''   字符串空串
// 0
// NaN
// false
// undefined
// null

+号两端如果没有字符串的话 那么会自动隐式转换为number

var a;
console.log(a + 1);//NaN

检测是否是数组

Array.isArray(arr)

判断数据类型

typeof

// 基本数据类型 除了null之外都可以识别
// null被识别为object
console.log(typeof 'a');//string
console.log(typeof 1);//number
console.log(typeof true);//boolean
console.log(typeof undefined);//undefined
console.log(typeof null);//object
console.log(typeof Symbol());//symbol

// typeof来判断引用数据类型
// 可以识别出function 但是object和array都被识别为object
function a(){}
console.log(typeof a);//function

instanceof

instanceof 不能识别基本数据类型
// console.log('a' instanceof String);//返回false

// instanceof能识别引用数据类型的数据
console.log({} instanceof Object);
console.log([] instanceof Array);
function f1(){};
console.log(f1 instanceof Function);

constructor

console.log('a'.constructor == String);
			
var a = 1;
console.log(a.constructor == Number);

console.log(true.constructor == Boolean);

// 总结:undefined和null没有constructor  
// 所以不能使用constructor来判断他们的数据类型

toString()

console.log(Object.prototype.toString.call('a'));
console.log(Object.prototype.toString.call(1));
console.log(Object.prototype.toString.call(true));
console.log(Object.prototype.toString.call(undefined));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call(Symbol()));

console.log(Object.prototype.toString.call({}));
console.log(Object.prototype.toString.call([]));
function f1(){}
console.log(Object.prototype.toString.call(f1));

数据类型转换parseInt

//只要第一位没见到数字,就是NaN
//console.log(parseInt(''));//NaN
//console.log(parseInt('     '));//NaN

使用乘以1的方式来将字符串转换为number

使用除以1的方式将字符串转换为number

使用减0的方式将字符串转换为number

取余

// 取余 取模  模余
console.log(5 % 3);// 2
console.log(3 % 5);// 3

// 符号和分子有关系,分子就是/前面的数
console.log(5 % -3);//2
console.log(-5 % 3);//-2
console.log(-5 % -3);//-2

比较

// 纯纯数字   ---正常比较
// console.log(2 > 1);

// 数字和字符串
//     将字符串转成了number--》所有的字母转成number都是NaN  结果就是false
// console.log(2 > '1'); // true
// console.log(2 > 'a'); //false
// console.log(2 < 'a'); //false

// 纯纯字符串
//    转换为了Unicode编码来比较  (按照位置比较)
console.log('1' < '2');//true
console.log('12' < '2');//true
console.log('21' < '2');//false
console.log('1' < 'a');//true

// 数字和字符串比较,会把字符串转换为number,然后再进行比较  
// 字符串比较 使用的是Unicode编码
比较时,如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
比较时,如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
比较时,如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。

在进行运算时,除了和字符串的加法运算之外 其余所有的算数运算全部转换为number之后,在进行计算.

switch注意点

switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,
字符串"10"不等于数值 10)。
console.log(null == undefined);//true
// 特殊的  以下都是false
console.log(null == 0);
console.log(null == ' ');
console.log(null == '');
console.log(null == false);
console.log(null === undefined);

优先级

image-20211225194223091.png

if和switch有什么区别,该如何选择?

1if能完成的,大部分情况下switch都能完成,switch性能更高一些
2if可以处理一些更加复杂的逻辑,还能实现嵌套,而switch只是简单的一些基本逻辑,有一些具体值,
3if可以写多个表达式,而switch只能写一个表达式,
4if后面可以跟很多种数据类型的数据,而switch后面只能跟整数和字符串

image-20211228194450035.png

for和while如何选择?

image-20211228201719187.png

数组的方法(重要)

总结: 对原数组有影响的方法 push pop unshift shift splice reverse sort 对原数组没有影响的方法 join concat slice toString valueOf

​ foreach() ​ map() ​ filter() ​ some() ​ every() ​ reduce()

push()方法
功能:从数组的尾部增加一个或多个元素或者数组
参数:增加的元素
返回值:返回加入元素后数组的新长度
这个方法对原数组有影响
pop()方法
功能:从数组的末尾删除一个
参数:无
返回值:返回删除的那一个元素
这个方法对原数组有影响
unshift()方法
功能:从数组的头部增加一个或多个元素或者数组
参数:增加的元素
返回值:返回加入元素后数组的新长度
这个方法对原数组有影响
shift()方法
功能:从数组的头部删除一个
参数:无
返回值:返回删除的那一个元素
这个方法对原数组有影响
splice()方法
功能:增删改一体化,这个方法根据参数的不同,有不同的功能,这个方法对原数组有影响
// 如果是一个参数的时候 是保留几个元素
// 如果是两个参数的时候 是删除几个元素  
//                    第一个参数是从哪个位置开始
//                    第二个参数是删除多少个
// 如果是三个以及三个以上的参数  是修改几个元素
//                    第一个参数是从哪个位置开始
//                    第二个参数是删除多少个
//                    第三个以及三个以上的那些参数是要添加的数据
// 如果三个参数及三个参数以上中的第二个参数是0  那么就代表添加元素,因为删除的是0个元素


// 一个参数的时候 返回的是被删除的那个元素
// 两个参数的时候 返回的是被删除的那个元素
// 三个参数的时候 返回的是被删除的那个元素
// 三个参数中如果第二个参数是0 也就是添加的时候  那么返回值是空数组
reverse()方法
功能:翻转数组
参数:无
返回值:返回的是翻转之后的数组
这个方法改变原数组

翻转之后和原数组是同一个数组
sort()方法
功能:排序,对数字的排序,默认情况下是升序,默认是unicode编码来排序

arr.sort(function(a,b){
	// a-b是升序 也是默认的排序,
	// b-a是降序  
	return a - b;
});


// 按照字符串的长度
var arr = ['aaaaa','hhh','bbbb'];
			
arr.sort(function(a,b){
    // return a.length - b.length;
    return b.length - a.length;
});

参数:如果不写,默认把每个元素转化为字符串进行排序(按照Unicode码)
返回值:返回的是被排序之后的数组
这个方法改变原数组

排序之后和原数组是同一个数组
join()方法
功能:将数组中的元素以某个字符串进行拼接,一般情况下 我们使用#来拼接,如果不传参数 那么默认情况下使用逗号拼接
参数:需要拼接的字符
返回值:返回连接好的字符串

这个方法对原数组没有影响


// 如果后端那个逗比把一个数组变成了字符串给你  而你呢 还需要使用数组
// var a = result.split('#');
// console.log(a);
concat()方法
功能;合并数组
参数:需要拼接的数组或元素
返回值:返回拼接好的新数组

这个方法对原数组没有影响

var arr = [1,2,3];			
var arr2 = [4,5,6];
var result = arr.concat(arr2);
slice()方法
功能:在数组当中截取部分元素形成新数组
参数:和字符串方法slice一致;起始位置和结束位置,不包含结束位置的元素
	如果只传一个代表起始位置,一直到结束。
	两个位置都可以使用负数
返回值:返回截取的元素形成的新数组

这个方法对原数组没有影响

indexOf()和 lastIndexOf()。这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。

在比较第一个参数与数组中的每一项时,会使用全等操作符;也就是说,要求查找的项必须严格相等(就像使用===一样)。

toString()方法
功能:将数组对象转化为字符串
参数:无
返回值;把数组的中括号去掉,其余加引号形成字符串返回

这个方法不改变原数组
//valueOf 方法 是Object的原型当中的
//返回数组实例对象。
var result = arr.valueOf()
console.log(result);
console.log(arr);
console.log(result === arr);//true

var o = new String('111');
console.log(typeof o);
var s = o.valueOf();
console.log(typeof s);
//功能:获取数组对象的基本值,数组实例对象。
//参数:无
//返回值:返回原数组,因为数组是非包装对象,所以它是没有基本值
foreach()函数
// foreach就是为了遍历数组中的数据  作用等同于for循环,.只能是用来遍历 ,一旦开始了遍历就停不下来
返回值:是undefined   这个undefined对于我们来说没有用
			
var arr = ['雪碧','可乐','芬达','七喜','大白梨','北冰洋','大香蕉'];

// item 数组中的每一个元素
// index数组中的下标
// arr  当前被操作的数组

var result = arr.forEach(function(item,index,arr){
    console.log(item,index,arr);
});

console.log(result);

// map()方法
// map方法的返回值是一个数组
// 数组中的元素是原数组中的元素操作之后的数据 以return返回
// return一个值就存储到数组中一个值
var new_arr = arr.map(function(item,index,arr){
    return item * item;
});

console.log(new_arr);
// foreach  只遍历   ----没有返回值      返回值为undefined
// map      遍历之后 对所有的元素进行操作  返回值是操作之后的新数组
// filter   遍历之后 对部分元素进行筛选    返回值是操作之后的新数组
filter()方法
filter用于过滤掉“不合格”的元素,根据条件过滤数组中的数据
返回一个新数组,如果在回调函数中返回true,那么就留下来,如果返回false,就扔掉

**作用:**

- 1.可以循环遍历数组中的每一项
- 2.可以对循环遍历到的数据进行判断
- 3.当条件成立时,使用了return true后会将满足条件的那一项存到一个新的数组当中
- 4.**==重点应用场景:根据条件过滤数组中的数据==**
some()方法
some用于遍历数组,如果有至少一个满足条件,就返回true,否则返回falsesome是判断某元素是否在某数组中出现
作用:

- 1.可以用来循环遍历数组中的每一项
- 2.在回调函数中进行条件判断,如果return true执行之后,会阻止后续代码的遍历执行
- 3.重点应用场景: **==条件成立时不再执行后续的循环==**   比如,注册邮箱时如果有某个人名字注册了就不能再注册了

//some的返回值是boolean类型的
every()方法
`every`用于遍历数组,只有当所有的元素返回true,才返回true,否则返回false**需求:遍历数组,判断==整体==是否都满足条件**

作用:

- 1.可以对数组中的每一项进行遍历,但是只打印第一项
- 2.对数组中的每一项进行判断,都满足条件则返回true,如果有一项不满足条件则返回false
- 3.==应用场景:重点是强调整体的一个处理结果,比如,某地区所有人健康码是否都为绿码==
reduce()方法
语法:`reduce(callback, initValue)`

在没有给初始值的时候
    初次执行的时候  会将第一个元素当做prev  第二个参数当做item
    再次执行的时候 会将返回的prev+item的结果 再次和第三个元素进行相加

给初始值的时候
    初始值当做prve  第一个元素当做item

callback: 每个元素都会执行一次的回调函数

initValue: 初始值

callback的4个参数

- prev: 上一次的值,第一次为初始值
- item:  当前值
- index: 下标
- arr: 数组

作用:// reduce方法 一般是处理的是 将数组中元素做一个汇总  eg 相加 相乘 减 除 取余 
案例:计算数组所有值的和
var nums = [10, 20, 30, 40, 50, 60]
var res = nums.reduce(function (prev, item) {
      return prev + item
 }, 0)

console.log(res);

函数的返回值的几种情况


1)函数中没有写return 那么接受到的是undefiend
2)函数中有return 但是return后面没有值 那么接受到的是undefiend
3)函数中有return 后面还有值           那么接受到的是return后面的值
4)函数中有return  后面还有值          那么return后面的代码不执行

变量的作用域

// 在函数内部定义的变量 我们称之为局部变量 // 在函数外部定义的变量 我们称之为全局变量

// 如果在局部作用域下 定义的变量没有var 那么这个变量就当做全局变量来使用

闭包

闭包产生的条件,闭包实现的步骤

(1)函数嵌套-外部函数嵌套一个内部函数
(2)内部函数要引用外部函数的局部变量
(3)外部函数返回的是内部函数的方法体
(4)外部函数和内部函数都要被调用

什么是闭包?

所谓闭包就是一种引用关系,该引用关系存在于内部函数中,引用的是外部函数的局部变量的对象。

闭包的作用

1.延长外部函数变量的生命周期
2.函数外部可以引用函数内部的变量

闭包的缺点和解决(内存泄漏和内存溢出)

内存泄漏 : 内存无法释放;
内存溢出 : 内存被撑爆;
f = null;  解决方式;

function fn(){
		var a = 0;
		function fn1(){
			a++;
			console.log(a);
		}
		return fn1;
}
var f =  fn();
f();
f();
f = null;//释放闭包

获取函数参数的个数

// 获取实参的个数   arguments
function f1(){
	console.log(arguments.length);
}


// 获取形参的个数   函数对象
function f2(a,b,c){
    console.log(f2.length);
}

console.log(f2.length);

arguments是伪数组,伪数组:看着像数组  但是不可以调用数组的方法  可以通过下标来访问元素

arguments的callee属性

function f1(){
    // callee属性是返回的是方法体
    console.log(arguments.callee);
}

创建对象

function Mener(name,age){
    this.name = name;
    this.age = age;
    return 1489;
}
// 构造函数如果返回的是基本数据类型 那么返回的是创建的那个对象
var mener = new Mener('和牛',2);
console.log(mener);


function Rabbit(name,age){
    this.name = name;
    this.age = age;
    return [1,2,3,4];
}

// 构造函数如果返回的是引用数据类型 则返回的是引用数据类型
var rabbit = new Rabbit('小白',1);
console.log(rabbit);

this的指向

1)在script标签里面   window2)在函数的内部        window3)在方法的内部       方法的调用者
(4)在构造函数内       实例化的那个对象
(5)在事件回调函数中    事件源
(6)在call和apply中    第一个参数
 (7)箭头函数:是该作用域的外层this

字符串的方法18个

/**
 * 1. toUpperCase     转大写
 * 2. toLowerCase     转小写
 *  
 * 3. charAt          根据下标找字符
 * 4. charCodeAt      根据下标找字符unicode编码
 * 5. fromCharCode    根据unicode编码找字符   String.fromCharCode(24352);
 * 
 * 6. indexOf         根据字符找下标
 * 7. lastIndexOf     根据字符找最后一个下标
 *  
 * 8. slice           截取   根据起始和结束下标左闭右开
 * 9. substr          截取   根据起始和长度 
 * 10.substring       截取   根据起始和结束下标左闭右开 无负数
 * 
 * 11.startsWith      以谁谁谁为开头  返回boolean
 * 12.endsWith        以谁谁谁为结尾  返回boolean
 * 
 * 13.concat          拼接
 * 14.localeCompare   比较    -1 1 0
 * 15.split           分割 拆分 切割 
 * 16.toString        
 * 17.includes        包含   返回boolean
 * 18.repeat          重复   

栈和堆得区别

栈:存储的是基本数据类型和引用数据类型变量
     先进后出,后进先出
     相比较堆来说 栈的内存较小
     相比较堆来说 存取速度快
 堆:存储的是对象
     无序
     相比较栈来说 堆内存较大
     相比较栈来说 存取速度慢

日期和时间对象

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script>
			var d = new Date();
			console.log(d.getFullYear());//年
			console.log(d.getMonth());//月  从0开始
			console.log(d.getDate());//日
			
			console.log(d.getHours());//时
			console.log(d.getMinutes());//分
			console.log(d.getSeconds());//秒
			
			console.log(d.toLocaleTimeString());//时分秒
			console.log(d.toLocaleDateString());//年月日
			
			console.log(d.getTime());
			
			console.log(d.getDay());
			
			
			function getSGGTime(){
				var year = d.getFullYear();
				var month = d.getMonth()+1;
				var day = d.getDate();
				
				var hms = d.toLocaleTimeString();
				
				return year + '年' + month + '月' + day + '日' + hms;
			}
			
			console.log(getSGGTime());
			
		</script>
	</body>
</html>

判断对象中是否有某属性的常见方式总结,不同的场景要使用不同的方式

一、点( . )或者方括号( [ ] )
通过点或者方括号可以获取对象的属性值,如果对象上不存在该属性,则会返回undefined。当然,这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。

// 创建对象
let test = {name : 'lei'}
// 获取对象的自身的属性
test.name   //"lei"
test["name"]   //"lei"

// 获取不存在的属性
test.age    //undefined
 
// 获取原型上的属性
test["toString"]  //toString() { [native code] }
 
// 新增一个值为undefined的属性
test.un = undefined
 
test.un    //undefined 不能用在属性值存在,但可能为 undefined的场景

二、in 运算符
如果指定的属性在指定的对象或其原型链中,则in 运算符返回true'name' in test  //true
'un' in test    //true
'toString' in test //true
'age' in test   //false

三、hasOwnProperty(propertyName):
用于检查给定的属性在当前对象实例中(而不是在实例
的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例
如:

test.hasOwnProperty('name')  //true 自身属性
test.hasOwnProperty('age')   //false 不存在
test.hasOwnProperty('toString') //false 原型链上属性

严格模式

// 一、严格模式的声明
//全局使用严格模式
// 'use strict';

//局部使用严格模式
/* function test(){
    'use strict';
} */

// 二、严格模式的特性
//1) 不允许使用未声明的变量
/* 'use strict';
a = 200;
console.log(a); */

//2) 函数内部的 this 不指向 window
/* 'use strict';
function test(){
    console.log(this); //undefined
}
test(); */

//3) eval 作用域   eval 是一个函数, 接受字符串参数, 会对字符串进行 JS 语法解析并运
/* 'use strict';
eval('alert(123)') */

//4) 对象不能有重复的属性
/* 'use strict';
var obj = {
    name : '晶哥',
    name : 'jingge'
}
console.log(obj); */

//5) 严格模式下 函数
/* 'use strict';
function test(a,a,a){
    console.log(a)
}
test(1,2,3) */


//6) 新增一些保留字  private protected implements
'use strict';
var private = '私有的';//在严格模式下会报错
console.log(private);

Object.create

    //创建一个老对象
    var car = {
        name : '汽车',
        run : function(){
            console.log('可以兜风');
        }
    }

    //以 car 为原型,创建一个新的对象
    var aodi = Object.create(car,{
    //需要在这个位置上给新对象设置新的属性
    brand : {
        value : '奥迪',
        writable : true, //该属性是否可以进行修改,默认是false
        configurable : true, //该属性是否可以进行删除,默认是false
        enumerable : true //该属性是否可以 for in,默认是false
    },
    price : {
        //通过 getter setter 动态获取该属性 和 动态设置该属性
        get : function(){
            return this.m;
        },
        set : function(v){
            this.m = v;
        }
    }


})

aodi.price = 50000;
console.log(aodi.price)

console.log(car)
console.log(aodi)

Object.defineProperties

//定义新属性
var car = {
    name : '汽车',
    run : function(){
        console.log('可以兜风');
    }
}

Object.defineProperties(car,{
    //设置新属性的
    brand : {
        value : '奔驰',
        writable : true,
        configurable : true,
        enumerable : true
    },
    price :{
        value : '30w'
    }

})

//修改对象的属性
// car.brand = 'hhhhh'
//删除属性
// delete car.brand;

//枚举
for(var item in car){
    console.log(item)
}


console.log(car)
//要求添加 total 属性, 获得班级的总分数
var banji = {
    name: 'HTML',
    scores: [
        {
            name: '张三',
            score: 90
        },
        {
            name: '李四',
            score: 85
        },
        {
            name: '王五',
            score: 95
        },
        {
            name: '赵六',
            score: 88
        }
    ]
};

Object.defineProperties(banji,{
    total : {
        get : function(){

            //总分数
            var sum = 0;
            for(var i=0;i<banji.scores.length;i++){
                sum = sum + banji.scores[i].score;
            }

            return sum;
        }
    },
    avg : {
        get : function(){
            return banji.total/banji.scores.length;
        }
    }
})

console.log(banji.total)
console.log(banji.avg)

call apply bind 面试题

共同点:call apply bind 都是用来改变this的指向

function test(a,b,c){
    console.log(this)
    console.log(a,b,c)
}
// test(1,2,3);
// call()  参数1:修改后的this, 从参数2开始是实参的列表
// test.call({},1,2,3)

// apply()  参数1:修改后的this, 参数2是数组,数组中的元素是实参列表
// test.apply({name:'晶哥'},[1,2,3])

//bind  参数是修改后的this, bind方法执行完返回的是一个函数
// 该函数调用需要传递实参的列表
// var f = test.bind({})
// f(4,5,6)

// console.log(f)//函数
// console.log(f === test)//false

//简写方式
test.bind({})(7,8,9)

var let const 面试题

var有变量提升
var不支持支持块级作用域
var可以先使用再声明,let只能先声明,再进行调用;
var可以重复声明,let不可以重复声明
用letconst声明的全局变量不归window所有


//let 声明特点
//1. 不允许重复声明
// let a = 100;
// let a = 200;
// console.log(a);//报错,Uncaught SyntaxError: Identifier 'a' has already been declared

//2. 块儿级作用域   if(){}  else{}  while  for(){}   {}
// if(true){
//     let a = 500;
//     console.log(a)
// }
// console.log(a);//报错 a is not defined

//3. 不存在变量提升 (临时性死区)
// console.log(b)
// let b = 600;//报错  Uncaught ReferenceError: Cannot access 'b' before initialization

//4. 不影响作用域链


//const特点
//const 用来声明一个常量(值不变)
//1. 声明的时候一定要赋初始值

//2. 常量的名称一般为 『大写』  潜规则
 
//3. 不能修改常量的值
  
//4. 不允许重复声明
  
//5. 块儿级作用域
/*  if(true){
    const A = 100;
    console.log(A)
}
console.log(A) */
    
//6. 关于数组和对象的元素的修改
//  数组和对象 使用的是地址
/* const arr = ['豪哥','申阳','刘昊'];
console.log(arr);
arr[0] = 'haoge';
console.log(arr) */

箭头函数


//声明格式
/* let test = (形参) => {
    函数体
} */

箭头函数特点
// 1. this 的值是静态的
 
//this:
// 普通函数调用,是window
// 对象obj.test()形式调用,是对象obj
// 构造函数的形式,是实例对象 new 
// 事件绑定的情况,是事件源
// 定时器:是window
// call apply bind ,是修改后的
// 箭头函数:是该作用域的外层this

// 2. 不能作为构造函数使用
// 3. 不能使用 arguments,可以使用 rest 参数
let demo = (...arg)=>{
    console.log(arg);
}
demo(1,2,3);

// 4. 箭头函数简写
// 一 不写小括号, 当形参有且只有一个的时候
// 二 不写花括号, 当代码体只有一条语句的时候, 并且语句的执行结果为函数返回值的 (如果不写花括号的话, return 也不能写)

需求: 从数组中返回偶数的元素

let arr = [1,2,3,99,55,6,7,8,8,8,6,5,4,43,3];

  let newArr = arr.filter(item=>item % 2 === 0)

ES6

//ES6 允许给函数参数赋值初始值
//1. 参数直接设置默认值  具有默认值的参数, 位置一般要靠后(潜规则)
/* function test(a,b,c=100){
    console.log(a,b,c)
}
test(500,600,800) */


//2. 与解构赋值结合使用   结构赋值的形式先后顺序不影响
function test({hobby="唱歌",name}){
    console.log(name)
    // console.log(age)
    console.log(hobby)

}
test({
    name :'豪哥',
    age : 18
})
    //3. 将伪数组转为真正的数组
    /* let divs = document.querySelectorAll('div')
    console.log(divs)
    console.log(divs instanceof Array)//false  
    console.log(typeof divs) //object
    
    let newDivs = [...divs];
    console.log(newDivs)
    console.log(newDivs instanceof Array) */
//2. Object.assign 对象的合并
   let obj1 = {
    a: 100,
    b: 200
}
let obj2 = {
    c: 300,
    d: 400
}

let newObj = Object.assign(obj1, obj2);
console.log(newObj)
console.log(obj1)
console.log(obj2)
console.log(newObj === obj1)//true
//rest 参数是用来代替 arguments 的

/* function test(...args){
    console.log(arguments)
    console.log(args)//是数组
}
test(2,3,4,5); */


//福利院
function test(one,two,...args){
    console.log(arguments)
    console.log(one)
    console.log(two)
    console.log(args)//是数组
}
test('秋秋','晶晶','岩岩','琪琪','平平')


//箭头函数中也可以使用rest方式
let demo = (...args)=>{
    console.log(args);
}

demo(4,5,6)



//spread扩展运算符  ...
//数组的展开
let arr = [3,4,5,6];
let newArr = [...arr];
console.log(newArr);

//对象的展开
let obj1 = {
    a : 100,
    b : 200
}
let obj2 = {
    c : 300,
    d : 400
}
let obj3 = {
    a : 500,
    f : 600
}

//如果有相同的属性,会发生覆盖操作
let newObj = {...obj1,...obj2,...obj3}
console.log(newObj)


//1. 数组的合并
let arr1 = [4,5,6]
let arr2 = [7,8,9]

let arr3 = [...arr1,...arr2]
console.log(arr3) 

//2. 数组克隆(复制,拷贝)
let arr1 = [2,3,4,5];
let newArr = [...arr1]
console.log(newArr)
console.log(arr1 === newArr) //false 

//3. 将伪数组转为真正的数组
let divs = document.querySelectorAll('div')
console.log(divs)
console.log(divs instanceof Array)//false  
console.log(typeof divs) //object

let newDivs = [...divs];
console.log(newDivs)
console.log(newDivs instanceof Array)//true

function test(){
    console.log(arguments)
    console.log(arguments instanceof Array)

    let arg = [...arguments]
    console.log(arg)
    console.log(arg instanceof Array)
}
test(2,3,4,5,6)

//ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值
// let s = Symbol();
// console.log(s);
// console.log(typeof s);
//1)Symbol的值是唯一的,用来解决命名冲突的问题
    let s1 = Symbol('html');
    let s2 = Symbol('html');
    console.log(s1)
    console.log(s2)
    console.log(s1 === s2) //false
//2)Symbol值不能与其他数据进行运算
	let s4 = Symbol('css');
// console.log(s4 + '拼串')//报错
// console.log(s4 + 100)//报错


//1. 二进制和八进制 十进制 十六进制
//二进制 以 0b 开头 ,表示的数字范围是0-1
// 八进制 以 0o 开头,表示的数字范围是0-7
// 十六进制  以 0x开头,表示的数字范围是0-9,a-f
    
//2. Number.isFinite  检测一个数值是否为有限数
console.log(Number.isFinite(Math.PI))//false
console.log(Number.isFinite(1/0))//true
    
//3. Number.isNaN 检测一个数值是否为 NaN  isNaN
    // console.log(Number.isNaN(NaN))//true
    // console.log(Number.isNaN(20))//false
    // console.log(Number.isNaN(20 + undefined))//true
    
//4. Number.parseInt 字符串转整数
    // console.log(Number.parseInt('123456'))
    // console.log(Number.parseInt('123adsfdsgd'))
    // console.log(Number.parseInt('sffhg456'))

    
    //5. Math.trunc 将数字的小数部分抹掉
    // console.log(Math.trunc(Math.PI))
    // console.log(Math.trunc(20/3))
    
    //6. Number.isInteger 判断一个数是否为整数 is 是否 integer 整型
    // console.log(Number.isInteger(10))
    // console.log(Number.isInteger(10.6))

    
    //7. 幂运算  (ES7)    Math.pow()
    // let num = Math.pow(2,3)
    // console.log(num)
    // let num2 = Math.pow(3,2)
    // console.log(num2)
    
    //1. 判断两个值是否完全相等  ===  Object.is
    // console.log(Object.is(100,100))
    // console.log(Object.is(100,'100'))
    // console.log(Object.is(NaN,NaN)) //true
    // console.log(NaN === NaN) //false
    
//2. Object.assign 对象的合并
   let obj1 = {
    a: 100,
    b: 200
}
let obj2 = {
    c: 300,
    d: 400
}

let newObj = Object.assign(obj1, obj2);
console.log(newObj)
console.log(obj1)
console.log(obj2)
console.log(newObj === obj1)//true

迭代器

迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
1)	ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费
2)	原生具备iterator接口的数据(可用for of遍历)
a)	Array
b)	Arguments
c)	Set
d)	Map
e)	String
f)	TypedArray
g)	NodeList
3)	工作原理
a)	创建一个指针对象,指向当前数据结构的起始位置
b)	第一次调用对象的next方法,指针自动指向数据结构的第一个成员
c)	接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
d)	每调用next方法返回一个包含value和done属性的对象
注: 需要自定义遍历数据的时候,要想到迭代器。




let arr = ['申阳','蒋成龙','豪哥']

//指针对象
let iterator = arr[Symbol.iterator]();
console.log(iterator)

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

面试题:for in 和 for of的区别

for infor of 区别
遍历数据
for of 是es6提出的新遍历方式,数据中有iterator接口才可以进行for of
如果 for of 遍历数组,item是数组中的每一元素
如果 for in 遍历数组,item是数组的下标
for of 不能遍历对象
for in 可以遍历对象

面试题:自定义迭代器

const team = {
    name: 'BJH5',
    members: [
        'xiaoA',
        'xiaoB',
        'xiaoC',
        'xiaoD'
    ],
    //自定义迭代器
    [Symbol.iterator] : function(){
        //定义一个下标
        let index = 0;
        //返回值:指针对象
        return {
            next : ()=>{

                //需要确定value的值
                let data = this.members[index];
                //this是指针对象
                //解决方案1:箭头函数
                //解决方案2:在外边,保存this

                //定义一个变量,遍历是否结束
                let done = false;
                //当循环结束的时候 done = true
                if(index >= this.members.length){
                    done = true;
                }

                index++;
                //返回值:对象,有value done属性
                return {value:data,done:done}
            }
        }
    }

}


for(let item of team){
    console.log(item)
}

//指针对象
let iterator = team[Symbol.iterator]();
console.log(iterator)

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

集合Set

//声明集合  Set
// let s = new Set()

//会对数组进行去重
let s1 = new Set([1,1,2,3,2,6,5,6]) 

//1. 元素个数
// console.log(s1.size)

//2. 添加
// s1.add(9)
// s1.add(40)

//3. 删除
// s1.delete(1)
// console.log(s1)

//4. 检测集合中是否包含某个元素
// console.log(s1.has(1))
// console.log(s1.has(9))

//5. 清空
// s1.clear()
// console.log(s1)

//6. for...of 遍历
// for(let item of s1){
//     console.log(item)
// }

//7. 扩展运算符
let arr = [...s1]


集合练习
//1. 数组去重
let arr = [1,1,1,5,5,5,3,3,5,2,8,9]
let newArr = [...new Set(arr)]
console.log(newArr)
    
    
//2. 交集
let arr1 = [1,1,3,3,4,2,2];
let arr2 = [3,3,4,4,5,6,7];

/* let newArr = [...new Set(arr1)].filter(function(item){
    //item 是数组1去重后的每一个元素
    let s2 = new Set(arr2);

   if(s2.has(item)){
        return true;
   }else{
       return false;
   }
}) */

// let s2 = new Set(arr2);
// let newArr = [...new Set(arr1)].filter(item => s2.has(item))

//3. 并集
// let arr1 = [1,1,3,3,4,2,2];
// let arr2 = [3,3,4,4,5,6,7];

// new Set(arr1)
// new Set(arr2)

// let newArr = [...new Set([...new Set(arr1),...new Set(arr2)])]
// console.log(newArr)

//4. 差集
let arr1 = [1,1,3,3,4,2,2];
let arr2 = [3,3,4,4,5,6,7];

/* let newArr = [...new Set(arr1)].filter(item => {
    let s2 = new Set(arr2);
    if(s2.has(item)){
        return false;
    }else{
        return true;
    }
}) */

let s2 = new Set(arr2);
let newArr = [...new Set(arr1)].filter(item => !s2.has(item))
    

集合map

    // 集合: map 地图,映射
    //声明 Map
    // let m = new Map();
    // console.log(m)
    // console.log(typeof m)

    let m1 = new Map([
        ['name','xiaoA'],
        ['age','18'],
        ['hobby','喝酒']
    ])
    //参数是二维数组
    // console.log(m1)

      
    //添加元素  
    m1.set('height','180cm')
    //对于map来说,属性名可以是对象
    let obj = {name:'atguigu'}
    m1.set(obj,'北京')
    // console.log(m1)
    
    //获取元素
    // console.log(m1.get('name'))
    // console.log(m1.get(obj))
    // console.log(m1.get('obj'))
    
    //删除元素
    // m1.delete(obj)
    // m1.delete('name')
    // console.log(m1)
    
    //检测
    // console.log(m1.has('name'))
    // console.log(m1.has('obj'))
    // console.log(m1.has(obj))
      
    //元素个数
    // console.log(m1.size)
    
    //清空
    // m1.clear()
    // console.log(m1)
    
    //遍历  for of
    for(let item of m1){
        //item 是每一组键值对组成的数组
        console.log(item)
    }

类的声明

    //es5构造函数(构造工厂)
    function Phone(brand,price){
        this.brand = brand;
        this.price = price;
    }
    //在原型添加方法
    Phone.prototype.call = function(){
        console.log('打电话')
    }

    let huawei = new Phone('华为',4000)
    console.log(huawei )
    huawei.call()


    //es6
    class Phone{
        //属性
        constructor(brand,price){
            this.brand = brand;
            this.price = price
        }

        //方法
        call(username){
            console.log('给'+ username +'打电话')
        }
    }

    let xiaomi = new Phone('小米',3000)
    console.log(xiaomi)
    xiaomi.call('红浪漫')

类的静态属性和方法

//es5
function Phone(){

}

//静态属性
Phone.name1 = '这是一个手机工厂'
//静态的方法
Phone.change = function(){
    console.log('改变世界')
}


//es6
class Phone{
    //实例对象的属性
    constructor(brand,price){
        this.brand = brand;
        this.price = price;
    }
    //实例对象的方法
    call(){
        console.log('打电话')
    }
    //静态属性
    static name2 = '工厂';
    //静态方法
    static ch(){
        console.log('改变世界')
    }
}

let vivo = new Phone('vivo',3500)

vivo.call();
console.log(Phone.name2)
Phone.ch()

面试题:对象的继承

    //es5
    // 定义父类
    function Phone(brand,price){
        //父类的属性
        this.brand = brand;  //this 是oldP
        this.price = price;
    }
    //父类的方法
    Phone.prototype.call = function(){
        console.log('打电话')
    }
    Phone.prototype.sendMessage = function(){
        console.log('发短信')
    }
    // 定义子类
    function SmartPhone(brand,price,screen,pixel){
        //父类继承过来的属性
        Phone.call(this,brand,price); //this 是new出来的对象
        // Phone.apply(this,[brand,price])
        //子类独有的属性
        this.screen = screen;
        this.pixel = pixel;
    }
    //子类的方法
    //父类继承过来的方法
    SmartPhone.prototype = new Phone();
    SmartPhone.prototype.constructor = SmartPhone;
    //子类独有的方法
    SmartPhone.prototype.playGame = function(){
        console.log('打游戏')
    }
    

    //定义父类的实例对象
    let oldP = new Phone('诺基亚',300);
    oldP.call();
    oldP.sendMessage();
    console.log(oldP)
    //定义子类的实例对象
    let huawei = new SmartPhone('华为','5000','6.5inch','80w')
    huawei.call()
    huawei.sendMessage();
    huawei.playGame()
    console.log(huawei)

    console.log(oldP.__proto__.constructor)
    console.log(huawei.__proto__.constructor)
    
    //es6
    // 定义父类
    class Phone{
        //父类上的属性
        constructor(brand,price){
            this.brand = brand;  // this 是 oldP
            this.price = price;
        }
        //父类上的方法
        call(){
            console.log('打电话')
        }
        sendMessage(){
            console.log('发短信')
        }
    }
    //定义子类
    class SmartPhone extends Phone{
        //子类的属性
        constructor(brand,price,screen,pixel){
            //父类继承过来的属性
            super(brand,price);
            //子类独有的属性
            this.screen = screen;
            this.pixel = pixel;
        }
        //子类的方法(只需要书写子类独有的方法即可)
        playGame(){
            console.log('打游戏')
        }
    }

    //定义父类的实例对象
    let oldP = new Phone('诺基亚',400);
    oldP.call()
    oldP.sendMessage()
    console.log(oldP)
    //定义子类的实例对象
    let oppo = new SmartPhone('oppo',4000,'6inch','50w')
    oppo.call()
    oppo.sendMessage()
    oppo.playGame()
    console.log(oppo)

    console.log(oldP.__proto__.constructor)
    console.log(oppo.__proto__.constructor)

class的get和set

    class Phone{
        constructor(brand){
            this.brand = brand;
        }
        //getter setter 是动态操作对象的属性
        //获取价格
        get price(){
            return this.abc;
        }
        //设置动态的价格
        set price(v){
            this.abc = v;
        }

    }

    let huawei = new Phone('华为');

    huawei.price = 5000;
    console.log(huawei)
    console.log(huawei.price)

面试题:拷贝

    //拷贝 就是 复制   对象和数组

    //老对象
    //通过某种方式 对 老对象 进行复制操作,生成了
    //新对象
    //如果修改新对象的属性,老对象的属性也修改了,这个时候叫浅拷贝
    //如果修改新对象的属性,老对象的属性不修改,这个时候叫深拷贝

    //1. 直接赋值
    /* let arr = [3,4,5,6];
    let newArr = arr;
    //修改新数组的值
    newArr[0] = 60;
    console.log(arr)
    console.log(newArr) */

    /* let obj = {
        name : '豪哥',
        age : 18
    }
    let newObj = obj;
    //修改新对象的属性
    newObj.age = 20;
    console.log(obj)
    console.log(newObj) */
    
    //2. 数组
    // 1> concat   连接,数组拼接方法
    /* let arr = [3,{name:'豪哥'},5,6];
    let newArr = [].concat(arr);
    //修改新数组的内容
    // newArr[0] = 666;
    newArr[1].name = 'haoge';
    console.log(arr)
    console.log(newArr) */

    // 2> slice
   /*  let arr = [3,{name:'豪哥'},5,6];
    let newArr = arr.slice(0);
    //修改新数组的内容
    newArr[1].name = '申阳';
    console.log(arr)
    console.log(newArr) */

    // 3> 扩展运算符
   /*  let arr = [3,{name:'豪哥'},5,6];
    let newArr = [...arr];
    //修改新数组的内容
    newArr[1].name = '申阳';
    console.log(arr)
    console.log(newArr) */

        
    //3. 对象   Object.assign 对象的合并
    let obj = {
        name : '蒋成龙',
        age : 18,
        hobby:['阅读','喝酒']
    }
    let newObj = Object.assign({},obj)

    //修改对象的属性
    // newObj.age = 20;
    newObj.hobby[0] = 'read';

    console.log(obj)
    console.log(newObj);

深拷贝:JSON实现

    //使用 JSON 实现深拷贝
    let obj = {
        name : '尚硅谷',
        pos : ['北京','上海','深圳','武汉','西安'],
        //不能对方法进行拷贝
        test : function(){
            console.log('测试')
        }
    }

    //1.把 obj对象 转化成 JSON字符串
    let str = JSON.stringify(obj);
    //2. 把 JSON字符串 转化成一个新的对象
    let newObj = JSON.parse(str);

    //修改新对象的属性
    newObj.pos[0] = 'beijing';

    console.log(obj)
    console.log(newObj)

递归实现深拷贝

   //老对象
   let obj = {
        name : 'atguigu',
        pos : ['北京','上海','深圳','武汉','西安'],
        founder : {
            name : '刚哥',
            age : 35
        },
        test:function(){
            console.log('测试')
            console.log(this)
        }
    }

    //封装深拷贝函数
    function deepClone(oldObj){
        //1.准备一个容器(对象 数组)
        let wrapper;
        //2.根据老对象判断数据类型,决定容器是 {} 还是 []
        let type = getDataType(oldObj)
        if(type === 'Array'){
            wrapper = [];
        }
        if(type === 'Object'){
            wrapper = {}
        }
        //3.给容器添加新属性
        //枚举老对象的属性
        for(let item in oldObj){
            //item 是对象的每一个属性
            // wrapper[item] = '123'
            //根据属性值的数据类型去添加
            let typeItem = getDataType(oldObj[item])
           
            if(typeItem === 'Object' || typeItem === 'Array'){
                //如果是对象或者数组,
                wrapper[item] = deepClone(oldObj[item]);
                
            }else if(typeItem === 'Function'){
                //如果是函数,通过 bind()去添加
                wrapper[item] = oldObj[item].bind(wrapper)
            }else{
                //如果是普通的数据类型,直接添加
                wrapper[item] = oldObj[item];
            }

        }

        //返回值:是新对象
        return wrapper;
    }

   //新对象
   let newObj = deepClone(obj)
   console.log(newObj)



    //封装函数:判断数据类型的
    function getDataType(data) {
        if(typeof data === 'object'){
            if(data instanceof Array){
                //数组
                return 'Array';
            }else {
                //对象
                return 'Object';
            }
        }else if(typeof data === 'function'){
            //函数
            return 'Function';
        }else {
            //num str bool (基本数据类型)
            return false;
        }
    }
// 在nodejs中,那些语句是正确的(答案是A、B)
A、 console.log(c)
B、global.console.log(777)
C、window.console.log(777)
D、document.console.log(777)

cookie 和 session的区别

cookie 和 session的区别
1)	存在的位置:
cookie 存在于客户端
session 存在于服务器端(文件,数据库,内存),一个session域对象为一个用户浏览器服务(浏览器之间 cookie 是不共享的)
2)	安全性:(https证书)
cookie是以明文的方式存放在客户端的,安全性较低,可以通过一个加密算法进行加密后存放
session存放于服务器中,所以安全性较好
3)	网络传输量:
cookie会传递消息给服务器
session本身存放于服务器,但是通过cookie传递id,会有少量的传送流量
4)	大小:
cookie 保存的数据不能超过4K,很多浏览器都限制一个站点最多保存50个cookie
session 保存数据理论上没有任何限制

GET 和 POST 的区别

GET 和 POST 是 HTTP 协议请求的两种方式

  • GET 主要用来获取数据, POST 主要用来提交数据
  • GET 带参数请求是将参数缀到 URL 之后, 在地址栏输入url访问网站就是 GET 请求, POST 带参数请求是将参数放到请求体中
  • POST 请求相对 GET 安全一些, 因为在浏览器中参数会暴露在地址栏.
  • GET 请求大小有限制, 一般为 2k, 而 POST 请求则没有大小限制
  • GET 类型报文请求方法的位置为 GET , POST 类型报文请求方法为 POST