前端 html 、 js 、css 面试题

文章目录

1、基本数据类型

String、Number、Boolean、Null、Undefined、Symbol(ES6)
基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问。

2、引用数据类型

Object(在JS中除了基本数据类型以外的都是对象,Array是对象,Function是对象,正则表达式是对象)
引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。

3、JS 的栈和堆

在 JS 中每一个数据都需要一个内存空间,这个空间被分为两种:栈内存(stack)、堆内存(heap)。
1)、栈:

自动分配相对固定大小的内存空间,并由系统自动释放,这样内存可以及时得到回收,相对堆更容易管理内存空间;
基础数据类型都是存储在栈内存里面的.,每一个数据占据的内存空间大小是确定的(引用类型的地址也存在栈里);
js可以直接访问栈里存储的数据;
栈是线性结构,先进后出,便于管理;

2)、堆:

动态分配内存,内存大小不一,也不会自动释放.;
引用内存数据存储在堆中,但是引用类型的数据地址指针是存储在栈里面的;
我们要访问引用类型需要现在栈里面获取对象地址,然后根据地址找到堆里面对应的数据,不可以直接访问堆里面的数据;
杂乱无序,方便存储和开辟内存空间;

4、深拷贝和浅拷贝

浅拷贝: 只是将数据中所有的数据引用下来,依旧指向同一个存放地址,拷贝之后的数据修改之后,也会影响到原数据的中的对象数据
深拷贝: 将数据中所有的数据拷贝下来,对拷贝之后的数据进行修改不会影响到原数据

浅拷贝方法:

1、赋值运算
2、扩展运算符 (…)
3、Object.assign( target, …sources)
4、concat、 解构赋值等

深拷贝方法:

1、递归
2、JSON.parse( JSON.stringify( a ) ):这个方法无法转化 function 和 undefined。

5、宏任务和微任务

JS 是单线程的,任务按顺序一个一个执行,避免一个任务消耗时间太长后面任务只能等待;所以将 JS 分为同步任务和异步任务,而异步任务中又分为宏任务和微任务两种;

1)、宏任务由宿主(浏览器、node)发起的;微任务由 JS 引擎发起;
2)、宏任务(setTimeout、setInterval、postMessage、script 、setImmediate(node)),微任务(promise.then、process.nextTick(node)、MutaionObserver);
3)、同一层级,先执行微任务,在执行宏任务;

6、typeof返回那些数据类型

object、undefined、string、number、boolean、function

7、原型 和 原型链

1)、原型的五条规则

1、所有的引用类型都可以自定义添加属性
2、所有的引用类型都有自己的隐式原型(proto)
3、函数都有自己的显式原型(prototype)
4、所有的引用类型的隐式原型都指向对应构造函数的显示原型
5、使用引用类型的某个自定义属性时,如果没有这个属性,会去该引用类型的proto(也就是对应构造函数的prototype)中去找

2)、原型理解
原型:一个简单的对象,用于实现对象的属性继承
构造函数:可以通过 new 新建一个对象函数
实例:通过构造函数new出来的对象

实例.proto === 原型
实例.constructor === 构造函数
原型.constructor === 构造函数
构造函数.prototype === 原型
顶层Object prototype.proto == null

在这里插入图片描述
3)、原型链理解
原型链是由原型对象组成,每个对象都有_proto_属性指向创建该对象的构造函数的原型,_proto_把对象连接起来组成原型链。(实现继承和共享属性)
属性查找机制:查找对象属性时,实例对象自身不存在该属性,则沿着原型链往上一级查找找到则输出找不到继续往上一级查找,直到顶级的原型对象还没找到则输出undefined。
属性修改机制:只会修改实例对象本身属性。如不存在,则添加该属性。可以用b.prototype.x = 2修改属性值,然后所有继承于该对象的属性都会改变。
在这里插入图片描述
步骤:cat没有price,根据_proto_找到animal.prototype,如果animal.prototype找不到则根据_proto_找到Object prototype,Object prototype的_proto_是null
cat.proto = animal.prototype //20
步骤:tidy根据_proto_找到dog.prototype = animal,如果找不到则根据animal._proto_往上面找
tidy.proto=dog.prototype = animal //1000

8、js事件循环

1)、js 是单线程的:因为 js 主要用途是与用户互动和操作 dom 的,所以它必须是单线程的,防止两个线程同时操作 dom 浏览器无法知道以哪一个为准。

2)、任务队列:

1、单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
2、js 把任务分成两种,一种是同步任务,另一种是异步任务。同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行后一个任务;异步任务不进入主线程、而进入"任务队列"(task queue),只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

3)、js 运行机制:

1、所有同步任务在主线程上执行;
2、主线程之外还有一个任务队列,异步任务有了运行结果就在任务队列里面放置一个事件;
3、同步任务执行完毕之后,系统会读取任务队列里存放发事件,把对应的异步任务推到主线程开始执行;
4、主线程任务执行完毕之后,继续从任务队列里面读取事件;

只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复,整个的这种运行机制又称为Event Loop(事件循环)。

9、闭包

闭包就是可以读取其他函数内部的变量;JavaScript语言中,闭包就是“定义在一个函数内部的函数”,主要的用途是设计私有的方法和变量;主要运用的框架是 jquery。

特点

可以在函数外部访问函数内部的局部变量;
局部变量会常驻在内存中,实现数据共享(会造成内存泄露);

使用场景

匿名自执行函数
结果缓存
实现类和继承
封装私有变量
回调:定义行为,然后把它关联到某个用户事件上。
setTimeout:原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。
函数防抖

问题1:多个子函数指向同一个父级,父级变量修改时,所有子函数都受影响
变量通过函数参数的形式传入。
问题2:清除闭包的缓存
在使用完之后将局部变量设置为null或者删除掉。

10、new运算符执行过程

生成新对象
链接到原型:obj.proto = Object.prototype(this指向这个新对象)
绑定this:apply(执行代码给this赋值)
返回新对象

11、cookie、session和token区别

token和session安全性高,cookie可以分析存放在本地cookie进行cookie欺骗和截获
token不一定需要存储,session 必须存在服务器
token可以跨域,session和域名绑定

参考:web 开发用到的几种会话跟踪技术

12、JWT

JWT:Json Web Token,是基于 JSON 的一个公开规范,这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换

使用:由服务端根据规范生成一个令牌(token),并且发放给客户端。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。

作用:保持登录状态 的办法,通过token来代表用户身份。

组成:JWT实际上就是一个字符串,它由三部分组成:头部、载荷与签名

头部:声明类型 和 声明加密的算法的JSON,然后进行base64加密的字符串
载荷:存放有效信息的地方的JSON,然后进行base64加密的字符串
签名:这个部分需要base64加密后的头部和载荷连接组成的字符串,然后通过头部中声明的加密方式进行加盐secret组合加密

优点:

1、jwt基于json,非常方便解析
2、可以再令牌中自定义丰富的内容,易扩展(payload可以扩展)
3、通过签名,让JWT防止被篡改,安全性高
4、资源服务使用JWT可不依赖认证服务即可完成授权

13、数组去重

下面是我总结的几种方法以及实现代码,可以直接查看!
数组去重方法总结(10种)

14、判断数组是数组

1、isArray
2、instanceof
3、constructor
4、Object.getPrototypeOf()

检测数组的6种方法

15、ES6

1)、let 、const
声明的变量无变量提升,且会被锁定在声明的代码块中,变量声明前都是暂时性死区;
const 声明的对象只是引用无法修改,对象内部结构是可以改变的,可以使用 Object.freeze()来锁定对象,只锁定一层,多层级对象需要递归锁定。

function freeze(obj){
	let name = Object.getOwnPropertyNmaes(obj);
	name.forEach(name =>{
		let prop = obj[name];
		if(typeof prop == 'objdet' && prop != null){
			freeze(prop);
		}
	})
	return Object.freeze(prop);
}

2)、解构赋值:可以方便的从数组或者对象中快速提取值赋给定义的变量

let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'

3)、symbol:原始数据类型不可以用 new 构建,表示独一无二的值,可以作为对象的key,不被常规方法遍历出来,只能用 Object.getOwnPropertySymbols()或者 Reflect.ownKeys ()返回全部 key。

// 相同参数 Symbol() 返回的值不相等
 Symbol("kk") !=  Symbol("kk")

//作为对象的属性名只能用方括号
let sy = Symbol('key1')
let obj = {}
obj[sy] = 'ss'
console.log(obj) //{symbol(key1):'ss'}

Symbol.for() 和 Symbol.keyFor()
Symbol.for(): Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索;
Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key;一定是登记过的才会返回;

let s1 = Symbol('foo');
let s2 = Symbol.for('foo');
let s3 = Symbol.for('foo');
s1 === s2 // false
s2 === s3 // true

Symbol.keyFor(s1) // undefiend
Symbol.keyFor(s2) // "foo"

4)、Map 和 set: Map 一般用来存储需要频繁取用的数据, Set 一般用来判断某个值是否存在其中.
Map:保存键值对,具有极快的查找速度
Map和Object的区别:

Object的键只能是字符串或者Symbol,Map的键可以是任意值。
Map中的键值是有序的(FIFO 原则)类似于数组,而添加到Object中的键则不是。
Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

Map对象的属性和方法

size:返回Map对象键值对个数
set(key,value):添加新元素
get(key):通过key值查找对应的值并返回
has(key):判断是否有key对应的属性,返回布尔值(强等判断===)(可以用NaN做key,不可以用{}、函数做key)
delete(key):删除key对应的数据
clear():清空Map所有元素

Map遍历方法

keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器(Iterator)
forEach():使用回调函数遍历每个成员
for…of:遍历每个成员

Map对象操作

与Array的转换:Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象;Array.from 函数以及扩展运算符[…map]可以将一个 Map 对象转换成一个二维键值对数组
合并:如果有重复的键值,则后面的会覆盖前面的

**Set:**允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
在Set里面 +0和-0、undefined、NaN 都是不会重复的。

Set对象的属性和方法

size:返回Set实例的成员总数。
add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
delete(value):删除某个值,删除成功返回true,否则返回false。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。

5)、Proxy和Reflect
Proxy 代理对象的各种内置方法,get set construct等,类似于拦截器。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

//target 即目标对象, handler 是一个对象(get、set方法进行拦截和修改,apply、call拦截函数的调用),声明了代理 target 的指定行为。
let proxy = new Proxy(target, handler)

Reflect 则作为Object的替代者,Object上的一些静态方法被移植到了Reflect上。可以用于获取目标对象的行为。
(get、set、has、construct、apply、defineProperty、deleteProperty、ownKeys、isExtensible、preventExtensions、getOwnPropertyDescriptor、getPrototypeOf、Reflect)

6)、字符串

include():是否找到参数字符串;str.include(‘参数字符串’)
startsWith():参数是否在字符串头部;str.startsWith(‘参数字符串’)
endsWith():参数是否在字符串尾部;str.endsWith(‘参数字符串’)
repeat():将字符串重复指定次数返回;str.repeat(重复次数) :小数向下取整、NaN取0、负数和infinity报错
padStart():用参数从头部补全字符串;str.padStart(指定字符串补全长度,参数字符串)
padEnd():用参数从尾部补全字符串;str.padEnd(指定字符串补全长度,参数字符串)
模板字符串

注意:补全长度小于等于原字符串长度返回原字符串;原字符串加上补全字符串长度大于指定长度,则截去超出位数的补全字符串:;常用于补全位数

7)、数字

Number.isFinite():数值是否为有限
Number.parseInt():数字转成指定整数
Math.trunc():取整数部分
Math.fround():取小数部分
Math.sign():判断数字的符号(正、负、0)。

8)、对象

属性、方法名简写:const person = {age, name}
对象拓展运算符(…):取出参数对象所有可遍历属性然后拷贝到当前对象,也可合并对象(可以覆盖相同属性)
Object.assign(target,resource):将源对象的所有可枚举属性复制到目标对象中
Object.is(value1,value2):比较两个值是否严格相等,与(===)基本类似

9)、数组

Array.of():将参数中所有值作为元素形成数组
Array.from():将类数组对象或可迭代对象(Map、Set、字符串)转化为数组
find():查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素
findIndex():查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引
fill(替换值,开始索引,结束索引):将一定范围索引的数组元素内容填充为单个指定的值
entries():遍历键值对
keys():遍历键名
values():遍历键值
includes():数组是否包含指定值
flat(num):将多维数组降指定参数维度;自动过滤空位,Infinity 必然得到一维数组
flatMap():先对数组中每个元素进行了的处理,再对数组执行 flat() 方法
扩展运算符(…):跟对象一样

10)、函数

函数参数默认值
不定参数:表示不确定参数个数(…变量名)
箭头函数:自身没有this,this指向外部环境

11)、迭代器(Iterator)
新的遍历机制,作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
可迭代的数据:Array、String、Map、Set、DOM元素、arguments;可以使用 for…of 遍历
普通对象不可迭代

let arr = [1,2,3,4,5];
let it = arr[Symbol.iterator]();
it.next()
//{value:1,done:false}

12)、class类
本质是 function ,用来定义类;
内置 constructor 方法,创建类的实例化对象时调用;静态方法使用 static

class example{
	constructor(){} //构造函数方法
	static sun(){} //静态方法
	list(){} //原型方法
}
example.sun() //执行静态方法
let it = new example()//调取constructor
it.list() //执行原型方法

注意:

class 的实例化必须通过 new 关键字。
通过 extends 实现类的继承。
子类 constructor 方法中必须有 super ,且必须出现在 this 之前。

13)、模块化:export 和 import 组成

模块可以导入导出各种类型的变量;import 命令会提升到整个模块的头部,首先执行;多次重复执行同一句 import 语句,那么只会执行一次.;import 是静态执行,所以不能使用表达式和变量
不同模块导出接口名称命名重复, 可以使用 as 重新定义变量名:export {a as b} ;import {a as b} from xxx;a重命名为b
在一个文件或模块中,export、import 可以有多个,export default 仅有一个

export { foo, bar } from "methods";
// 约等于下面两段语句,不过上面导入导出方式该模块没有导入 foo 与 bar
import { foo, bar } from "methods";
export { foo, bar };

14)、promise对象
三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)
缺点:

无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

方法:

then()
catch()
all()
race()
finally()

可参考:ES6 Promise基础知识总结

15)、Generator 和 yield
可以生成一个迭代器(Iterator),每次iterator.next()返回yield的产出值,且中断程序执行。换句话说:可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。

组成: function 后面,函数名之前有个 * ;函数内部有 yield 表达式。

function* func(){
	console.log(1)
	yield '1';
	console.log(2)
	yield '2';
}
let fn = func();
fn.next() //1 
fn.next() //2
16、promise如何终止链式调用

1)、通过reject来中断(特定条件满足后)

return Promise.reject('break without exception.')

2)、通过抛出一个异常来终止

throw "we need break";

3)、返回“pending”状态的Promise对象

Promise.resolve().then(() => {
	console.log('ok1')
	return new Promise(()=>{}) // 返回“pending”状态的Promise对象
}).then(() => {
	// 后续的函数不会被调用
	console.log('ok2')
}).catch(err => {
	console.log('err->', err)
})

4)、Promise.race:最先改变状态的实例返回的值将会是race的返回值。

let p1 = new Promise((resolve, reject) => {
	resolve('ok1')
})
let p2 = new Promise((resolve, reject) => {
	setTimeout(() => {resolve('ok2')}, 10)
})
Promise.race([p2, p1]).then((result) => {
	console.log(result) //ok1
}).catch((error) => {
	console.log(error)
})
17、promise和async/await区别

1)、promise:异步编程的解决方案,为了解决传统的callback函数回调地狱问题,支持链式调用,三种状态(),存放着未来将要执行的结果,结果不可逆,业务复杂时需要不停的then,这样不美观;

2)、async/await:基于promise 的异步解决方案,遵循 Generator 函数的语法糖,拥有内置执行器,不需要额外的调用会自动执行和返回结果,返回的是一个 promise 对象;解决promise里面不停使用then的问题;

区别:

1、promise是ES6,async/await是ES8
2、async/await基于promise实现的,它不能用于普通的回调函数
3、promise链式调用,自己catch异常,async要在函数内catch
4、async/await使得异步代码看起来像同步代码
5、async/await与Promise一样,是非阻塞的
6、需要注意的是async指定了函数 才能在那个函数里面写await

18、如何让 a== 1 && a== 2 成立?(js)

利用 js 的隐式类型转换 valueOf() 和 toString(),Symbol.toPrimitive()

var a = {
	value: 0,
	valueOf () {
		return ++this.value;
	}
}
if (a == 1 && a == 2 && a == 3) {
	console.log('成立');
}

利用数组转化字符串会隐式调用join()

let a = [1, 2, 3]
a['join'] = function () {
	return this.shift()
}
if (a == 1 && a == 2 && a == 3) {
	console.log('成立')
}
19、ES5继承和ES6继承区别

继承:拥有另一个对象的方法和属性
1)、ES5:基于原型链,让原型对象等于另一个类的实例;(call改变this指向)
先创建子类,在实例化父类并添加到子类的this中。
父.call(子) => 子.prototype = new 父()
2)、ES6:基于class个extends,子类通过执行super方法继承父类的this对象。
父类先创建出来,在实例化子类通过调用super方法访问父类,然后修改子类的this;

20、判断是否是整数?(js)

1)、除以1,余数为0

function isInteger(obj) { return obj%1 === 0 }

2)、取整后还等于自己

function isInteger(obj) { return Math.floor(obj) === obj }

3)、ES6提供了Number.isInteger

21、this指向

可查阅:javascript的this指向问题

22、箭头函数可以做构造函数吗?可以通过call修改this吗?为什么?

箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。

箭头函数特点:

没有自己的 this,this指向外部环境,通过 call() 或 apply() 方法调用一个函数时只能传递参数,不能绑定 this;因此第一个参数会被忽略。
箭头函数没有 prototype 属性;但是有 prop 属性,所以箭头函数本身是存在原型链的,它也有自己的构造函数,但是原型链到箭头函数就停止了,所以不能作为构造函数。
没有自己的 arguments,在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值

23、for循环里面加定时器
for(var i = 0;i<4;i++){
	setTimeout(function(){
		console.log(i)
	})
}
//3 3 3 3

因为 var 定义的 i 是全局变量,加上定时器是异步操作,等循环结束之后定时器里面的 i 都变成了3。

如果想返回 0,1,2,3该如何修改呢?
1)、利用闭包
每次循环都把 i 值保存下来

for(var i = 0; i < 5; i++) {
    (function(i) {
        setTimeout(function () {
            console.log(i);
        });
    })(i)
}    

2)、将 var 换成 let
因为 let 关键字劫持了循环的块作用域,产生类似闭包的效果;

3)、try…catch
catch后面的花括号是一个块作用域,和let的效果一样;在try语句块里抛出循环变量i,然后在catch的块作用域里接收到传过来的i,就可以将循环变量保存下来,实现类似闭包和let的效果。

for(var i = 0; i < 4; i++) {
   try {
        throw(i)
    } catch(j) {
        setTimeout(function () {
            console.log(j);
        });
    }
} 
24、执行上下文、执行栈

1)、执行上下文类型:全局上下文、函数上下文(可以把执行上下文理解为一段代码执行需要准备的环境)

  • 全局上下文:基础的上下文,函数中的代码都要在全局执行上下文中。它创建全局对象(window);将 this 指向 window,var 和 function 定义的变量提升,添加为 window 的属性和方法;一个程序中只能存在一个全局执行上下文。
  • 函数上下文:每次函数调用时都会创建一个新的 执行上下文;

2)、执行上下文生命周期:创建-执行-回收

1、变量声明提升:var 声明的变量会被提升了
2、函数声明提升:函数声明(function aa() {})的函数会被提升,函数表达式(var aa = function () {})声明的则会将变量提升
3、当函数和变量同名且都会提升的情况下,函数优先级高;变量声明先提升,然后被函数声明覆盖掉,但是变量可以重新赋值
4、this的值在执行的时候才会确定,定义的时候不能确定。

3)、执行栈:

1、在全局代码执行前,JS 会创建一个栈来存储管理所有的执行上下文对象;
2、创建一个全局执行上下文来执行全局代码并放入执行栈底部;
3、当遇到函数被调用时开始将函数代码封装为执行上下文放入执行栈;(函数有调用关系的话则会排队执行)
4、JS 调用总是从栈顶开始调用执行,执行完之后出栈,等待垃圾回收;
5、全局上下文在浏览器窗口关闭后出栈;

(var声明的变量存储在变量环境,函数声明、let和const存储在词法环境)

25、script标签上面的属性

async:可选,异步加载,不妨碍页面中的其他操作,加载完之后立即执行;对外部文件有效
defer:可选,异步加载,不妨碍页面中的其他操作,延迟到文档完全被解析和显示之后再执行;只对外部脚本文件有效。

26、判断两个对象相等

1)、通过JSON.stringify(obj)来判断两个对象转后的字符串是否相等
优点:用法简单,对于顺序相同的两个对象可以快速进行比较得到结果
缺点:当两个对比的对象中key的顺序不是完全相同时会比较出错

2)、Object.keys()

// 列出所有的键,接着遍历数组
function ifCompare(object1, object2) {
	var o1keys = Object.keys(object1);
	var o2keys = Object.keys(object2);
	if (o2keys.length !== o1keys.length) return false;
	for (let i = 0; i <= o1keys.length - 1; i++) {
		let key = o1keys[i];
		if (!o2keys.includes(key)) return false;
		if (object2[key] !== object1[key]) return false;
	}
	return true;
}

3)、ES6

Object.entries(object1).toString() === Object.entries(object2).toString();
27、JS模块化规范

JS常规实现模块化:

1、直接全局写方法作为模块:可以直接调用,但是污染了全局变量,模块和成员直接看不出直接的关系;
2、对象写法:会暴露所有模块成员,内部可被改写;
3、立即执行函数(闭包):外部无法读取内部变量;

1)、AMD(异步加载模块)
AMD 规范需要用到 RequireJS库,主要用于浏览器端的模块化规范;RequireJS是依赖前置,先去执行依赖的模块,然后再执行当前模块。

定义模块:define([依赖的模块],function(){自定义模块})
引入模块:require([依赖的模块], function(){ //回调 })

// math.js
define(function (){
  var add = function (x,y){
   return x+y;
  };
  return {
   add: add
  };
});
// main.js
require(['math'], function (math){
  alert(math.add(1,1));
});

优缺点:

优:适合浏览器环境的异步加载,可以并行加载多个模块;
缺:书写困难,提高开发成本,不符合通用的模块化思维;

2)、CMD(异步加载模块)
CMD规范和AMD很相似,简单,并与CommonJS和Node.js的 Modules 规范保持了很大的兼容性;在CMD规范中,一个模块就是一个文件
定义模块使用全局函数define,其接收 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串;
factory 是一个函数,有三个参数,function(require, exports, module):

require 是一个方法,接受模块标识作为唯一参数,用来获取其他模块提供的接口:require(id)
exports 是一个对象,用来向外提供模块接口
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法

define(function(require,exports,module){
	let a = require('./b');
})

优缺点:

优:依赖就近,延迟执行,可以很容易在 Node.js 中运行;
缺:依赖 SPM 打包,模块的加载逻辑偏重;

3)、CommonJS(同步加载模块):
CommonJS 主要用于服务端的模块化规范,比如:node、webpack;
CommonJS定义的模块分为三种:模块引用(require);模块定义(exports);模块标识(module)。require()用来同步引入外部模块,exports对象用于导出当前模块,或者当前的模块的方法和变量,module对象代表模块本身;

let list = require('list.js');
let coint = 0;
exports.info = function(){}; //到处单个
module.exports = {//导出多个
	count
}

优缺点:

优:简单易用,便于服务器模块的复用
缺:同步加载,不适合浏览器上使用,因为会出现阻塞加载(模块都是放在服务器上的,加载会很快,浏览器端想获取的话会受到网速的限制,所以会出现阻塞现象)

4)、UMD
UMD是AMD和CommonJS的糅合,先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式;在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

5)、ES6模块化(import 、export )
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块设计思想:尽量的静态化、使得编译时就能确定模块的依赖关系,以及输入和输出的变量(CommonJS和AMD模块,都只能在运行时确定这些东西)。

优缺点:

优:容易进行静态分析,面向未来的ES标准
缺:兼容性还没有做好

AMD 与 CMD 的区别

AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require
CMD是延迟执行;AMD是提前执行,requireJS从2.0开始可以延迟执行
CMD的API推崇职责单一,没有全局的require;AMD的API默认是一个当多个用

require 和 import 的区别
require用于CommonJs规范,import使用于Es6模块规范;所以两者的区别实质是两种规范的区别;
1)require

对于基本数据类型,属于复制。即会被模块缓存;同时,在另一个模块可以对该模块输出的变量重新赋值。
对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
当使用require命令加载某个模块时,就会运行整个模块的代码。
当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

2)import

ES6模块中的值属于【动态只读引用】。
对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。
原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

需要知道 import/export 最终都是编译为 require/exports 来执行的。

28、判断对象是空

1)、将对象转化为json字符串,再判断该字符串是否为"{}"

var data = {};
var b = (JSON.stringify(data) == "{}");
conosle.log(b)

2)、for in 循环判断

var obj = {};
var b = function() {
	for(var key in obj) {
		return false;//进了循环说明不是空
	}
	return true;
}
conosle.log(b)

3)、jquery的isEmptyObject方法

var data = {};
var b = $.isEmptyObject(data);
conosle.log(b)

4)、Object.getOwnPropertyNames()方法

var data = {};
var arr = Object.getOwnPropertyNames(data);
console.log(arr.length == 0)

5)、使用ES6的Object.keys()方法

var data = {};
var arr = Object.keys(data);
console.log(arr.length == 0)
29、Instanceof的原理,如何实现

原理:从当前引用的proto一层一层顺着原型链往上找,能否找到对应的prototype,找到了就返回true。
实现: L的 proto 是不是强等于 R.prototype,不等于再找 L.proto .proto 直到 proto 为 null 。

//console.log(L instanceof R)
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 
    var O = R.prototype;   // 取 R 的显示原型 
    L = L.__proto__;  // 取 L 的隐式原型
    while (true) {    
        if (L === null)      
             return false;   
        if (O === L)  // 当 O 显式原型 严格等于  L隐式原型 时,返回true
             return true;   
        L = L.__proto__;  
    }
}
30、封装promise定时器
function timeout(delay = 100) {
	return new Promise(resolve => {
		setTimeout(resolve, delay)
	})
};
timeout(2000).then(()=>{
	console.log('sssss')
})
31、事件委托原理,为什么用事件委托?

原理:利用事件冒泡原理将触发子元素的事件绑定到父元素上,利用e.target获取子元素实现事件触发
用处:由于对新生成的子元素无法绑定事件 这个时候就可以用事件委托的方法实现动态添加的新元素的触发事件

32、多线程和单线程

1)、多线程:程序执行时,可以同时运行多个不同的线程来执行不同的任务。
优点:
提高cpu利用率;
提高程序的效率;
缺点:
占用内存随线程数增加;
线程太多控制复杂;
多线程需要协调管理,需要CPU时刻跟踪线程;
对共享资源的访问会相互影响;
2)、单线程:程序执行时,必须前面处理好了才能执行后面的。

33、进程和线程的区别

两个名词都是CPU工作时间片的一个描述
进程:程序开始运行时,它就是一个进程;描述了CPU在运行指令及加载和保存上下文所需的时间,放在应用上来说代表了一个程序
线程:是进程中的更小单位,描述了执行一段指令所需的时间
(在浏览器中来说,打开一个tab其实是创建了一个进程,一个进程有多个线程,js引擎单线程、http请求线程等)

34、针对前端js单线程和Web Worker多线程

js单线程却可以执行异步任务,主要是是js存在事件循环(enent loop)和任务队列(task queue)。
js当前线程中没有任何同步代码后才会执行异步代码;setTimeout是异步代码,只有等js空闲才会执行。
web worker是H5新标准,为js创造多线程环境;允许主线程创建worker线程,主线程运行同时worker线程在后台运行两者互不干扰;worker线程运行完会把结果返回给主线程。
web worker一般处理计算密集或高延迟任务,保证主线程流畅。
创建worker:var worker = new Worker(js文件路径)
web Worker使用
参考:HTML Web Workers
webworkers限制:同源限制;无法使用document、window、alert、confirm;无法加载本地资源

35、函数柯里化

函数先填充几个参数,返回一个新的函数的技术
作用:在不侵入函数的前提下为函数预置通用参数,供多次使用

const add = function add(x){
	return function(y){
		return x + y;
	}
}
const add1 = add(1);
add1(2) //3
add1(20) // 21

(add里面传递参数是x,add1里面参数是y)

36、防抖与节流

是一种常用的高频触发优化方式(延时器setTimeout)
1)、防抖:
在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后200ms内没有再次触发滚动事件,那么就执行函数;在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时(常用于输入框)

function debounce = (fn, delay) => {
	var delay = delay || 500;
	var timer;
	return function() {
		var th = this;
		var args = arguments;
		if(timer) {
			clearTimeout(timer);
		}
		timer = setTimeout(function() {
			timer = null;
			fn.apply(th, args);
		}, delay);
	};
}

节流:每隔一段时间后执行一次,降低频率(每次触发事件都判断当前是否有等待执行的延时函数);常用于滚动

function throttle = (fn, interval) => {
	var last;
	var timer;
	var interval = interval || 500;
	return function() {
		var th = this;
		var args = arguments;
		var now = +new Date();
		if(last && now - last < interval) {
			clearTimeout(timer);
			timer = setTimeout(function() {
				last = now;
				fn.apply(th, args);
			}, interval);
		} else {
			last = now;
			fn.apply(th, args);
		}
	}
}
37、for of 遍历对象

for…of…: 它是es6新增的一个遍历方法,但只限于迭代器(iterator), 所以普通的对象用for…of遍历是会报错的。下面来说明一下如何用for…of…遍历普通对象

1)、类数组对象:用Array.from转成数组即可

var obj = {
    0:'one',
    1:'two',
    length: 2
};
obj = Array.from(obj);
for(var k of obj){
    console.log(k)
}

2)、非类数组对象:添加一个[Symbol.iterator]属性,并指向一个迭代器即可

var obj = {
    a:1,
    b:2,
    c:3
};
obj[Symbol.iterator] = function*(){
    var keys = Object.keys(obj);
    for(var k of keys){
        yield [k,obj[k]]
    }
};
for(var [k,v] of obj){
    console.log(k,v);
}
38、怎样在ES5中实现 let 和 const

1)、可以通过匿名函数和闭包的形式来模拟let,用匿名函数的作用域来模拟块级作用域

(function(){
	var a = 3;
	console.log(a) //3
})
console.log(a) //undefined

2)、const:除了块级作用域,还不能修改,不能重复声明

数据属性
定义在对象内部的数据有四个特征:

configurable:是否可以被 delete 删除或者改变特征值
enumerable:是否能通过 for-in 循环遍历返回属性
writable:是否可以修改属性的值
value:保存这个属性的数据值

function _const(key, value) {
  window[key] = value;
  Object.defineProperty(window, key, {
    enumerable: false,
    configurable: false,
    get: function () {
      return value;
    },
    set: function (newValue) {
      if (newValue !== value) {
        throw TypeError("这是只读变量,不可修改");
      } else {
        return value;
      }
    },
  });
}
_const('a',1)
_const('a',2) //报错
39、ES5模拟箭头函数

箭头函数是没有自己的 this 的,使得this被强制绑定到第一个外层作用域,同时也不允许 new 箭头函数

function newArrowCheck(innerThis, outThis) {
    if (innerThis !== outThis) {
      throw new TypeError("Cannot instantiate an arrow function");
    }
}
var outThis = this;
var arrow = function arrow(b){
	newArrowCheck(this,outThis);
	return b;
}.bind(this);

先记录函数外部作用于的this
将外部的this通过bind绑定到arrow内部
arrow内部对比外部this和内部this,一致则通行,不一致则报错,这样arrow内部使用的就是外部环境的this了

40、balel编译原理

babel是一个转译器,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。
但是和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

1、ES6代码输入
2、babylon进行解析,得到AST
3、plugin用babel-traverse对AST树进行遍历转译 ,得到新的AST树
4、用babel-generator通过AST树生成ES5代码

41、前端缓存

浏览器第一次向服务器发起请求拿到结果,会根据 http 头的缓存标识决定是否缓存结果,是则将结果和缓存标识存入浏览器缓存中;
1)、http缓存:强缓存、协商缓存
强缓存:200
Expires:代表资源过期时间,由服务器返回提供(http1.0),在与max-age(http1.1)共存的情况下,优先级要低
Cache-Control:

  • no-store:所有内容不缓存
  • no-cache:缓存,但是每次缓存都会请求浏览器判断缓存资源是否是最新
  • max-age=x(秒):请求缓存后 x 秒内不在发起请求(http1.1)
  • s-maxage=x:代理服务器(CDN)请求源站缓存后 x 秒不在发起请求(只对 CDN 有效)
  • public:客户端和代理服务器(CDN)都可缓存
  • private:只有客户端可以缓存

协商缓存:304

  • Last-Modified / If-Modified-Since:第一次访问一个资源的时候,服务器返回最后修改时间 Last-Modified,再次请求时请求头里会带上 If-Modified-Since,值为上次请求服务器返回的 Last-Modified;对比值是否相同(相同则没修改),确认资源在这段时间是否修改过,没有返回304,有返回200,获取最新资源。
  • Etag / If-None-Match:Etag是通过一个校验码来对比资源是否更改过的,一个资源修改时校验码会改变。第一次请求,服务器返回一个 Etag字段,再次请求时请求头会带上 If-None-Match,值是上次返回的 Etag,然后对比。

2)、浏览器缓存:本地缓存(cookie、localStorage、sessionStorage)、默认缓存
本地存储小容量
cookie:4kb,请求时传递给服务器
localStorage:5M,永久存储
sessionStorage:5M,当标签页被关闭时,SessionStorage也会被清除

本地存储大容量:(在线编辑浏览器或者网页邮箱)
WebSql:50M左右,关系型数据库,已被 W3C 废弃。
IndexDB:50M左右,非关系型数据库,正常使用。

往返缓存:又被称为 BFCache,是浏览器前进后退按钮上为了提升渲染速度的一种策略;当用户前往新页面的时候,将当前页面的DOM保存到 BFCache 中,用户后退时直接从 BFCache 中加载,节省请求时间。

3)、缓存的好处

1、减小服务器压力
2、节省客户端流量,避免重复请求同样的内容

42、重绘和回流

重绘:当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。

回流必将引起重绘,而重绘不一定会引起回流。

43、Canvas和svg的区别

canvas是H5提供的新绘图方法,svg很早就出现了
canvas基于像素点,是位图,放大缩小会失真,svg是用html标签绘制,放大缩小不会失真
canvas在js里面绘制,svg在html里面
canvas支持颜色比svg多
canvas无法修改已经绘制的图像,svg可以获取到标签进行修改

44、浏览器最小字体是多少,如何实现10px

Chrome浏览器最小是12px
用Chrome的私有属性禁用浏览器文字大小调整的功能:-webkit-text-size-adjust:none;
或者

display:inline-bilck;
transform: scale(0.5)
45、页面加载过程(url输入)

向浏览器输入地址
浏览器根据DNS服务器解析得到域名下的IP地址
向这个地址服务器发送HTTP请求
服务器接收到,处理并返回HTTP请求
浏览器接收服务器返回内容并渲染

46、浏览器渲染过程

解析HTML生成DOM树,解析CSS生成CSSOM树(两者互不影响)
将DOM和CSSOM树结合,生成渲染树(只包含可见节点)
根据生成的渲染树,得到节点的几何信息进行布局,也叫回流
根据渲染树和回流得到的几何信息得到节点的像素进行重绘
将像素发送给 GPU ,然后通过操作系统的 Native GUI 的API 绘制
渲染过程(HTML、CSS)中遇到 js 会先暂停,等 js 执行完毕才会恢复构建
js 可以改变HTML 和 CSS ,所以需要在 HTML 和 CSS 构建完毕之后加载
如果CSS还没有构建完,js 想修改它,浏览器会延迟脚本执行和DOM 构建,直到完成 CSSOM的下载和构建在执行 js
script 标签加 async :异步加载js ,加载完就执行; defer:异步加载 js,等文本渲染结束后执行;

47、常见状态码:

200:OK(请求成功)
204:No Content(服务器已经成功处理,但是未返回内容)
304:Not Modified(缓存未修改,服务器不返回资源,从缓存拿取)
400:Bad Request (客户端请求的语法错误)
401:Unauthorized(请求要求用户身份认证)
402:Payment Required (保留,将来使用)
403:Forbidden (服务器拒绝请求)
404:Not Found (接口不存在)
500:Internal Server Error(服务出错)
502:Bad Gateway(网关、代理服务器请求时出错)

48、cookie 的属性有哪些,什么情况下属性不可以改变?

name(String ): cookie 名称,一旦创建不可改
value(Object): cookie 值
maxAge(int):Cookie失效时间,秒(正数,则Cookie在maxAge秒之后失效;负数,该Cookie为临时Cookie,关闭浏览器即失效)
secure(boolean):Cookie是否仅被使用安全协议传输。安全协议有HTTPS,SSL等
domain(String ):可以访问该Cookie的域名
path(String ):该Cookie使用路径
comment(String ):该Cookie的用处说明
version(int):该Cookie使用的版本号
size(int):大小

49、对称加密和不对称加密

1)、对称加密:最快速、最简单的加密方式,加密和解密用的同样的密钥;
对称加密一般使用相对较小的密钥,一般在256B;因为密钥越大,加密越强,但是速度越慢;密钥的大小需要考虑安全性也要照顾到效率;

2)、不对称加密:为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥:公钥(public key)和私钥(private key);
私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。

总结:

对称加密加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。
非对称加密使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。
解决的办法是将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。

50、元素在图层里的顺序

背景 => z-index为负 => 块级元素 => 浮动元素 => 行内元素 => z-index为正

51、1px问题

1)、媒体查询利用设备像素比缩放,设置小数像素(小数像素兼容差)
2)、viewport + rem 方案(适合新项目,不适合老项目改动)
3)、设置border-image(需要精确的图片,圆角可能模糊)
4)、background-image渐变(无法实现圆角)
5)、box-shadow(没有圆角问题,缺点是颜色不好控制)
6)、transform:scale(0.5)(比较推荐)

52、优雅降级和渐进增强(css)

优雅降级:向下兼容,先构建站点的完整功能,后针对浏览器测试和修复。(高版本->低版本)
渐进增强:向上兼容,先针对低版本浏览器构建页面,后针对高版本浏览器追加功能达到更好的体验。(低版本->高版本)

53、margin合并

当两个垂直外边距(兄弟元素、父子元素)相遇时,它们将合并,合并后的外边距的高度等于高度较大的边距;水平方向不会合并。

1、只设置一个元素的边距
2、触发BFC

54、margin塌陷

当给一个块级元素的第一个子元素设置margin-top时会带动父级盒子一起下移

1、父级元素设置边框或者padding边距(不推荐使用)
2、触发BFC

55、BFC(块级格式上下文)

具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。

触发:

body 根元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)

特点:

同一个 BFC 下外边距会发生折叠
BFC 可以包含浮动的元素(清除浮动;父元素添加,防止子元素脱离文档流)
BFC 可以阻止元素被浮动元素覆盖

56、水平垂直居中

1)、利用lineheight
适用行内块元素

{
	height:200px;
	line-height:200px;
	text-align:center;
}

2)、flex布局

{
	display: flex;
	justify-content: center;
    align-items: center;
}

3)、absolute + transform
需要给父元素添加 relative

	position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

4)、absolute + margin auto
需要给父元素添加 relative

	position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;

5)、absolute + 负margin
需要给父元素添加 relative,并且已知子元素宽高(100px)

 	position: absolute;;
    top: 50%;
    left: 50%;
    margin-left: -50px;
    margin-top: -50px;

6)、css-table

.father {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
.sun{
    display: inline-block;
}
57、H5和CSS3

1)、H5

1、语义标签(有利于代码可读性和SEO):header、aside、time、footer等
2、新表单元素:type、email验证、url验证、autofocus等
3、canvas标签:绘制图像
4、支持内联SVG
5、拖放(drag、drop)
6、地理位置(Geolocation):用于定位用户的位置
7、视频和音频(video、Audio)
8、本地存储:sessionStorage、localStorage和indexedDB加强本地存储
9、Web SQL数据库
10、Web Workers:运行在后台的 JavaScript,不会影响页面的性能
11、离线web应用:在没有网络状态可以运行应用
12、SSE(服务器发送事件):网页自动获取来自服务器的更新
13、WebSocket:单个 TCP 连接上进行全双工通讯的协议

2)、CSS3

1、边框:圆角、阴影、边界图片
2、背景:图片、尺寸、定位区域、绘制区域
3、渐变
4、文本效果:阴影、换行、单词拆分
5、字体:加粗、样式、拉伸
6、2D 、3D
7、过渡:transition
8、动画:animation、@keyframes

58、CSS样式叠加的结果

如果多个样式有重合,浏览器会根据权重的大小进行样式覆盖

1、!important,加在样式属性值后,权重值为 10000
2、内联样式,如:style=””,权重值为1000
3、ID选择器,如:#content,权重值为100
4、类,伪类和属性选择器,如: content、:hover 权重值为10
5、标签选择器和伪元素选择器,如:div、p、:before 权重值为1
通用选择器(*)、子选择器(>)、相邻选择器(+)、同胞选择器(~)、权重值为0

59、移动端适配方案(vw)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值