记录近期面试题目,以及论坛里其余技术大佬的面试题梳理,缓慢更新中。。。
HTML
H5标签语义化
- H5新增标签:
<header> <footer> <article> <canvas> <section> <nav> <video>
- 语义化优点:便于阅读和维护,便于对浏览器、搜索引擎解析
WebStorage、cookie、session区别
- session是服务器端用于存储数据,存储特定用户会话所需的属性和配置信息。
- cookie、sessionStorage、localStorage是用于浏览器端存储数据。
- cookie数据不能超过4k,适用于会话标识,设置了cookie后,数据会发送到服务端,cookie在设置了cookie过期时间之前一直有效,即使关闭窗口或者浏览器。
- webStorage数据存储可达到5m,且数据保存在本地不会发送到服务端。localStorage数据存储永久有效,sessionStorage仅在关闭浏览器之前有效。
特性 | Cookie | localStorage | sessionStorage |
数据的生命期 | 一般由服务器生成,可设置失效时间。如果在浏览器生成,默认是关闭浏览器后失效 | 除非被清除,否则永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清楚 |
存放数据大小 | 4k左右 | 一般为5MB | |
与服务器端通信 | 每次都会携带在HTTP头中,如果使用Cookie保存过多数据会带来性能问题 | 仅在浏览器中保存,不参与和服务器的通信 |
localStorage.getItem('name');
localStorage.setItem('name','hk');
localStorage.removeItem('name');
HTML自定义属性data-*
<div id="test" ></div>
读写方式:
- 直接在元素标签书写:
<div id="test" data-age="24"></div>`
- js操作:
let str=document.getElementById("test");
str.dataset.age="24";
CSS
行内元素、块级元素、空元素
- 行内元素:宽度、高度由内容决定,与其他元素共占一行的元素。例如
<input> <i><span> <a>
- 块级元素:默认宽度由父容器决定,默认高度由内容决定,独占一行并可以设置宽高的元素。例如
<p> <h1>~<h6> <div> <ul> <table>
- 可通过设置
display:inline-block
,打破其壁垒。
display
可选值:inline
、block
、inline-block
。inline
是将对象内联展示,即都在同一行内,不能设置其宽高;block
是将对象作为块级元素展示,另起一行;inline-block
是将对象呈递为内联块级,既在同一行内,也能设置其宽高。
display:inline-block
时元素间会因为标签段之间的空格会产生间隙,消除方法有:- 设置元素
margin
为负值 - 设置父容器
font-size
为0,元素font-size
为1rem
- 设置元素
CSS reset
重置CSS样式,尽可能使网页在各个浏览器中相差不大,在网上找reset.css文件引用即可。
CSS盒模型
box-sizing
属性可设置为border-box
和content-box
border-box
中,整个div的宽、高包括padding、bordercontent-box
中,div的宽、高则不包括以上元素。
CSS单位
单位 | 描述 |
---|---|
% | 百分比 |
px | 像素,绝对单位,计算机屏幕一个点为1px |
em | 相对单位,相对父元素运算。如果父元素字体大小为14px,子元素为2em,则子元素字体大小为28px |
rem | 相对单位,相对根元素html运算。如果html元素字体大小为14px,子元素为2rem,则子元素字体大小为28px |
CSS选择器及其优先级
- 通配符:*
- ID选择器:#id
- 类选择器:.class
- 元素选择器:p div
- 后代选择器:p span
- 伪类选择器:a:hover
- 属性选择器:input[type="text"]
- 子元素选择器:li:first-child
!important>行内样式>#id>.class>元素和伪元素>*>继承>默认
CSS常见布局
-
水平垂直居中
- Flex实现
<div class="container"> <div class="center"></div> </div>
.tcontainer{ width: 900px; height: 300px; margin: 0 auto; background-color: gray; display: flex; justify-content: center; align-items: center; } .tcenter{ width: 100px; height: 100px; background-color: #42b983; }
- position:absolute实现
.container{ width: 900px; height: 300px; margin: 0 auto; background-color: gray; position: relative; } .center{ width: 100px; height: 100px; background-color: #42b983; position: absolute; top: 50%; left: 50%; /* 下面两种方式均可 */ /*margin-left: -50px;*/ /*margin-top: -50px;*/ transform: translate(-50%,-50%); }
- 水平居中
- 行内元素:
display: inline-block; text-align: center;
- 块级元素:
margin: 0 auto
- Flex:
display: flex; justify-content: center;
- 行内元素:
- 垂直居中
- Flex:
display: flex; align-items: center;
- Flex:
-
两列布局,一侧固定,另一侧自适应
- Flex布局
<div class="container"> <div class="left"></div> <div class="right"></div> </div>
.container { display: flex; } .left { width: 100px; height: 300px; background-color: aliceblue; } .right { width: 100%; height: 300px; background-color: pink; }
- float布局
.left { width: 100px; height: 300px; background-color: aliceblue; float: left; } .right { margin-left: 100px; height: 300px; background-color: pink; }
-
三列布局 可参考两列布局
BFC
-
BFC:块级格式化上下文,是一个独立的渲染区域,使得BFC内部元素和外部元素定位不会互相影响。
-
作用:防止
margin
重叠,清除内部浮动 -
产生规则:
display:inline-block;position:absolute/fixed
-
拓展:HTML有浮动流、定位流、文档流。文档流即正常HTML的渲染规则,块级元素新开一行、内联元素公用一行。浮动流和定位流能打破文档流,使得元素原本所在位置被清除,脱离文档流。设置了
float
,或者position
为absolute
或fixed
时,会脱离文档流。
CSS3新特性
- transition 过渡
- transform 旋转、缩放、移动或者倾斜
- animation 动画
- gradient 渐变
- shadow 阴影
- border-radius 圆角
- @Keyframes规则,指定从目前样式更改为新的样式
<div id="red"></div>
#red {
width: 50px;
height: 50px;
animation: mymove 2s;
position: relative;
/*以下非必须 */
border-radius: 50%;
box-shadow: 10px 10px 5px #888888;
background: linear-gradient(deeppink,black);
}
@keyframes mymove {
from {
background-color: deeppink;
left: 0px;
}
to {
background-color: green;
left: 200px;
}
}
Less
CSS预编译器,添加变量、嵌套规则、混合、函数等技术,使CSS更具维护性、扩展性。
网站页面适配hack
待更新
画三角形
#div1{
width: 0;
height: 0;
border: 5px solid transparent;
border-bottom: 5px solid red;
}
浏览器如何解析CSS选择器
.mod p span{ color:red}
浏览器从右向左解析,先找到html中所有class='red'
的span
元素,找到后再查找其父元素中是否有p
元素,再判断p
元素的父元素中是否包含mod
类,如果都存在则匹配上。
JavaScript
js数据类型
- 基本数据类型(
Null
、Number
、String
、Boolean
、Undefined
、Symbol
)和引用数据类型(Object
)。 - 基本数据类型属于值类型,存放在栈内存中,
==
时比较值,===
时比较数据类型和值。 - 引用数据类型可以细分为
Object
、Array
、Function
、Date
等,存放在堆内存中,比较时是引用的比较(即其地址)。 - 类型判断
typeof
、instanceof
。
var a = {};
typeof a; //Object
a instanceof Object; //true
typeof
会把Null
类型和Object
都返回Object
类型,并不能细分出Array
、Function
。判断是否为数组方法:
var array = [];
console.log(array instanceof Array);
console.log(Array.isArray(array));
console.log(array.constructor === Array);
console.log(Object.prototype.toString.apply(array) === "[object Array]");
比较运算符
- 严格比较运算符
===
和转换比较运算符==
。对于严格比较运算符,会比较操作数类型和值,对于转换比较运算符,会在进行比较前,转换两个操作数为相同的类型。 - NAN不和任何值相等,包括自身,正数0和负数0恒等。
- 对于
null
和undefined
而言,使用严格运算符比较自身,转换运算符进行互相比较。
var b, a = null;
console.log(a == b);//true
console.log(a === b);//false
var c, d = NaN;
console.log(c == d);//false
console.log(c === d);//false
console.log(+0 == -0);//true
console.log(+0 === -0);//true
var e, f;
console.log(e == f);//true
console.log(e === f);//true
ajax的步骤
//第一步,创建XMLHttpRequest对象
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//第二步,配置请求信息
xhttp.open("get", "", true);
//post请求时,必须配置请求头信息
//xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//第三步,发送请求(post的参数在此处配置传递)
xhttp.send();
//第四步,创建回调函数
xhttp.onreadystatechange = function () {
if (xhttp.readyState === 4 && xhttp.status === 200) {
}
}
get方式传参,参数的提取方法
getParams: function () {
let url = "http://index?a=1&b=2&c";
url = url.substr(url.indexOf("?"));
let result = {};
if (url.includes("?")) {
let str = url.substr(1);
str = str.split("&");
for (let i = 0; i < str.length; i++){
result[str[i].split("=")[0]] = str[i].split("=")[1];
}
}
return result;
}
原型与原型链
-
原型链
function Person(name, age) { this.name = name; this.age = age; this.eat = function (name) { console.log("eating"); } } Person.prototype.dance = function (name) { console.log(name + "dancing"); } var p1 = new Person("hk", "26"); var p2 = new Person("tdf", "25"); console.log(p1.__proto__ === Person.prototype); //true console.log(Person.prototype.__proto__ === Object.prototype); //true console.log(Object.prototype.__proto__ === null); //true console.log(p1.constructor === Person); //true console.log(Object.getPrototypeOf(p1) === p1.__proto__); //true
- 每个实例对象(
p1
、p2
)都有一个私有属性(__proto__
)指向它的原型对象(prototype
)。该原型对象也能看做是实例对象,通过其私有属性(__proto__
)指向它的原型对象,层层向上直到一个对象的原型对象为null
。根据定义,null
没有原型,并作为原型链中的最后一个环节。 - 实例对象
p1
,构造函数Person
,原型对象Person.prototype
之间关系为:
//仅实例具有__proto__属性,仅构造函数具有prototype属性 p1.__proto__ === Person.prototype; Person.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null; p1.constructor === Person; //由上得出寻找p1的原型过程还能写为: p1.__proto__.__proto__.__proto__ === null; //从ES6开始,__proto__属性可以通过Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问 Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(p1))) === null //p1的原型链为: //p1-->Person.prototype-->Object.prototype-->null
- 每个实例对象(
-
new运算符作用
- 在JavaScript中,创建对象和生成原型链的方法包括:词法结构创建对象、构造器创建对象、
Object.create
创建对象、class
关键字创建对象。 - 词法结构创建对象
var o = {a: 1}; //原型链: //o-->Object.prototype-->null var a = [1, 2, 3]; //原型链: //a-->Array.prototype-->Object.prototype-->null
- 构造器创建对象(
new
运算符)
在 JavaScript 中,构造器其实就是一个普通的函数。当使用
new
操作符来作用这个函数时,它就可以被称为构造方法(构造函数)。var p1 = new Person("hk", "26"); //实际执行内容如下: //创建一个空对象 var p1 = new Object(); //将空对象的原型指向Person.prototype p1.__proto__ = Person.prototype; //将构造函数Person的this指向新对象p1,执行构造函数并返回新对象 Person.call(p1,"hk","26");
Object.create
创建对象
var a = {a: 1}; // a--> Object.prototype--> null
class
关键字创建对象
ES6引入了一套新关键字用来实现
class
,这些新的关键字包括class
、constructor
、static
、extends
、super
。class
本质是Function
。class animal { constructor(name) { this.name = name; } } class duck extends animal { constructor() { super("duck"); } } var a = new duck(); console.log(animal instanceof Function); //true
- 在JavaScript中,创建对象和生成原型链的方法包括:词法结构创建对象、构造器创建对象、
-
继承
-
原型链继承
基本思想是让子类型原型对象等于超类型的实例。
function SuperType() { this.property = true; this.colors = ["red", "blue"]; } SuperType.prototype.getSuperValue = function () { return this.property; }; function SubType() { this.subproperty = false; } //核心:子类型原型对象等于超类型的实例 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); //true
缺点:1、超类型中的引用类型值将被所有子类型实例共享。2、创建子类型实例时,不能像超类型的构造函数中传递参数。
var instance = new SubType(); instance.colors.push("black"); console.log(instance.colors); //["red", "blue","black"] var instance2 = new SubType(); console.log(instance2.colors); //["red", "blue","black"]
-
借用构造函数
基本思想是在子类型构造函数的内部调用超类型构造函数。
function SuperType() { this.colors = ["red", "blue"]; } function SubType() { //调用超类型构造函数 SuperType.call(this); } var instance = new SubType(); instance.colors.push("black"); console.log(instance.colors); var instance2 = new SubType(); console.log(instance2.colors);
优点:1、解决了原型链继承问题。
缺点:超类型原型中的方法对子类型是不可见的。
-
组合继承
基本思想是使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。
function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType(name,age) { //继承属性 SuperType.call(this,name); //第二次调用SuperType() this.age = age; } SubType.prototype = new SuperType(); //第一次调用SuperType() SubType.prototype.sayAge = function () { console.log(this.age); }; var instance = new SubType("jack",26); instance.colors.push("black"); console.log(instance.colors); //[ 'red', 'blue', 'black' ] instance.sayName(); //jack instance.sayAge(); //26 var instance2 = new SubType("rose",25); console.log(instance2.colors); //[ 'red', 'blue' ] instance2.sayName(); //rose instance2.sayAge(); //25
优点:避免了原型链继承和借用构造函数继承的缺陷,融合了他们的优点,是JavaScript中最常用的继承模式。
缺点:调用了两次超类型的构造函数,第一次调用时,
SubType.prototype
会得到name
、colors
属性,第二次调用时,会在新对象创建实例属性name
、colors
。于是,这两个属性就屏蔽了原型中的两个同名属性。可采用寄生组合式继承解决该问题。 -
this
指向以及call
、apply
、bind
方法
-
call
、apply
方法在函数内部,
this
的值取决于函数被调用的方式。如果要想把this
的值从一个环境传到另一个,就要用call
或者apply
方法。当函数在其主体中使用this
关键字时,可以通过使用函数继承自Function.prototype
的call
或apply
方法将this
值绑定到调用的特定对象。function add(c, d) { console.log(this.a + this.b + c + d); } let obj = {a: 1, b: 2}; //第一个参数是作为this使用的对象 //call和apply的区别是参数不同 add.call(obj, 3, 4); add.apply(obj, [3, 4]);
-
bind
方法调用
f.bind(someObject)
会创建一个与f
具有相同函数体和作用域的函数,但是在这个新函数中,this
将永久地被绑定到bind
的第一个参数,无论这个函数是如何被调用的。call
和apply
方法调用后会立即执行,bind
方法不会。function f() { console.log(this.a) } let g = f.bind({a: 1}); g(); //1 let h = g.bind({a: 2}); h(); //1
-
箭头函数中
this
指向箭头函数不会创建自己
this
,它只会从自己作用域链上一层继承this
(即this
与封闭词法环境中的this
保持一致)。在全局代码中,它的this
指向全局变量,在其他函数体内,this
被设置为封闭词法环境的this
。function Person() { this.name = "name"; setTimeout(function () { console.log(this.name + "ing"); //undefineding }, 0); //this正确指向p实例 setTimeout(() => console.log(this.name + "growing")); //namegrowing } let p = new Person();
-
对象的方法
当函数作为对象的方法被调用时,它的
this
指向调用该函数的对象。var obj = { prop: 2, f: function () { console.log(this.prop); } }; obj.f(); //2
-
构造函数
构造函数中
this
指向的是new
创造的新对象。
作用域与闭包
-
作用域
当函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性(即[[scope]])。然后使用this、arguments和其他命名参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象处于第二位,外部函数的外部函数的活动对象处于第三位,直到作为作用域链终点的全局执行环境。
-
闭包
闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了该闭包创建时能访问的所有局部变量。通俗理解是,函数B定义在函数A内部,且使用了函数A的局部变量,则B被称为闭包。
function init() { var name = "name"; function f() { console.log(name); } f(); } init();
闭包常见问题
for (var i = 1; i < 4; i++) { setTimeout( function () { console.log(i); }, 0) }
for循环是同步代码,先执行然后生成三个闭包函数,这三个函数共享同一个作用域,setTimeout是异步代码,此时for循环结束,i=4,所以会输出4、4、4。
解决办法:
1、使用let,每个闭包绑定的是块级作用域的变量
for (let i = 1; i < 4; i++) { setTimeout( function () { console.log(i); }, 0) }
2、 使用立即执行函数
for (var i = 1; i < 4; i++) { (function (i) { setTimeout(function () { console.log(i); }, 0) })(i); }
-
性能问题
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多内存,过度使用闭包可能会导致内存占用过多。
深拷贝与浅拷贝
定义两个变量a和b,a=b,当修改b时,a不发生变化则为深拷贝,a发生变化则为浅拷贝。由数据类型可以得出,当b为引用类型数据时,会发生浅拷贝。
深拷贝实现:
-
JSON.stringify
和Json.parse
实现function deepClone(obj) { let _obj = JSON.stringify(obj); let result = JSON.parse(_obj); return result; }
-
递归实现深拷贝
function deepClone(obj) { let newobj = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === "object") { for (key in obj) { //判断是否自有属性 if (obj.hasOwnProperty(key)) { //如果属性值为对象,则递归拷贝 if (obj[key] && typeof obj[key] === "object") { newobj[key] = deepClone(obj[key]); } else { newobj[key] = obj[key]; } } } } return newobj; }
防抖与节流
-
防抖(debounce):任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。核心是对于一个时间段的连续的函数调用,只让其执行一次。
function debounce(fn, interval = 300) { let timeoutid = null; return function () { clearTimeout(timeoutid); timeoutid = setTimeout(() => { fn.apply(this, arguments); }, interval); } }
应用场景:
- 每次resize/scroll触发统计事件
- 文本输入的验证(连续输入文字后发送AJAX请求进行验证,验证一次就好)
-
节流(throttle):指定时间间隔内只会执行一次任务。核心是让一个函数不要执行得太频繁,减少一些过快的调用来节流。
function throttle(fn, interval = 300) { let canRun = true; return function () { if (!canRun) return; canRun = false; setTimeout(() => { fn.apply(this, arguments); canRun = true; }, interval); } }
应用场景:
- DOM元素拖拽功能(mousemove)
- 搜索联想(keyup)
- 监听滚动事件判断是否到页面底部自动加载更多,给scroll加了debounce后,只有用户停止滚动才会判断是否到了页面底部,如果是throttle,只要页面滚动就会间隔一段时间判断一次
数组操作
-
数组的检测
-
数组模拟栈和队列
栈:后进先出,数组的
pop()
、push()
方法模拟var array = []; array.push("red"); array.push("green"); array.push("black"); array.pop();
队列:先进先出,数组的
shift()
、push()
方法模拟。(也可使用unshift()
、pop()
方法模拟)var array = []; array.push("red","green","black"); array.shift();
-
数组常用方法
contact()
,数组连接,返回一个新数组。slice(start,end)
,数组截取,返回一个新数组。splice(start,num,value)
,数组删除、插入或替换,修改原数组indexof/lastIndexof(value,fromIndex)
,查找数组项,返回对应的下标every()
,对数组的每一项运行给定函数,如果每一项都返回true,则返回truefilter()
,对数组的每一项运行给定函数,返回该函数返回true的项组成的数组forEach()
,对数组的每一项运行给定函数,无返回值map()
,对数组的每一项运行给定函数,返回每次函数调用结果组成的数组some()
,对数组的每一项运行给定函数,如果任一项返回true,则返回truereduce/reduceRight(fn(prev,cur,index,array))
,数组缩小方法reverse()
,将原数组的顺序进行反转sort()
,按升序排列数组项join()
,通过指定连接符生成字符串
ES5高级技巧
-
判断是否原生数组、函数
var arr = []; function foo() { return 0; } console.log(Object.prototype.toString.call(arr) == "[object Array]");//true console.log(Object.prototype.toString.call(foo) == "[object Function]");//true
-
作用域安全的构造函数
当调用构造函数创建实例却未使用new运算符时,会误创造全局变量,为了解决这个问题,在构造函数内判断this是否正确对象的实例。这种写法会导致仅借用构造函数的继承原型链被破坏,加上原型链继承可解决该问题。
function Person(name) { if (this instanceof Person) { this.name = name; }else { return new Person(name); } }
-
函数柯里化
函数柯里化用于创建已经设置好了一个或多个参数的函数。
function curry(fn) { var args = Array.prototype.slice.call(arguments, 1); //返回从第二个参数开始的所有参数 return function () { var innerArgs = Array.prototype.slice.call(arguments); //返回内部函数参数 var finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); } } function add(num1, num2) { return num1 + num2; } var curadd = curry(add, 2); console.log(curadd(3)); //5
bind
函数柯里化:function bind(fn, context) { var args = Array.prototype.slice.call(arguments, 2); return function () { var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return fn.apply(context, finalArgs); } }
-
防篡改对象
- 不可扩展对象:
Object.preventExtensions(obj)
,调用此方法后,不能给obj添加新属性和方法,但可修改和删除已有成员。(防篡改登记:低) - 密封对象:
Object.seal(obj)
,密封对象不可扩展。调用此方法后,不能给obj添加新属性和方法,不能删除属性和方法。(防篡改等级:中) - 冻结对象:
Object.freeze(obj)
,冻结对象既不可扩展又是密封的。调用此方法后,不能给obj添加新属性和方法,不能修改删除属性和方法。(防篡改登记:高)
- 不可扩展对象:
-
使用定时器小任务分割处理大数组
function chunk(array, process, context) { setTimeout(function () { var item = array.shift(); process.call(context, item); if (array.length > 0) { setTimeout(arguments.callee, 100); } }, 100) }
-
自定义事件
function EventTarget() { this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function (type, handler) { if (typeof this.handlers[type] == "undefined") { this.handlers[type] = []; } this.handlers[type].push(handler); }, fire: function (event) { if (!event.target) { event.target = this; } if (this.handlers[event.type] instanceof Array) { var handler = this.handlers[event.type]; for (let i = 0; i < handler.length; i++) { handler[i](event); } } }, removeHandler: function (type, handler) { if (this.handlers[type] instanceof Array) { var handlers = this.handlers[type]; for (var i = 0; i < handlers.length; i++) { if (handlers[i] === handler) { break; } } handlers.splice(i, 1); } } };