因为借鉴的文章比较多加上个人理解整理,没有备注来源。如侵权比删!
一、js的数据类型
ES5的5种:
基本数据类型: Number、 String、 Boolean、 undefined、 null
复杂数据类型:Object、function、Array
ES6新增
Symbol 表示独一无二
ES10新增:
BigLnt 表示任意大的数
ps:存储区别
基本数据类型和引用数据类型存储在内存中的位置不同:
- 基本数据类型存储在栈中
- 引用类型的对象存储于堆中
二、undefined和null的区别
相同点:
1、在if语句中null和undefined都会转换为false
2、用 == 比较 返回true
null ==undefined // true
不同点:
undefined:代表的含义未定义
null: 表示一个空对象指针
typeof undefined //'undefined'
typeof null // "object"
三、判断 JavaScript 的数据类型typeof
typeof:type的返回值是字符串(String)
typeof undefined // "undefined"
typeof null // "object"
typeof 1 // "number"
typeof "1" // "string"
typeof Symbol() // "symbol"
typeof function() {} // "function"
typeof {} // "object"
typeof 不能识别 null,如何识别 null?[使用全等即可]
let a = null
a === null
instanceof: 可以用来判断对象的类型:
instanceof
只能正确判断引用数据类型 而不能判断基本数据类型,
原因:其内部运行机制是判断在其原型链中能否找到该类型的原型
或者可以理解为:一个对象是否在一个类的示例上
var date = new Date()
date instanceof Date // true
var number = new Number()
number instanceof Number // true
var string = new String()
string instanceof String // true
四、js数据类型转换
强制类型转换
转化成字符串 toString() String()
转换成数字 Number()、 parseInt()、 parseFloat()
转换成布尔类型 Boolean()
隐式类型转换
"" + - / % === =
例子:
转换为数字
Number():可以把任意值转换成数字,如果有不是数字的值,则会返回NaN
------------------
Number('1') // 1
Number(true) // 1
Number('123a') // NaN
// 空字符串转为0
Number('') // 0
// 布尔值:true 转成 1,
//false 转成 0
Number(true) // 1
Number(false) // 0
// undefined:转成 NaN
Number(undefined) // NaN
// null:转成0
Number(null) // 0
parseInt(string):解析一个字符串并返回指定基数的十进制整数,
------------------
parseInt('a123') // NaN 如果第一个字符不是数字或者符号就返回NaN
parseInt('123a') // 123
parseInt('32a3') //32
String()//转换字符串
-------------------
String(1) // "1"
String("a") // "a" 字符串转换后还是原来的值
String(true) // "true"
String(false) // "false"
String(undefined) // "undefined"
String(null) // "null"
Boolean()可以将任意类型的值转为布尔值
-------------------
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(1)// true
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
隐式类型转换
-------------------
- * 不进行演示
一旦存在字符串,进行字符串拼接操作
'1' + 1 // '11'
'1' + true // "1true"
'1' + false // "1false"
'1' + {} // "1[object Object]"
'1' + [] // "1"
'1' + undefined // "1undefined"
'1' + null // "1null"
5、==和===的区别
(==)相等操作符会做类型转换,再进行值的比较,
(===)全等运算符不会做类型转换
'' == '0' // false
1 == '1' // true
1 === '1' // false
6、作用域
简单的理解就是变量的有效范围。
可分为:全局作用域、函数作用域、块级作用域
全局作用域
函数最外层定义的变量拥有全局作用域,函数内部是可以访问的
let inVariable = "全局作用域";
function fn() {
console.log(inVariable); // 全局作用域
}
fn();
函数作用域
函数作用域也叫局部作用域:如果一个变量是在函数内部声明的,那这些变量只能在函数下访问,不能再函数以外去访问
function fn() {
let inVariable = "函数内部变量";
console.log(inVariable)// "函数内部变量";
}
fn();
//报错
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
块级作用域
ES6引入了块级作用域 ,【由大括号包裹,比如:if(){},for(){}等】
let
和const
关键字,和var
关键字不同【后续讲解let、const、var区别】
在大括号中使用let
和const
声明的变量存在于块级作用域中。在大括号之外不能访问这些变量
{
// 块级作用域中的变量
let a = 'Hello';
var b = 'World';
console.log(a); // Hello
}
console.log(b); // 'World'
console.log(a); // 报错:Uncaught ReferenceError: a is not defined
如何访问函数内部的变量(本人面试时候有问到)
1、通过return访问:
function bar(value) {
var testValue = "hello word";
return testValue;
}
console.log(bar());//hello word
2、通过 闭包 访问函数内部变量
闭包的定义:闭包就是能够读取其他函数内部变量的函数
可以理解成“定义在一个函数内部的函数“
闭包是将函 数内部和函数外部连接起来的桥梁。”
function bar() {
var testValue = "hello";
var rusult = testValue + "Word";
function innser() {
console.log(rusult);//helloWord
return rusult;
}
return innser();
}
console.log(bar()); // "helloWord"
7、var、let、const之间的区别
var 声明变量可以重复声明,而 let 不可以重复声
var 是不受限于块级作用域的,而 let 是受限于块级作用域
var 可以在声明的上面访问变量,而 let 有暂存死区,在声明的上面访问变量会报错
var在全局作用域下声明变量会导致变量挂载在 window
上let、const 不会
const 声明之后必须赋值,否则会报错 const 定义不可变的量,改变了就会报错
const 和 let 一样支持块级作用域、在声明的上面访问变量会报错
上代码var篇
//用var声明的变量既是全局变量也是顶层变量,所谓顶层就是window
var a = 10;
console.log(window.a) // 10
//使用var声明的变量存在变量提升的情况
console.log(a) // undefined
var a = 20
//在编译阶段,编译器会将其变成以下执行
var a;
console.log(a); //undefined
a = 20;
console.log(a); //20
//var 多次声明
var a = 20
var a = 30
console.log(a) // 30
//在函数中使用使用var声明变量时候,该变量是局部的
var a = 20
function change(){
var a = 30
}
change()
console.log(a) // 20
let篇
//let 具有块级作用域
{
let a = 20
}
console.log(a) // 报错Uncaught ReferenceError: a is not defined
//不存在变量提升
console.log(a) // 报错Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 2
// let 不允许相同作用域下声明
let a = 20
let a = 30 //报错【累了具体报错不打印了】
const篇
//const声明一个只读的常量,一旦声明,常量的值就不能改变
const a = 1
a = 3 // 报错
//const一旦声明变量,就必须立即初始化,不能留到以后赋值
const a;
// SyntaxError: Missing initializer in const declaration
const实际上保证的并不是变量的值不能改动,而是变量指向的那个内存地址的数据不能改动。
复杂数据类型变量指向的是内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保改变量的结构不变
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.a = 123;
foo.a // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; //报错
8、闭包
闭包的概念就是:有权利访问另一个函数作用域中的变量,一般就是函数包裹着函数[之前有提到过闭包的定义]
闭包可以重用一个变量,且保证这个变量不会被污染的一种机制。这些变量的值始终保持在内存中,不会被垃圾回收机制处理
闭包的缺点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题
使用场景 :
防抖、节流、函数套函数避免全局污染
创建私有变量
延长变量的生命周期
//简单的闭包
function bar() {
var testValue = "hello";
var rusult = testValue + "Word";
function innser() {
console.log(rusult);//helloWord
return rusult;
}
return innser();
}
console.log(bar()); // "helloWord"
9、什么是内存泄漏
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在
10、原型、原型链
原型
- 每个函数都有一个
prototype
属性,被称为显示原型 - 每个引用数据类型都会有
_ _proto_ _
属性,其被称为隐式原型 - 每个引用数据类型,它的
_ _ proto_ _
属性指向它的构造函数的’prototype’属性。 - 每个prototype原型都有一个
constructor
属性,指向它关联的构造函数。 - 当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的
_ _ proto_ _
属性(也就是它的构造函数的’prototype’属性)中去寻找。
原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的proto隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的proto中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。直到顶层返回null
11、什么是递归
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数
注意:递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
12、什么是防抖什么是节流
防抖:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
应用场景:用户在输入框中连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减
少请求次数,节约请求资源;
节流:高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率(也可以理解:可以减少一段时间内事件的触发频率)
应用场景: 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;
区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
13、call,apply和bind的区别
相同点: 三个函数都会改变this的指向(调用这三个函数的函数内部的this)
不同点:
- call、apply与bind的差别
call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。(bind不会立即执行)
- call、apply的区别
他们俩之间的差别在于参数的区别,call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数
14、Localstorage、sessionStorage、cookie 的区别
共同点:都是保存在浏览器端的,且同源
localStorage:声生命周期永久生效,除非手动删除 否则关闭页面也会存在
sessionStorage:生命周期为关闭浏览器窗口。
存储大小:sessionStorage约5M、localStorage约20M
Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端(储存量4k左右)。cookie可以设置过期时间,到达时间后自动销毁,如果没有设置会随浏览器的关闭而销毁。cookei中储存的数据会伴随着每一次http请求发送到服务端。
15、同源策略
MDN官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制
通俗的理解:浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互
同源:协议,域名,端口,三者必须一致
跨域
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域
跨域,指的是从一个域名去请求另外一个域名的资源。即跨域名请求
跨域时,浏览器不能执行其他域名网站的脚本,是由浏览器的同源策略造成的,也是浏览器施加的安全限制。
怎么解决跨域
1、需要后台配置cors进行跨域,原理是给服务器设置一个响应头,然后浏览器将会允许跨域请求
2、jsonp:原理动态创建一个script标签,利用script标签的src属性不受同源策略的限制。所有的src属性和href都不会受同源策略的限制。可以请求第三方服务器的数据内容
3、反向代理
4、ngex
16、常见的http状态码
200:这个是最常见的http状态码,表示服务器已经成功接受请求,并将返回客户端所请求的最终结果
202:表示服务器已经接受了请求,但是还没有处理,而且这个请求最终会不会处理还不确定
204:服务器成功处理了请求,但没有返回任何实体内容 ,可能会返回新的头部元信息
301:客户端请求的网页已经永久移动到新的位置,当链接发生变化时,返回301代码告诉客户端链接的变化,客户端保存新的链接,并向新的链接发出请求,已返回请求结果
404:请求失败,客户端请求的资源没有找到或者是不存在
500:服务器遇到未知的错误,导致无法完成客户端当前的请求。
503:服务器由于临时的服务器过载或者是维护,无法解决当前的请求,
17、事件循环机制EventLoop
Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制。
首先我们需要知道JS 是⻔⾮阻塞单线程语⾔,就是同一时间只能做一件事情
同步任务和异步任务
javascript是单线程。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就意味着等待。于是javascript所有任务分为两种:同步任务,异步任务
同步任务:不需要等待可立即看到执行结果,比如console、promise 的回调
异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、setInterval、网络请求(ajax)、script 脚本的执行
异步任务分为:宏任务与微任务
宏任务:script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
微任务:Promise、 MutaionObserver、process.nextTick(Node.js环境)
- 在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务
- 当同步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中,不同的另一个任务队列中等待执行。
- 任务队列可以分为宏任务对列和微任务对列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队列的事件压入栈中执行。
- 当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。
执行顺序
1、先执行主线程
2、遇到宏队列,放到宏队列
3、遇到微队列,放到微队列,
4、主线程执行完毕
5、执行微队列,微队列执行完毕
6、执行一次宏队列,中的一个任务,执行完毕
7、执行微队列,执行完毕
8、依次循环
上代码:
setTimeout(function () {
console.log(1);
});
new Promise(function (resolve, reject) {
console.log(2);
resolve(3);
}).then(function (val) {
console.log(val);
});
console.log(4);
// 2431
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function(resolve, reject) {
console.log(2);
resolve()
}).then(function() {
console.log(3)
});
process.nextTick(function () {
console.log(4)
})
console.log(5)
//25431
1、主线程开始执行,遇到setTimeout,将setTimeout的回调函数丢到宏任务队列中
2、在往下执行new Promise立即执行,输出2
3、.then的回调函数丢到微任务队列中,再继续执行
4、遇到process.nextTick,同样将回调函数扔到为任务队列,再继续执行,输出5
5、当所有同步任务执行完成后看有没有可以执行的微任务
6、因为process.nextTick指定的异步任务总是发生在所有异步任务之前,因此先执行process.nextTick输出4,然后执行.then函数输出3,第一轮执行结束。
7、第二轮:从宏任务队列开始,发现setTimeout回调,输出1执行完毕,因此结果是25431