延迟加载JS有哪些方式?
延迟加载:async、defer
例如:<script defer type="text/javascript" src="script.js"></script>
例如:<script async type="text/javascript" src="script.js"></script>
区别:
defer:等html全部解析完成,才会执行js代码,顺次执行的
async:async是和html解析是同步的(一起的),不是顺次执行js脚本(谁先加载完谁执行)
js的数据类型有哪些
基本类型:string、number、boolean、undefined、null、symbol、bigint
引用类型:object(data、function、array)
注意:NaN是个数值类型,但是, 不是一个具体的数字
笔试题拓展:
console.log(true + 1); // 1
console.log("name" + true); // name true (字符串和其他类型相加会变成连接形式)
console.log(undefined + 1); // NaN
console.log( typeof undefined); // undefined
console.log( typeof null); // object
console.log( null == undefined ); // true
console.log( string == number); // true
console.log( boolean == number); // true
console.log( object == number || string .......); // true
null和undefined的区别
1.作者在设计js时先设计的null(为什么设计null:最初设计js的时候借鉴了java语言)
2.null会被隐式转换成0,很不容易发现错误。
3.先有null后有undefined,出来undefined是为了填补之前的坑
具体区别:JavaScript是最初版本是这样区分的:null是一个表示"无"的对象(空对象指针),转为数值0;undefined是一个表示"无"的原始值,转为数值时为NaN
== 和 ===有什么不同
== : 比较的是值
string == number || boolean || number ....都会隐式转换
在valueOf不被重写的情况下, 通过valueOf转换(valueOf() 方法通常由javascript在后台自动调用, 并不显式地出现在代码中)
===: 除了比较值,还会比较数据类型
类型不相等就会返回false, 不会走valueOf()方法,在性能上比==好
js微任务和宏任务
1.js是单线程语言,同是只能执行一件事
2.js代码执行流程:同步执行完==》事件循环(even loop事件循环机制)
同步任务都执行完了,才会执行事件循环的内容
进入事件循环的有:请求、定时器、事件...
3.事件循环包含了【微任务、宏任务】
微任务:promise.then
宏任务: setTimeout..
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务==》微任务...
拓展笔试题:
题目一
for(var i=0; i<3; i++) {
setTimeout(function() {
console.log(i)
},1000*i)
}
console.log(2222)
请写出打印结果:
2222
3
3
3
题目二
setTimeout(function() {
console.log(1)
})
new Promise((resolve) => {
console.log("1 promise 1"); //同步
resolve();
}).then(()=> {
console.log("微1")
})
.then(()=> {
console.log("微2")
})
console.log(222);// 同步
请写出打印结果:
1 promise 1
222
微1
微2
1
js作用域考题***特别重要
1.除了函数外,js是没有块级作用域的
2.作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的
3.注意声明变量是var还是没有写(window.)
4.注意:js邮编按量提升的机制()
5.优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
面试的时候怎么看:
1.本层作用域有没有此变量【注意变量提升】
2.注意:js除了函数外没有块级作用域
3.普通声明函数是不看写函数时候的顺序
拓展题:
题目一:
(function() {
var a = b = 10
})()
console.log(a) // undefined
console.log(b) // 10
题目二:
function c() {
var b = 1;
function a() {
console.log(b)
var b = 2;
console.log(b)
}
a();
console.log( b )
}
c()
请写出打印值:
undefined
2
1
题目三:
var name = "a"
(function() { // var name
if(typeof name === "undefined") {
var name = "jack"
console.log("再见" + name)
} else {
console.log("再见" + name)
}
})()
请写出打印值:
再见jack
题目四:
function fun() {
var a = 10;
function a() {}
console.log(a)
}
fun(100)
请写出打印值:
10
JS对象考题
js对象注意点:
1. 对象是通过new操作符构建出来的,所以对象之间不相等(除了引用类型外)
2.对象注意:引用类型(共同一个地址)
3.对象的key都是字符串类型;
4.对象如何找属性 或 方法;
查找规则: 先在对象本身找 ===》 构造函数中找 ===》 对象原型中找 ===》 构造函数原型中找==》对象上一层原型查找
考题一:
[1,2,3] === [1,2,3] //false
考题二:
var obj1 ={
a:'hellow
}
var obj2 = obj1;
obj2.a='world';
console.log(obj1); //{a:world}
(function(){
console.log(a); //undefined
var a=1;
})();
考题三
var a = {}
var b = {
key: "a"
}
var c = {
key:'c'
}
a[b] ='123';
a[c]='456;
console.log( a[b] ); // 456
js作用域+this指向+原型 考题
考题一
function Foo() {
getName = function() { console.log(1) } //注意全局的window
}
Foo.getName = function() { console.log(2) }
Foo.prototype.getName = function() { console.log(3) }
var getName = function() { console.log(4) }
function getName() {
console.log(5)
}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo().getName(); // 3
请输出打印值:
2
4
1
1
3
考题二
// 头条考题
var o = {
a: 10,
b: {
fn: function() { //这里的this指向b
console.log(this.a)
console.log(this)
}
}
}
o.b.fn();
请输出打印值:
undefined
{fun:f}
考题三
// 头条考题
window.name = 'byteDance'
function A() {
this.name = 123
}
A.prototype.getA = function() {
console.log(this)
return this.name + 1
}
let a = new A()
let funcA = a.getA;
funcA()
请写出打印值:
byteDance1
考题四
// 头条考题
var length = 10;
function fn(){
return this.length + 1;
}
var obj = {
length: 5,
test1: function(){
return fn();
}
}
obj.test2 = fn;
console.log( obj.test1() ); // 1
console.log( fn() === obj.test2() ); // false
console.log( obj.test1() == obj.test2() ); // false
请输出打印值:
1
false
false
js判断变量是不是数组,你能写出哪些方法?
方式一: isArray
var arr = [1,2,3];
var str = '你好'
console.log(Array.isArray( arr ))
方式二:instanceof
var arr = [1,2,3];
var str = '你好'
console.log(arr instanceof Array | Object)
方式三:原型prototype
var arr = [1,2,3];
var str = '你好'
console.log(Object.prototype.toString.call(arr).indexOf('Array') != -1)
方式四:isPrototypeOf()
var arr = [1,2,3];
var str = '你好'
console.log(Array.prototype.isPrototypeOf(arr))
方式五:constructor
var arr = [1,2,3];
var str = '你好'
console.log(arr.constructor.toString().indexOf('Array') != -1)
slice是干嘛的,splice是否会改变原数组
// slice是来截取的 参数可以写slice(1) slice(1,3) slice(-3) 返回的是一个新的数组
// splice 功能有: 插入、删除、替换 会改变原数组
var arr1 = ['a','b','c','d','e']
var arr2 = arr1.slice(1,3)
console.log(arr2)
var arr3 = ['a','b','c','d','e']
var arr4 = arr3 .splice(1,1)
console.log(arr4)
js数组去重
方式一:new Set()
var arr1 = [1,2,3,1,2,5,4]
console.log(Array.from(new Set(arr1)))
console.log([...new Set(arr1)])
方式二:indexOf
var arr1 = [1,2,3,1,2,5,4]
function unique( arr ) {
var brr = []
for(var i = 0; i < arr.length; i++) {
if (brr.indexOf(arr[i]) == -1) {
brr.push([i])
}
}
return brr;
}
方式三
var arr1 = [1,2,3,1,2,5,4]
function unique( arr ) {
arr.sort();
var brr = [[arr[0]]]
for(var i = 0; i < arr.length; i++) {
if(arr[i] !== arr[i-1]) {
brr.push(arr[i])
}
}
retun brr;
}
console.log(unique(arr1))
找出多维数组中每个数组的最大值
function fnArr(arr) {
var newArr = []
arr.forEach((item,index) => {
newArr.push(Math.max(...item))
})
return newArr
}
console.log(fnArr([
[4,5,1,3],
[13,27,18,26],
[32,35,37,39],
[1000,1001,857,1]
])) //[5, 27, 39, 1001]
给字符串新增方法实现功能
String.prototype.addPrefix = function(str) {
return str + this
}
cosole.log('你'.addPrefix('好')) // 你好
找出字符串中出现最多次数的字符以及次数
var str = 'aaaaabbbbbbbccccccccdddddddddddw'
var obj = {}
for(var i = 0; i < str.length; i++) {
var char = str.charAt(i)
if (obj[char]) {
obj[char]++
} else {
obj[char] = 1
}
}
console.log(obj)
// 最大值
var max = 0
for(var key in obj) {
if (max < obj[key]) {
max = obj[key]
}
}
console.log(max)
// 拿最大值对比
for(var key in obj) {
if (max == obj[key]) {
console.log('最多的字符是' + key)
console.log('出现的次数是' + max)
}
}
new操作符具体做了什么
1.创建了一个空的对象
2.将空对象的原型,指向于构造函数的原型
3.将空对象作为构造函数的上下文(改变this指向)
4.对构造函数有返回值的处理判断
function create(fn, ...args) {
// 1.创建了一个空对象
var obj = {} // var obj = Object.create({})
// 2. 将空对象的原型,指向于构造函数的原型
Object.setPrototypeOf(obj,fn.prototype);
// 3. 将空对象作为构造函数的上下文 (改变this指向)
var result = fn.apply(obj, args)
// 4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log(create(Fun, 18, '张三'))
闭包
1.闭包是什么
---闭包是一个函数加上到创建函数的作用域的连接,闭包关闭了函数的自由变量
2.闭包可以解决什么问题【闭包的优点】
--内部函数可以访问到外部函数的局部变量
--闭包可以解决很多问题
var lis = document.getElementsByTagName("li")
for(var i = 0; i < lis.length; i++) {
(function(i) {
lis[i].onclick = function() {
console.log(i)
}
lis[i] = null;// 设置为空,解决内存损耗
})(i)
}
3.闭包的缺点
--变量会驻留在内存中,造成内存损耗问题
解决: 把闭包的函数设置为空
--内存泄漏【IE浏览器】可说可不说,如果说一定要提到IE浏览器低版本会造成内存泄漏问题
原型链
1.原型可以解决什么问题
--对象共享属性和方法
2.谁有原型
函数拥有:prototype
对象拥有:__proto__
3.对象查找属性或者方法的顺序
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型 --> 当前原型的原型中查找
4.原型链
-->是什么?:就是把原型串联起来
-->原型链的最顶端是null
JS继承有哪些方式
方式一:ES6
class Parent {
constructor() {
this.age = 18
}
}
class Child extends {
constructor() {
super()
this.name = "张山"
}
}
let o1 = new Child()
console.log(o1, o1.name, o1.age)
方式二:原型链继承
function Parent() {
this.age = 20
}
function Child() {
this.name = "张山"
}
Child.prototype = new Parent()
let o2 = new Child()
console.log(o2, o2.name, o2.age)
方式三:构造函数继承
function Parent() {
this.age = 20
}
function Child() {
this.name = "张山"
Parent().call(this)
}
let o2 = new Child()
console.log(o2, o2.name, o2.age)
方式四:组合继承
function Parent() {
this.age = 20
}
function Child() {
Parent().call(this)
this.name = "张山"
}
Child.prototype = new Parent()
let o2 = new Child()
console.log(o2, o2.name, o2.age)
说一下call、apply、bind区别
共同点:功能一致
--》可以改变this指向
语法:函数.call()、函数.apply()、函数.bind()
区别:
-->call、apply可以立即执行、bind不会立即执行,因为bind返回的是一个函数,需要加入()执行
-->参数不同:apply第二个参数是数组,call和bind有多个参数需要挨个写。
应用场景:
call场景和bind区别只是立即执行问题
sort排序背后的原理是什么
V8 引擎 sort 函数只给出了两种排序 InsertionSort和QuickSort,数量小于1e的数组使用 InsertionSort,比1@大的数组则使用 QuickSort
之前的版本是:插入排序和快排,现在是冒泡
原理实现链接: https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js
***718行代码开始***
深拷贝和浅拷贝
共同点:复制
浅拷贝:只复制引用,而未复制真正的值
var arr1 =['a','b','c']
var arr2 = arr1;
var obj1 = {a:1,b:2}var obj2 = 0bject.assign(obj1);
深拷贝:是复制真正的值(不同引用)
var obj3 = {
a:1,
b:2}
var obj4 = JSON.parse(JSON.stringify( obj3 ));// 递归
function copyobj( obj ){
if( Array.isArray(obj) ){
var newObj = []
} else {
var newObj = {};
}
for( var key in obj ) {
if( typeof obj[key] == object' ){
newObj[key] = copyobj(obj[key]);
} else {
newObj[key] = obj[key];
return newObj;}
}
总结:
浅拷贝:浅拷贝会在栈内存复制一份,当我们改变复制引用地址的数据时原地址数据也会跟着改变
深拷贝:在复制数据的时候会将栈内存里面的数据在堆内存中创建,当我们去改变数据的时候改变的是堆内存里面的数据而不会改变栈内存里面的原数据,两者互不影响
localstorage、sessionstorage、cookie的区别
公共点:在客户端存放数据
区别:
数据存放有效期:localstorage数据持久存储、sessionstorage仅在浏览器窗口关闭之前有效、cookie可以设置过期时间,只在设置cookie过期时间之前有效,即使浏览器关闭也有效、注意需要是线上环境
3.存储大小
cookie: 不超过4k
localstorage和sessionstorage不能超过5M
根据浏览器不同大小也不同,值是有波动的
var、let、const区别
共同点:都是可以声明变量的
区别:
-->var 具有变量提升, let和const不存在变量提升
-->var可以多次声明同一个变量, let和const不可以多次声明同一个变量
-->var、let用来声明变量,(var、let用来声明的变量可以再次赋值),const用来声明常量,(const不可以再次赋值,特殊情况const可以修改内部变量)
-->var声明的变量没有自身的作用域,let和const声明的变量有自身的作用域
考题一、let和const没有变量提升性
考题二
demo()
考题三
将下列对象合并
方式一:Object.assign()
方式二:...
方式三:自己封装
箭头函数和普通函数有什么区别?
1.this指向的问题
--》箭头函数中的this只在箭头函数定义时就决定的,而且不可修改(call、apply、bind)
****箭头函数的this指向定义时候、外层第一个普通函数的this
2.箭头函数不能new(不能当做构造函数)
3.箭头函数没有prototype原型
4.箭头函数没有arguments对象
Promise有几种状态
有三种状态分别为:pending(进行中)fulfilled(已成功)和reject(已失败)
解决回调地狱防止多次请求
异步解决方案,同步写法
因为一致.then不利于后期维护,结果出现了generator,就是promise语法糖,通过yield实现
因为generator还不够简洁,async和await又出现了使代码更简洁,后期更好的维护
find和filter的区别
--》返回的内容不同(filter返回的是新数组,find返回的是具体内容)
--》find匹配到第一个即返回,filter返回整体(没一个匹配到的就返回)
some和every的区别
some:如果有一项匹配就会返回true
every:需要全部匹配才会返回true,
http和https的区别
http传输的数据都是未加密的,明文的,http协议传输是不安全的,为了保证数据安全传输,就诞生了https
https是由ssl和http协议构建的,可以进行加密传输,身份认证的网络协议要比http协议更安全
https两个作用:
--》简历一个信息安全通道,用来保证数据传输的安全性
--》确认网站的真实性
总结:
--》http不需要证书,而https需要申请ca证书,而一般免费的证书很少,因此需要一定的费用成本
--》http下的信息是明文传输的,而https协议是由ssl加http协议构建的,可进行加密传输,身份认证的网络协议可以防止传输内容被窃取篡改,比http协议更加安全
--》http和https使用的是完全不同的连接方式,所以他们的端口也不一样,http是80端口,后者是443端口
get和post请求的区别
1.get请求的参数是放在url里面的,post请求的参数是放在请求体里面的
2.get请求可以被浏览器缓存,而post请求是不能被缓存的
3.get请求因为是放在url里面的,而url里面的长度是受限的,他最大长度是2048个字符,而post请求是没有限制的,
4.同样是get请求是放在url里面所以安全性是比较差的,post请求是放在body里面,安全性相对好一些
5.get请求支持浏览器直接访问,支持刷新和后退,post请求呢是不能被浏览器直接访问的,刷新后数据需要重新传送
token和session有什么区别
session弊端
1.首先是服务器的资源占用比较大,session是存储在服务器的内存之中,随着用户量的增加服务器压力就会比较大
2.session的安全性比较低session是基于cookie进行用户识别的,如果cookie被截获了,那么用户就非常容易收到跨站请求伪造的攻击
3.拓展性不强,随着用户量的增加,系统如果使用了多服务器的负载均衡方案,那session数据呢是保存在单节点中的,用户第一次访问的是服务器1,那用户再次请求时可能是另外一台服务器2,那服务器2就会获取不到用户的信息,就会判定用户未登录过,
总结:
如果我们改用token的认证机制那由于服务端不保存session了,他只是生成token,然后验证token,然后用服务器CPU的计算时间换取了session的存储空间,而且即使是使用了多台服务器的集群,只要每台机器的处理逻辑算法是一样的,这就意味着基于token真正的认证,不需要去考虑用户在那一台服务器登录过,这就为应用的拓展提供了便利性,解决了session拓展性的弊端