🤳👀👀👀
JS数据类型
分为两类:一类是基本数据类型,也叫简单数据类型,包含7种类型,分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。另一类是引用数据类型也叫复杂数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。
\
数据分成两大类的本质区别:基本数据类型和引用数据类型它们在内存中的存储方式不同。
基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
\
加分回答
Symbol是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据,可以作为object的key。
数据的创建方法Symbol(),因为它的构造函数不够完整,所以不能使用new Symbol()创建数据。由于Symbol()创建数据具有唯一性,所以 Symbol() !== Symbol(), 同时使用Symbol数据作为key不能使用for获取到这个key,需要使用Object.getOwnPropertySymbols(obj)获得这个obj对象中key类型是Symbol的key值。
\
http和https的区别
HTTP协议:超文本传输协议,英文是Hyper Text Transfer Protocol,它是从服务器传输超文本标记语言(HTML)到本地浏览器的传送协议,基于TCP/IP通信协议来传递数据,端口号是80。
优点:
1.简单快速,客户端向服务器请求服务时,只用传递请求方法(get、post)和路径,通信速度快。
2.无连接,服务器处理完客户端的请求后就断开连接。
3.无状态,服务器断开后无任何记录,可以减轻服务器的负担。
缺点:
1.它是明文传输,各种账号、密码等信息通过http协议传输隐私信息非常不安全。
2.加入购物车,支付,登录。每次都需要验证身份信息,但是无状态所以无法连续。解决办法,就是cookie技术。
HTTPS协议:HTTP+SSL,通过SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密,端口号是443。
\
输入www.baidu.com并按下回车,中间发生了什么?
1、 浏览器先尝试从Host文件中获取该请求对应的服务器IP地址,如果没有找到,就使用DNS域名解析服务器来解析IP地址。(一个域名可能对应多个ip地址)
2、 根据IP地址,建立TCP连接3次握手。(第三次发送一个确认报文,防止出现失效的连接请求被服务端接收的情况,从而产生错误)
3、 发送HTTP请求。
4、收到服务器的响应,浏览器解析HTML文本,构建DOM树,解析css,构建样式树,加载js,两者合成render(渲染)树,最后渲染在页面上。
\
浏览器渲染HTML的过程?
1、html被html解析器解析成DOM元素,css被css解析器解析为CSS DOM树。
2、DOM树和CSSOM树合成形成Render树。
3、渲染树上的每个节点信息重排,这个过程叫做Reflow。
4、最后页面渲染重绘(Repaint)呈现出来。
减少重排:用事件捕获减少DOM操作、js addClass而不是操作style、动画元素设置成absolute或者fixed,这样不会影响到其他元素、opcity代替display:none,transform代替top。
\
(document).ready:意思就是DOM树加载完毕,就执行,不必等到页面中图片或其他外部文件都加载完毕。并且可以写多个.ready。
window.onload:是页面所有元素都加载完毕,包括图片、视频、js等所有元素。只能执行一次。
\
谈谈你对闭包的理解
\
使用闭包主要是为了封装私有的方法和变量,闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,使用不当容易造成内存泄漏。
即:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
闭包有三个特性:
1、函数嵌套函数;
2、函数内部可以引用外部的参数和变量;
3、参数和变量不会被垃圾回收机制回收;
清除:把变量或者函数设为null。
\
清除浮动的几种方式?
\
1、浮动的元素后面加一个空div,设为clear:both;
2、父元素overflow:hidden或zoom:1;
3、父元素设置高度
4、父元素:添加伪类法
\
.div:after(伪类){
height: 0;
clear: both;
content: '.';
visibility: hidden;
zoom: 1; //兼容IE
}
\
gulp到底用来干什么呢?
\
- 编译 sass
- 合并优化压缩 css
- 校验压缩 js
- 优化图片
- 添加文件指纹(md5)
- 组件化头部底部(include html)
- 实时自动刷新…
- ......
- 压缩静态资源
- 变更静态资源
- 给静态资源添加 md5
- 修改预处理样式后自动编译(SASS,Less)
- 合并雪碧图
- 自动刷新浏览器
- ......
- Sass编译
- Css Js 图片压缩
- Css Js 合并
- Css Js 内联
- Html的include功能
- Autoprefixer
- 自动刷新
- 去缓存
- Handlebars模板文件的预编译
- 雪碧图
- ESLint
- rem移动端适配方案
\
总之,gulp是前端开发过程中对代码进行自动化构建的利器。它不仅能对资源进行优化,而且在开发过程中能够通过配置自动完成很多重复的任务,让我们可以专注于代码,提高工作效率。
\
webpack和gulp的区别
1、 webpack是前端资源模块的管理和打包工具,gulp是规范前端开发流程的工具。
2、webpack文档资源文件的处理是通过入口文件产生的依赖形成的,gulp是配置不同的task,对该task配置路径下的所有资源都可以管理。
\
npm install、npm install --save与npm install --save-dev区别?
npm install moduleName //安装模块到文件node_modules目录下
npm install --save moduleName //将模块安装到文件node_modules目录下,并在package文件的dependencies节点写入依赖。
npm install --save-dev moduleName //将模块安装到文件node_modules目录下,并在package文件的devDependencies节点写入依赖。
\
如何判断一个变量是不是数组?
1、使用Array.isArray() 判断,如果返回true,说明是数组;
\
Array.isArray([1,2,3]); //true
Array.isArray(arguments); //false arguments是类数组,不是数组。
\
2、使用instanceof判断,如果返回true,说明是数组;
\
[1,2,3] instanceof Array; //true
\
3、使用Object.prototype.toString.call() 判断,如果值是[object Array],说明是数组,如果值是[object Object],说明是对象;还可以用来准确判断number、string、boolean、function等
\
Object.prototype.toString.call([1,2,3]); //[object Array]
\
4、通过constructor来判断,如果是数组,那么arr.constructor===Array(不准确,因为可以更改值);
\
例:
\
var arr = [1,2,3];
arr.constructor===Array; //true
arr.constructor = Object;
arr.constructor===Array; //false
例:
function fn(){
console.log(arguments.constructor===Array); //false
arguments.constructor=Array;
console.log(arguments.constructor===Array); //true
}
fn(1,2,3);
\
谈谈你对JS执行上下文和作用域链的理解
\
执行上下文就是当前js代码被解析和执行时所在环境,函数每调用一次,都会产生一个新的执行环境,因为不同的调用可能就有不同的参数。
\
function fn(x) {
console.log(arguments)
console.log(x)
}
fn(20);
fn(10);
\
作用域是一个‘地盘’,作用域是静态观念的,而执行上下文环境是动态的,有闭包存在时,一个作用域存在两个上下文环境也是有的。
\
let x = 100; // 全局作用域
function fn(x) { // fn作用域
return function bar() { // bar作用域
console.log(x)
}
}
let f1 = fn(5);
let f2 = fn(10);
f1() // 5
f2() // 10
\
同一个作用域下,对同一个函数的不同调用会产生不同的执行上下文环境,继而产生不同的变量的值,所以,作用域变量的值是在执行过程中确定的,而作用域是在函数创建时就确定的。
\
let a = 100
function fn() {
let b = 20
return function bar() {
console.log(a + b);
}
}
let x = fn();
b = 200
x() //120
\
bar的这个作用域中没有a和b的值,它就会向上找到fn的作用域取得b的值,再向上全局作用域找到a的值,找到了就结束了,这就是作用域链。
\
js的常见错误类型
1、 SyntaxError (语法错误)
eg:var 1a;
2、Reference (引用错误)
eg:console.log(a); let a=1;
3、TypeError(类型错误)
eg:var a=new 123;
4、RangeError(范围错误)
eg:var a=new Array(-1); //超出有效范围
\
null 和 undefined 的区别,如何让一个属性变为null
\
操作的变量没有被赋值、全局对象的一个属性、函数没有return返回值、值 null 特指对象的值未设置 undefined == null、undefined !== null
\
undefind 是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是undefined。undefined通过typeof判断类型是'undefined'。undefined == undefined undefined === undefined 。 null代表对象的值未设置,相当于一个对象没有设置指针地址就是null。null通过typeof判断类型是'object'。null === null null == null null == undefined null !== undefined undefined 表示一个变量初始状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。在实际使用过程中,不需要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。 让一个变量为null,直接给该变量赋值为null即可。 加分回答 null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
\
谈谈你对promise的理解
\
promise是处理异步操作的一种解决方案。
promise对象有三个状态:pending、fulfilled、rejected,异步操作的结果是由pending变成fulfilled成功或变成rejected失败。
promise构造函数接受一个函数作为参数,以及该函数的两个参数分别为resolve和reject两个函数。
\
const promise = new Promise(function(resolve, reject){
if(success){
resolve(value);
}else{
reject(error);
}
})
//分别对应promise对象的then()和catch()方法。
promise.then(function(value){
console.log(success);
}.catch(error){
console.log(error);
})
\
Promise的作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码的可读性更高,更容易维护 Promise使用:Promise是ES6提供的一个构造函数,可以使用Promise构造函数new一个实例,Promise构造函数接收一个函数作为参数,这个函数有两个参数,分别是两个函数 resolve和reject,resolve将Promise的状态由等待变为成功,将异步操作的结果作为参数传递过去;reject则将状态由等待转变为失败,在异步操作失败时调用,将异步操作报出的错误作为参数传递过去。实例创建完成后,可以使用then方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用。 Promise的特点: 1. 对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态)。 - pending(执行中) - Resolved(成功,又称Fulfilled) - rejected(拒绝) 其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束)。 2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise对象的状态改变,只有两种可能(状态凝固了,就不会再变了,会一直保持这个结果): - 从Pending变为Resolved - 从Pending变为Rejected 3. resolve 方法的参数是then中回调函数的参数,reject 方法中的参数是catch中的参数 4. then 方法和 catch方法 只要不报错,返回的都是一个fullfilled状态的promise 加分回答 Promise的其他方法: Promise.resolve() :返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。 Promise.reject():返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法。 Promise.all():返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。 Promise.any():接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。 Promise.race():当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
\
Promise的then的第二个参数和catch的区别
\
第一:reject是用来抛出异常的,catch是用来处理异常的;
第二:reject是Promise的方法,而then和catch是Promise实例的方法(Promise.prototype.then 和 Promise.prototype.catch)。
\
如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到
\
then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。
\
Promise.all() 方法用于将多个Promise实例,包装成一个新的Promise实例。
\
const p=Promise.all([p1,p2,p3]);
\
注:a、只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
b、只要p1、p2、p3之中有一个变成rejected,p的状态就会变成rejected,此时第一个变成reject的实例的返回值,会传递给p的回调函数
\
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
\
const p = Promise.race([p1, p2, p3]);
\
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
\
promise解决了什么问题?
promise解决了多层嵌套回调的问题。
上一个函数请求的输出作为下一个请求的输入,如果网络延迟,下一个请求执行时,很可能上一个请求还没返回值,这样就会导致请求失败。
而用promise.then() 方法链式调用可以解决这个问题,当上一个then方法执行完成且成功才会调用下一个then。
缺点:
1.异步加载,延时问题
2.promise一旦创建,就会立即执行,无法取消
(可以手动取消)
//只打印ok1
\
Promise.resolve().then(() => {
console.log('ok1')
return new Promise(()=>{}) // 这样即可,返回“pending”状态的Promise对象
}).then(() => {
// 后续的函数不会被调用
console.log('ok2')
}).catch(err => {
console.log('err->', err)
})
\
3.then方法每次调用都会创建一个新的promise对象,造成了内存的浪费
\
作用域
1.全局作用域
\
在JavaScript中{ }外面的作用域,就称之为全局作用域。既然是全局作用域,那么其中的声明的变量在全局中都可以使用。
\
2.局部作用域
\
对于javaScript中函数后面的{ } (注意:是函数后面紧跟着的{ })中的作用域,就称之为局部作用局。而这个局部作用域内声明的变量,就只能在它这个作用域内才能使用了。
\
3.块级作用域
\
在ES6之前,只要{ }没有和函数结合在一起,那么{ }内的作用域就称为块级作用域。
\
函数作用域:变量在定义的函数内及嵌套的子函数内处处可见;
\
块级函数域:变量在离开定义的块级代码后马上被回收。
\
var、let和const的区别?
1、var声明的变量属于函数作用域,let和const声明的变量属于块级作用域;
2、var存在变量提升现象(undefined),而let和const不存在,会报错(referenceError)。
3、var变量可以重复声明,let和const不能重复声明。
4、let声明变量可以改,const声明常量不可改,不能被再次赋值,声明变量可以改,只要保证变量的内存地址不改动,比如向数组push数据、改变对象的属性值,这些都是没问题的。
\
//let存在暂时性死区
let a = 'outside';
if(true) {
console.log(a);//Uncaught ReferenceError: a is not defined
let a = "inside";
}
\
typeof、instanceof、constructor的区别?
1、typeof 判断运算数的数据类型,返回字符串。
\
typeof a='number'/'string'/'function'/'object'/'undefined'/'boolean';
typeof null='object';
typeof []='object';
typeof a='undefined';
\
2、instanceof 用来判断一个变量是否是某个对象的实例,只能用来判断对象、数组和构造函数。
\
[] instanceof Array //true
[] instanceof Object //true
\
3、constructor是每一个对象都拥有的属性,而这个属性也相当于是一个指针,它指向于创建当前对象的对象。
\
[1,2,3].constructorArray; //true
[1,2,3].constructorObject; //false
\
call、apply和bind的区别
他们都是函数的方法
fn.call(obj,1,2); //立即执行
fn.apply(obj,[1,2]); //最多有两个参数,立即执行
var f=fn.bind(obj,1,2);
f(); //等调用后再执行
\
js获取url的参数值
\
function getRequest(){
var url=location.search;
var theRequest=new Object();
if(url.indexOf('?')!=-1){
//**indexOf()** 查找数组中是否含有某个元素,返回找到的第一个的下标,如果找不到返回-1
var a=url.substr(1);
var b=a.split('&');
for(var i=0; i<b.length; i++){
theRequest[b[i].split('=')[0]]=b[i].split('=')[1]
}
}
console.log(theRequest);
}
\
EventListenter第三个参数是什么以及它的作用?
第三个参数是boolean值,true表示事件捕获,false表示事件冒泡。
\
element.addEventListener(event, function, useCapture);
\
第一个参数是事件的类型 (如 "click" 或 "mousedown").
\
第二个参数是事件触发后调用的函数。
\
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
\
事件冒泡 :当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window 。(注意这里传递的仅仅是事件 并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。)
\
纯css画三角形
\
width: 0;
height: 0;
border-width: 100px;
border-style: solid;
border-color: red transparent transparent transparent;
\
浅拷贝和深拷贝的区别?
\
基本数据类型只是简单的赋值数据,不存在深拷贝和浅拷贝的问题。
浅拷贝:对于引用数据类型,拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
比如改变对象的属性值,或数组push,都会影响另一个对象。
b复制了a的引用地址,而不是复制了a的值,所以是a和b共同指向了a堆内存的值。
\
实现方案:let obj2 = Object.assign({}, obj1);
注意Object.assign一级为深拷贝,二级为浅拷贝
\
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1; //一级
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.b.c = 5; //二级
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 5}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 5}}
\
深拷贝:对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
b不仅复制得到了a的内容,而且b在堆内存中有专门的内存为其存放内容。
\
instanceof 操作符来判断对象的具体类型
\
实现方案:
\
function deepClone(obj = {}, map = new Map()) {
if (typeof obj !== "object") {
return obj;
}
if (map.get(obj)) {
return map.get(obj);
}
let result = {};
// 初始化返回结果
if (
obj instanceof Array ||
// 加 || 的原因是为了防止 Array 的 prototype 被重写,Array.isArray 也是如此
Object.prototype.toString(obj) === "[object Array]"
) {
result = [];
}
// 防止循环引用
map.set(obj, result);
for (const key in obj) {
// 保证 key 不是原型属性
if (obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key], map);
}
}
// 返回结果
return result;
}
\
let arr2 = JSON.parse(JSON.stringify(arr1));
JSON.stringify无法处理function、RegExp(正则)、new Date()
deepCopy(parent, clone) {
let child = clone || {};
for (let i in parent) {
if (!parent.hasOwnProperty(i)) {
continue;
}
if (typeof parent[i] === 'object') {
child[i] = (parent[i].constructor === Array) ? [] : {};
this.deepCopy(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
return child;
}
var obj = deepCopy({a:[1,2,3],b:{x:10}},{}) ; console.log(obj);
\
实现浅拷贝
\
1、ES6:object.assign()
\
var a = { name : “hello” };
\
var b = Object.assign( { },a );
\
b.name = “hi”;
\
console.log(a);
\
2、展开运算符……
\
var a = { name : “hello” };
\
var b = { …a}; => 扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值
\
b.name = “hi”;
\
console.log(a);
\
3、自己封装函数实现for in
\
var a = { name : “hello” };
\
var b = copy(a);
\
b.name = ‘hi’;
\
console.log(a);
\
function copy(obj){
\
var result = { };
\
for(var attr in obj ){
\
result [attr] = obj[attr];
\
}
\
return result;
\
}
\
实现深拷贝
\
JS实现深拷贝则可以应用JSON.parse()JSON.Stringify()的方法
\
对象自变量和JSON的区别
\
1对象自变量是对象的操作 可以又方法和属性JSON数据的格式。规定数据的操作
\
2JSON只能写基本类型的数但是还有JSON
\
3JSON要求属性(key)和值都必须添加双引号
\
深拷贝实现
\
1、var a = {name = {age:20}};
\
var b = JSON.parse(JSON.Stringify(a));
\
b.name.age =30;
\
console.log(a);
\
必须针对对象JSON这样的深拷贝是有局限性的
\
2、已封装函数与实现for in +递归
\
var a = {name : {age : 20}};
\
var b =deepcopy(a);
\
b.name.age = 30 ;
\
console.log(a);
\
function deepcopy (obj){
\
var result = { };
\
for(var attr in obj){}
\
if(type of obj[attr] === ‘object’){
\
result [attr] = deepcopy(obj[attr]);
\
}
\
else{
\
result[attr] = obj[attr];
\
}
\
}
\
Return result;
\
}
\
重绘和重排的区别?
所有对元素视觉表现属性的修改,都会导致重绘,比如修改了背景颜色、文字颜色、透明度等。
所有会触发元素布局发生变化的修改,都会导致重排,比如窗口尺寸发生变化,添加、删除DOM元素,修改了元素盒子大小如width、height、padding等。
\
get和post的区别?
\
GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。
get后退无刷新,post后退会重新发请求;
get请求能被浏览器自动缓存网页,post要手动缓存;
get有长度限制,不超过2048个字节,post长度无限制;
get参数通过url传递,post放在request body 中;
get向服务器索取数据,post向服务器提交数据。
\
什么是事件委托?
事件委托是利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件。
优点:
减少事件注册,节省内存;
动态添加事件,即不用在新添加或删除的li上绑定或解绑click事件。
\
移动端click事件延迟300ms的原因以及解决办法?
当用户点击屏幕后,浏览器不能立即判断用户是单击还是双击缩放,因此会延迟300ms。
解决方案:
1、添加viewpoint meta标签(user-scalable=no禁止用户缩放解决安卓的300ms延迟问题)
2、fastClick (github.com/ftlabs/fast…)
移动端事件触发顺序:touchstart–>touchmove–>touchend–>click。
fastClick.js的原理是:在检测到touchend事件的时候,会通过DOM自定义事件立即触发模拟一个click事件,并把浏览器在300ms之后真正的click事件阻止掉。
引入fastclick.js文件
window.addEventListener(function(){
FastClick.attach( document.body );
},false );
\
说出几个常见的状态码?
100:请求开始连接;
200:请求连接成功;
301:表示永久重定向,搜索引擎会把旧地址替换成新地址,输入www.baidu.com会跳转至www.baidu.com;旧地址会永久移除,搜索引擎在抓取新内容的同时将旧地址替换为新地址\
302:表示临时重定向,例如未登录的用户会跳转至登录页面,搜索引擎会在抓取新内容的同时会保留旧地址;
301和302允许更改请求方法(get和post),对应的308(永久)和307(临时)不允许更改请求方法。
304:再次刷新网页返回304,表示客户端存在缓存;
400:表示服务器不理解请求的语法;(请求错误)
401:用户身份未验证,需要先登录;
404:客户端请求失败,找不到该请求的链接;(未找到)
500:服务器端错误,比如tomcat正在启动;
\
移动端开发遇到过哪些坑?
1、IOS滚动不平滑的问题 -webkit-overflow-scrolling:touch;
2、fastclick可以解决在手机上点击事件的300ms延迟;
3、统一分辨率,禁止用户缩放;
4、ios去掉移动端点击时的高亮效果
\
{
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
\
5、 IOS中input键盘事件keyup、keydown、keypress支持不是很好
解决:可以用html5的oninput事件去代替keyup
6、input输入框聚焦时底部的按钮被弹起
解决:input聚焦时底部隐藏
7、开发App时,ios7以上顶部header和手机状态栏重合
\
window.uexOnload = function(type){
var ios7style=uexWidgetOne.iOS7Style;
var isFullScreen = uexWidgetOne.isFullScreen;
if (ios7style == '1' && isFullScreen != '1') {
$("body").addClass("uh_ios7");
}
}
.uh_ios7 .uh,.uh_ios7{
padding: 20px 0 0;
}
\
8、输入框元素无法自动获得焦点focus()并唤起软键盘
\
webview.KeyboardDisplayRequiresUserAction = "NO";
\
9、ios日期转换NAN的问题 ‘yyyy-MM-dd’.replace(/-/g, ‘/’)
10、ios和安卓样式不一样,ios下input输入框有阴影:-webkit-appearance: none
\
vue生命周期?
\
beforeCreate:el(访问器属性)和data还未初始化,都为undefined;
created:完成了data数据的初始化,el没有;
beforeMount:完成了el和data的初始化,但是el里面的{{message}}还没显示出来,虚拟Dom,先把位置占住;
mounted:el的{{message}}显示,完成Dom元素的挂载,这个阶段可以写入ajax请求;(new vue()首屏加载完成)
beforeUpdate和updated:修改meaasge两者先后调用;
beforeDestroy和destroyed:实例销毁后,重新改变组件里的message,vue不再对此动作进行响应了。
开始创建—初始化数据—编译模板—挂载Dom—更新渲染—销毁,通俗点说Vue实例是从创建到销毁的过程。
\
angular生命周期?
ngOnChanges:当数据绑定输入属性(@input)的值发生变化时调用;
ngOninit:在第一次ngOnChanges后调用,只调用一次;
ngDoCheck:用于检测和处理值的改变,可能会被多次调用;
ngAfterContentInit:被投影组件内容初始化之后调用;
ngAfterContentChecked:被投影组件内容的变更检测之后调用;
ngAfterViewInit:被投影组件视图及其子视图初始化之后调用;
ngAfterViewChecked:被投影组件视图及其子视图的变更检测之后调用;
ngOnDestroy:在Angular销毁指令/组件之前调用。
\
EventLoop事件循环机制?
EventLoop指的是计算机系统的一种运行机制,js就是采用这种机制。
js事件分为同步任务和异步任务,同步任务顺序执行,异步任务分两种,宏任务(Macrotasks)和微任务(Microtasks)。
宏任务:script(整体代码)、setTimeout、setInterval、setImmediate;
微任务:Process.nextTick、Promise.then、Promise.catch(注意不是new Promise);
script中的代码也是一个宏任务,所以先执行宏任务script。
执行顺序:宏任务script–>同步任务–>微任务–>宏任务。
(注意:当函数执行的时候,一旦遇到await就会先执行,等到其它操作完成,再接着执行函数体内await后面的语句。)
\
async function async1() {
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 end');
}
async1();
console.log('async3 end');
async2 end
async3 end
async1 end
\
谈谈如何跨域?(高频问)
\
同源策略:域名、端口、协议相同,其中有一个不同就会产生跨域。
浏览器为什么不跨域:防止XSS、XSRF攻击,保护用户和网站的资源。
1、通过使用jsonp解决跨域
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的网站那获取资料,即跨域读取数据。
服务端不再返回 JSON 格式的数据,而是返回一段调用某个函数的 js 代码,这样实现了跨域。
你以为客户期望返回数据:[“name1”,“name2”]。
实际上客户端的数据显示为: test([“name1”,“name2”])。
\
<script>
function test(json) {
console.log('我被调用了');
console.log(json);
}
</script>
<script src="http://api.douban.com/v2/movie/top250?callback=test"></script>
\
如果要在jQuery中使用Ajax,则只需要在dataType属性中把json改为jsonp即可。注意:跨域请求是只能是get请求不能使用post请求
\
$.ajax({
type: 'get',
url: 'api/register.php',
data:data,
dataType: 'jsonp',
jsonp: "callback", //参数名
jsonpCallback:"success", //自定义函数名
success:function(data){
console.log(data);
}
})
\
2、后台配置CORS解决跨域
服务器端文件加上header("Access-Control-Allow-Origin", " ");
3、通过iframe来解决跨域
基于iframe实现的跨域要求两个域具有 aa.xx.com,bb.xx.com这种主域相同的域**。
a.study.cn/a.html 请求 b.study.cn/b.html
在a.html:
\
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
function test() {
alert(document.getElementById('a').contentWindow);
}
</script>
</head>
<body>
<iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>
\
在b.html:
\
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
</script>
</head>
<body>
我是b.study.cn的body
</body>
</html>
\
这样在a页面就能获取b页面的内容了
\
网络安全问题?(经常问)
\
XSS:跨站脚本攻击(Cross-site scripting)是一种网站应用程序的安全漏洞攻击,它允许恶意用户将代码注入到网页上,这类攻击通常包含了html以及用户端脚本语言。
XSS攻击分类
存储型:攻击者将恶意代码提交到目标网站的数据库中,服务器将数据库中的恶意代码取出并返回给浏览器执行,恶意代码窃取用户数据后就可以冒充用户的行为攻击网站。
反射型:攻击者构造出包含恶意代码的url,用户打开该url,服务端将恶意代码取出并在浏览器执行,恶意代码窃取用户数据后就可以冒充用户的行为攻击网站。
DOM型:攻击者构造出包含恶意代码的url,用户打开该url,浏览器直接执行恶意代码,恶意代码窃取用户数据后就可以冒充用户的行为攻击网站。
区别:
存储型是将恶意代码存储到数据库中,反射型是将恶意代码存在url,两者都是服务端的安全漏洞,而DOM型的恶意代码是有浏览器直接执行,属于前端安全漏洞。
\
例如一直弹窗:
\
while (true) {
alert('你关不掉我')
}
\
网站左右两侧的广告栏:
\
img = document.createElement('img')
img.onclick = function() {
window.open('不好的网站')
}
\
防御:
1、设置cookie的属性为HttpOnly,那么js脚本将无法读取cookie信息;
2、对用户输入进行编码,转义过滤掉特殊字符;
3、防止恶意嵌套iframe(src可以跨域)。
\
if(window.top !== window.self){ window.top.location = window.location;}
\
判断一个页面是否存在两个窗口,只有一个窗口window.top == window.self为true。
\
XSRF:跨站请求伪造(Cross Site Request Forgery),诱导用户发起请求,完成一些违背用户意愿的请求(如恶意发帖、删帖、发邮件等)。
例如在论坛中发一贴,包含一链接:
\
www.csdnblog.com/bbs/delete_…
只要有用户点击了这个链接,那么ID为X的这一篇文章就被删掉了,而且是用户完全不知情的情况。
防御:
1、referer验证
referer是http请求header中的一部分,例如在www.google.com里有一个www.baudu.com的链接,那么点击www,baidu,com,它的header信息里就有:
\
Referer:www.google.com
\
如果伪造请求的referer不是www.google.com,就拦截该请求。\
2、token验证
如果攻击者伪造了转账的表单,那么网站可以在表单中加入一个随机的token来验证,银行服务器通过验证表单的值来判断post请求是否合法,不合法则拦截。
3、加验证码
\
什么是BFC?
\
BFC即块级格式化上下文,具有BFC特性的元素可以看做隔离了的独立容器,容器里面的子元素不会在布局上影响到外面的元素。
只要元素满足下面任一条件即可触发BFC特性:
a.body根元素
b.浮动元素:float除none以外的值
c.绝对定位元素(absolute、fixed)
d.display为inline-block(融合行内于块级)、flex 如果设置父级为display:flex,内部的浮动就会失效
e.overflow(hidden、auto、scroll)
\
/////////////////////////////
\
如何设置子元素高度是父元素宽度的一半?
\
.parent{width: 210px; height: 100px}
.child{width: 100%; padding-bottom: 50%} //width: 200px; height: 105px;
\
content-box和border-box的区别?
\
盒模型包括content、padding、border和margin
w3c盒模型content-box:宽度的呈现=width+padding+border
IE盒模型border-box:宽度的呈现=width
\
标准盒模型box-sizing:content-box; 如下图
\
\
()IE盒模型 box-sizing:border-box;如下图
\
\
可以通过 box-sizing 来改变元素的盒模型:
\
box-sizing: content-box:标准盒模型(默认值)。
box-sizing: border-box:IE(替代)盒模型。
\
数组去重?
\
第一种方法:利用对象属性key排除重复项:遍历数组,每次判断对象中是否存在该属性,不存在就存储在新数组中,并且把数组元素作为key,设置一个值,存储在对象中,最后返回新数组。这个方法的优点是效率较高,缺点是占用了较多空间,使用的额外空间有一个查询对象和一个新的数组
\
第二种方法:利用Set类型数据无重复项:new 一个 Set,参数为需要去重的数组,Set 会自动删除重复的元素,再将 Set 转为数组返回。这个方法的优点是效率更高,代码简单,思路清晰,缺点是可能会有兼容性问题
\
第三种方法:filter+indexof 去重:这个方法和第一种方法类似,利用 Array 自带的 filter 方法,返回 arr.indexOf(num) 等于 index 的num。原理就是 indexOf 会返回最先找到的数字的索引,假设数组是 [1, 1],在对第二个1使用 indexOf 方法时,返回的是第一个1的索引0。这个方法的优点是可以在去重的时候插入对元素的操作,可拓展性强。
\
第四种方法:这个方法比较巧妙,从头遍历数组,如果元素在前面出现过,则将当前元素挪到最后面,继续遍历,直到遍历完所有元素,之后将那些被挪到后面的元素抛弃。这个方法因为是直接操作数组,占用内存较少。
\
第五种方法:reduce +includes去重:这个方法就是利用reduce遍历和传入一个空数组作为去重后的新数组,然后内部判断新数组中是否存在当前遍历的元素,不存在就插入到新数组中。这种方法时间消耗多,内存空间也有额外占用。
\
方法还有很多,常用的、了解的这些就可以 加分回答 以上五个方法中,在数据低于10000条的时候没有明显的差别,高于10000条,第一种和第二种的时间消耗最少,后面三种时间消耗依次增加,由于第一种内存空间消耗比较多,且现在很多项目不再考虑低版本浏览器的兼容性问题,所以建议使用第二种去重方法,简洁方便。
\
var arr = [1,1,1,2,3,3,4]
1、function unique1(){
var newArr = [];
for(let i in arr){
if(newArr.indexOf(arr[i]) == -1){
newArr.push(arr[i]);
}
}
console.log(newArr);
}
unique1();
2、function unique2(){
var newArr = [];
var newObj = {};
for(let i in arr){
if(newObj[arr[i]] == undefined){
newArr.push(arr[i]);
newObj[arr[i]] = 1;
}
}
console.log(newArr); //[1,2,3,4]
console.log(newObj); //{1:1,2:1,3:1,4:1}
}
unique2();
3、function unique3(){
return Array.from(new Set(arr));
//return [...new Set(arr)];
//new Set()对象存储的值是唯一的,Array.from将一个有length属性的对象或类数组转换成真正的数组
}
unique3();
\
找出一串字符串中重复的字符和长度?
\
1、var str = 'adddfxssx';
function func(){
var obj = {};
for(let i = 0; i<str.length; i++){
if(!obj[str[i]]){
obj[str[i]] = 1;
}else{
obj[str[i]]++;
}
}
return obj;
}
2、var str = 'adddfxssx';
function func(){
var arr = [];
var obj = {};
for(let i = 0; i<str.length; i++){
if(arr.indexOf(str[i]) == -1){
arr.push(str[i]);
obj[str[i]] = 1;
}else{
obj[str[i]]++;
}
}
return obj;
}
func(); //{a: 1, d: 3, f: 1, x: 2, s: 2}
\
冒泡排序?
\
function bubbleSort(arr) {
for(let i = 0;i<arr.length-1;i++) { //外层下标0,1,2,3
for(let j = i+1;j<arr.length;j++) { //内层下标1,2,3,4
if(arr[i]>arr[j]) {
let tem = arr[i];
arr[i] = arr[j];
arr[j] = tem;
}
}
}
return arr;
}
bubbleSort([2,1,4,5,3]);
\
字符串反转?
\
function reverse(str){
for(let i = 0; i<str.length; i++){
return str.split('').reverse().join('');
}
}
\
new的四个步骤?
var obj = new Func();
1.创建一个空对象
\
var obj = {};
\
2.链接到原型(将构造函数的prototype赋值给新对象的proto)
\
obj.proto = Func.prototype;
\
3.绑定this,并执行构造函数的代码(this指向obj,不用new指向window)
\
Func.apply(obj,arguments);
\
4.返回新对象
\
return obj
\
js延迟(异步)加载的5种方式?
1.defer属性:告诉浏览器立即下载该js文件,但该脚本会延迟到整个页面解析完毕之后再执行
2.async属性:下载完毕立即执行该脚本,异步加载其他内容。
两者都不能控制加载文件先后的顺序。
即:如果依赖其他脚本和 DOM 结果,使用 defer
如果与 DOM 和其他脚本依赖不强时,使用 async
3.动态创建DOM(放在body标签上面,接近页面底部)
4.setTimeout延迟
5.把js放底部最后加载
\
异步任务
\
回调函数、事件监听、setTimeout、Promise、生成器Generators/yield、async/awt
\
所有异步任务都是在同步任务执行结束之后,从任务队列中依次取出执行。 回调函数是异步操作最基本的方法,比如AJAX回调,回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。此外它不能使用 try catch 捕获错误,不能直接 return Promise包装了一个异步调用并生成一个Promise实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve 和 reject方法,then接收到对应的数据,做出相应的处理。Promise不仅能够捕获错误,而且也很好地解决了回调地狱的问题,缺点是无法取消 Promise,错误需要通过回调函数捕获。 Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数是一个状态机,封装了多个内部状态,可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。优点是异步语义清晰,缺点是手动迭代Generator 函数很麻烦,实现逻辑有点绕 async/awt是基于Promise实现的,async/awt使得异步代码看起来像同步代码,所以优点是,使用方法清晰明了,缺点是awt 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 awt 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式。 加分回答 JS 异步编程进化史:callback -> promise -> generator/yield -> async/awt。 async/awt函数对 Generator 函数的改进,体现在以下三点: - 内置执行器。 Generator 函数的执行必须靠执行器,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。 - 更广的适用性。 yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 awt 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 - 更好的语义。 async 和 awt,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,awt 表示紧跟在后面的表达式需要等待结果。 目前使用很广泛的就是promise和async/awt
\
vue和angular的区别?
jquery和三大框架我理解的区别一个是居于DOM的操作,一个居于数据的操作。
1.vue和angular相比体积更小,同样项目占的内存大概是angular的一半。
2.vue相比angular更加灵活,可以直接像引用jquery那样在html中引用vue。
3.angular模板功能强大,指令丰富,是一个比较完善的框架,包括服务、模板、双向数据绑定、路由等,复杂度高一点。而vue要配合vuex(状态管理)和vue-router这两个单独的插件来使用。
4、两者双向数据绑定的原理不一样
\
rem的本质?
\
rem布局的本质是等比缩放,作用于非根元素时,它是相对于根元素。作用于根元素时,相对于浏览器原始字体大小。
em是字体大小是相对于父元素。
\
px是固定像素
\
javascript垃圾回收机制?
执行环境负责管理代码执行过程中使用的内存,垃圾收集器会定时找出那些不再继续使用的变量,回收释放内存。
1、标记清除法(标记为0的清除掉,标记为1的不清除,缺点是释放的内存空间不连续,当分配空间需要遍历找到大于size的块才能为其分配)
2、引用计数法(标记为0的清除掉,数字递增的不清除,缺点是计数器无上限,计数器也很占空间)
例如:
清理定时器clearTimeout;
把变量设置成null;
合理使用全局变量;
\
HTML语义化?
1.代码结构,为了在没有css的情况下,页面也能很好的呈现内容结构和代码结构。
2.语义化,h表示标题,p表示段落,title和alt解释名称和图片信息(title是鼠标滑过出现的备注信息,而alt是图片未加载出现的备注信息)
3.有利于SEO:和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息
\
png、jpg、svg的区别?
1.png、jpg是位图,由屏幕上的像素构成,svg是矢量图,由一系列计算机指令来描述和记录一幅画。
2.png可以无损压缩,jpg压缩多次图片质量变差,svg体积更小,任意放大也不会损失图片质量。
base64文本文件,适合小图标,通过对Base64编码将结果写入到HTML或CSS,从而减少HTTP文件。
\
pc端和移动端区别?
1、pc端考虑的是浏览器兼容性,而移动端开发考虑的是手机兼容性。
2、移动端有触屏事件,而缺少hover事件,另外移动端有弹出手机键盘的处理,而pc端没有。
3、在动画支持上,移动端使用css3的兼容性更好。
4、手机上有300ms的延迟,pc端没有
5、pc端更新不需要重新下载,APP要重新下载。
\
h5开发和混合APP开发的区别?
h5:
1、支持设备广,可跨平台,一套代码可以在ios、android、pc端上通用。
2、不能访问设备的一些硬件条件(摄像头、麦克风等)。
APP:
1、一套代码可以在ios、android上通用。
2、如果要上线的话要发到appStore或应用市场审批。
3、获得新版本的话要重新下载更新。
\
return和return true、return false的区别?
return是终止程序不再继续往下执行。
return true是返回正常的处理结果,后面的语句不再执行。
return false是终止事件的默认行为,比如事件冒泡、阻止表单提交,a跳转。
return+表达式 结束函数执行,表达式的值作为函数的结果。
\
强制转换和隐式转换?
String(),Number(),parseInt()函数是强制转换
不同类型的变量比较要先转类型,叫做类型转换,类型转换也叫隐式转换。隐式转换通常发生在运算符加减乘除,等于,还有小于,大于等。
\
判断一个对象是不是空对象?
1、Object.keys(obj).length === 0;
2、JSON.stringify({}) == '{}'
\
link 标签 和 import 标签的区别?
link 属于 html 标签,而@import 是 css 提供的
页面被加载时,link 会同时被加载,而@import 会阻塞页面的渲染
link 是 html 标签,因此没有兼容性,而@import 只有 IE5 以上才能识别。
link 方式样式的权重高于@import 的。
\
块元素和行元素?
\
块元素:独占一行,并且有自动填满父元素,可以设置 margin 和 padding 以及高度和宽度
行内元素:img、input
\
==、===的区别?
===:称为等同符,值和类型都相同才返回true;
==:称为等值符,当等号两边的类型相同时,直接比较值是否相等,若不相同,则先转化为类型相同的值,再进行比较;
\
setTimeout中var和let的区别?
\
就是每个i都是指向同一个,i=5,然后打印5个5
\
for(var i=0;i<5;i++) { setTimeout(function(){
console.log(i); //0,1,2,3,4
},1000);
}
每次循环都会生成不同的i,i为0,1,2,3,4
\
JSON和JSONP?
json返回的是一串数据对象;而jsonp返回的是脚本代码
\
Ajax是什么?
Ajax = 异步JavaScript和XML
发出HTTP请求时,实现了页面在不刷新浏览器的情况下与服务器进行交互。例如鼠标滑过菜单出现详细列表。点击加载更多数据等。
\
null和undefined和NaN?
null表示一个无的空对象,undefined表示一个无的原始值
NaN(not a number),表示非数字。
typeof null //Object
typeof undefined //undefined
typeof NaN // 返回number,神奇
\
(this)返回一个Jquery对象,可以调用Jquery方法,比如hide()、show()方法。
this代表当前DOM元素。
\
JavaScript的栈和堆?
1、堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放。
2、基本数据类型是按name、value直接存放在栈中的,而引用数据类型的value存放的是数据的指针,当访问引用数据类型的值时,先从栈中获得对象的指针,通过指针找到堆中所需要的数据,引用数据类型如下图。
\
同步任务和异步任务?
因为Javascript的单线程,所有任务都要排队,前一个任务执行完,才能继续执行下一个任务,当遇到ajax请求获取大量数据时,用户只能干等着,严重影响用户体验。因此就有了同步任务和异步任务。
同步任务:主线程上排队执行任务,只有前一个任务完成,才能继续执行下一个任务。
异步任务:是指不进去主线程,先挂起进入到任务队列,js的宿主环境(浏览器、node)是多线程的,使得js拥有了异步的属性。
比如一个ajax请求,它不会像我们想象的直接拖到同步任务之后,当主线程遇到异步请求时,浏览器会开一条新的http线程来执行请求,当这个异步请求执行完毕时,任务队列就会把完成的事件插入到主线程的尾部等待输出。
\
…args(剩余参数)和arguments的区别?
\
剩余参数表示那些没有对应形参的实参,arguments包含了传给函数的所有实参,有length属性。
可以通过 [...arguments] 和Array.prototype.slice.call(arguments) 转化为真正的数组。
\
TypeScript和JavaScript的区别?
TypeScript是JavaScript 的一个超集(js里有的东西ts都有),支持 ECMAScript6标准。与js最大的区别就是ts添加了可选的静态类型(简单理解为使用变量先声明数据类型)和基于类(类、接口、继承等)的面向对象编程思想。
还有IDE增强功能,提供代码补全、错误提示、跳转定义、重构等等。
\
TCP和UDP区别?
tcp(Transmission Control Protocol),传输控制协议,传输数据前需要先建立连接,是一种可靠的、基于字节流的传输层通信协议;
udp(User Datagram Protocol),用户数据报协议,传输数据的时候不需要建立连接,是一种传输报文的简单不可靠的信息传送协议。
\
高内聚低耦合?
高内聚:设计的时候将相关的功能内聚到一块,比如下单模块包括产品信息,用户信息、订单信息。
低耦合:描述了模块与模块之间的依赖,当更改其中一个模块,另一个模块的影响越小,耦合性就越低。
\
v-if和v-show的区别?
\
v-if是dom元素的销毁和重建
\
v-show表示存在这个dom元素,它是通过元素的display来实现显示和隐藏。
\
脏检查是什么意思?
脏检查的全名是 脏数据检查。是AngularJS命名的。
脏数据也就是产生了变化的数据。
脏检查为什么被称为脏。它是定时检查而不是直接监听属性变化。我们把这个检查称为摘要周期(digest)。
angularJS监测对象变化不是像vue.js那样通过Object.defineproperty这种接口,而是在某些情况下制定策略,通过复制保存一份数据,进行快照对比,来监测变化。
\
什么是双向数据绑定?
\
(v-model)双向数据绑定是UI视图(view)和数据(model)相互绑定在一起,当数据发生改变时,UI视图也发生改变,反之UI视图改变数据同步发生改变。
双向数据绑定最开始出现在表单中,在表单输入数据的时候就改变对象的属性值,反之对应属性值改变也会反映到表单中。
目前流行的 MVVM 框架(Angular、Vue)都实现了双向数据绑定,这样也就实现了视图层和数据层的分离。
\
vue如何实现双向数据绑定的?
\
1、实现一个数据监听器Observer,具体利用Object.defineProperty() 来监听属性变动,如有变动可拿到最新值并通知订阅者Watcher;
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,以及绑定相应的更新函数添加到监听的订阅者;
3、实现一个订阅者Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行update()方法,从而更新视图。
\
\
angular如何实现双向数据绑定的?
angular1:scope.$digest循环触发脏检查。
angular2:数据监听通过zoneJs实现,zoneJs就像是一个代理,它监听了angular所有的异步事件(用户输入、点击提交等;ajax请求;setTimeout,setInterval)并重写了所有的异步API(猴子补丁),一个异步事件发生时,脏检查会从根组件开始,自上而下对树上的所有子组件进行检查。zoneJs会通知angular有数据发生变化,需要检测更新。
\
no-cache 和 no-store 的区别
no-cache:表示可以在客户端存储资源,每次发请求给服务端来获取资源(200)还是使用客户端缓存(304)。
no-store:不在客户端存取资源,每次发请求给服务器重新拉取资源。
\
两边宽固定,中间自适应(flex布局)?
父元素display: flex; 左右flex: 0 0 200px; 中间flex: 1 1 auto;
\
async函数的优点?
\
到 Promise 对象,再到 Generator 函数,把异步写法的复杂性变成了形式上同步的写法,但是这种改进还是不彻底,于是就有了async\await。async 函数,就是 Generator 函数的语法糖。
async是把Generator函数的*号换成async,yield换成await。
1、内置执行器
Generator函数的执行必须靠执行器(.next()),而async函数自带执行器,和普通函数一样,直接调用。
2、语义化更好
async和await,比起星号和await,语义更清楚,async是声明函数是异步的,而await是用于等待一个方法执行完成。await会阻塞后面的代码(先执行第一个await,再执行async函数外的同步函数代码,然后再执行第二个await),如果等到的是 Promise 对象,则得到其 resolve 值。否则,会得到一个表达式的运算结果。
3、更广的实用性
yield 命令后面只能是 Thunk 函数或 Promise 对象,async 函数的 await 命令后面,可以跟 Promise 对象和普通表达式。
缺点:
await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。
\
箭头函数和普通函数的区别?
\
1、箭头函数省略了function和return。
2、箭头函数没有arguments对象和prototype对象,不能用new实例化。
3、箭头函数中this的指向和普通函数不一样,全局作用域下声明的箭头函数this指向window,函数中包裹箭头函数,它的this和最外层非箭头函数的this指向一致。并且apply、call不能改变this的指向。
\
nginx反向代理与负载均衡的实现?
用户访问网站的时候首先会访问nginx服务器,然后nginx服务器再从公司的服务器集群中选择压力较小的服务器,将该访问引向该服务器,但是用户并不知道是哪一台服务器提供的数据,这就是反向代理。
\
vue中为什么data是一个函数?
\
组件中的data写成一个函数,数据以函数返回值的形式定义,这样每复用一次组件,就会返回一份新的data,类似给每个组件实例创建一个私有的数据空间,如果写成对象模式的话,所有组件实例就公用了一份data,改变一个data其它的也会改变。
\
什么是虚拟DOM?
\
如果要频繁的操作DOM,那就借助虚拟DOM尽可能的一次性将差异更新到真实DOM中,这样保证了DOM不会出现性能很差的问题。Virtual DOM的diff就是比较新旧虚拟DOM Tree找出差异并更新。
diff的过程:
1、用JS对象模拟DOM(虚拟DOM ,vue生命周期beforeMount)
2、把此虚拟DOM转成真实DOM并插入页面中(createElement方法,vue生命周期mounted)
3、如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff算法)
4、把差异对象应用到真正的DOM树上(patch方法)
虚拟DOM的缺点:
1、首次渲染真实DOM时,还要多渲染一层虚拟DOM,会比较慢。
2、如果修改非常少量的虚拟DOM,实际上有可能比操作真实DOM更慢。
\
vue模板template编译成render函数的原理?
1、使用正则对template字符串进行解析转化为抽象语法树AST
2、遍历AST,找到静态节点并跳过,优化runtime的性能
3、AST转化为render函数
\
vue响应式原理?
\
数据发生变化后,会重新对页面渲染,这就是Vue响应式。
原理:Object.defineProperty数据劫持+订阅器Dep和观察(订阅)者模式Watcher。
1、使用 Object.defineProperty劫持属性的变化
2、哪个组件用到了该属性,那么这个组件相对应的Watcher对象都会被注册到该属性的Dep(依赖收集)中
3、分别通知观察者(watcher)使用过的组件
4、watcher触发重新渲染生成虚拟 DOM
5、Vue框架遍历计算得到新旧虚拟 DOM差异
6、最后将差异局部修改到真实 DOM
\
单向数据流?
数据流和数据绑定是两个概念,单向数据流,即父组件可以向子组件传递props,但是子组件不能修改父组件传递来的props,子组件只能通过事件通知父组件进行数据更改。
\
http1.0和http2.0的区别?
blog.csdn.net/qq_37012533…
\
原型和继承
blog.csdn.net/qq_37012533…
\
浏览器的兼容性问题?
blog.csdn.net/qq_37012533…
\
简单总结position定位和元素水平垂直居中(经常问)
blog.csdn.net/qq_37012533…
\
MVC、MVVM的区别和联系?
blog.csdn.net/qq_37012533…
\
谈谈网站性能优化?(重点问)
blog.csdn.net/qq_37012533…
\
使得对象或属性不能被修改
\
1、使用Object.preventExtensions()可以使一个对象不能够增加新的属性,但是可以对原有属性进行修改和删除操作。
\
2、Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
\
使用这个方法冻结的对象,不可以增加新的属性,可以修改原有的属性,这些和Object.preventExtensions()一样,但是既然说它比Object.preventExtensions()更加纯粹,肯定有不一样的地方——Object.seal()还可以禁止删除原有属性。
\
3、Object.freeze()方法使得被冻结的对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。
\
4、手动实现一个冻结函数
function deepFreeze(obj) {
if (typeof obj != 'object') {
throw Error('Type Error!')
}
Object.freeze(obj);
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
if (typeof obj[key] === 'object') {
deepFreeze(obj[key])
}
}
}
}
\
立即执行表达式会丢失this指向 obj.fn.bind(5)()
\
例:
\
Function a(x){
\
this.x = x
\
return this
\
}
\
Var x = a(5)
\
var y = a(6)
\
console.log(x.x)//undefind
\
//Window.x = 5;
\
//Window.x = window;
\
console.log(y.x)//6
\
//Window.x = 6;
\
//Window.y = window
\
正则函数中test和match的区别
\
用于检测一个字符串是否匹配某个模式
\
1.match()方法
\
match是String的方法,参数是正则表达式,返回值是数组,没有找到时返回的是null。
\
stringObj.match(rgExp) 其中stringObj是必选项,对其进行查找的string对象或字符串文字。rgExp是必选项,为包含正则表达式模式和可用标志的正则表达式对象。
\
使用方法如下:
\
\
2.test()方法
\
test是RegExp的方法,参数是字符串,返回值是boolean类型
\
\
返回值:如果字符串 string 中含有与 RegExpObject 匹配的文本,则返回 true,否则返回 false。
\
JS数组去重
\
第一种:遍历数组法
\
这种方法最简单最直观,也最容易理解,代码如下:
\
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 if (newArr.indexOf(arr[i]) === -1) {
5 newArr.push(arr[i])
6 }
7 }
8 console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]
\
这种方法很好理解,利用了indexOf() 方法(indexOf()方法如果查询到则返回查询到的第一个结果在数组中的索引,如果查询不到则返回**-1**)。先创建一个新的空数组用来存储新的去重的数组,然后遍历arr数组,在遍历过程中,分别判断newArr数组里面是不是有遍历到的arr中的元素,如果没有,直接添加进newArr中,如果已经有了(重复),那么不操作,那么从头到尾遍历一遍,正好达到了去重的目的。
\
第二种:数组下标判断法
\
这种方法也比较好理解,代码如下:
\
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 if (arr.indexOf(arr[i]) === i) {
5 newArr.push(arr[i])
6 }
7 }
8 console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]
\
这和第一种方法有重叠,不说多余的,直接看if这里,在遍历arr的过程中,如果在arr数组里面找当前的值,返回的索引等于当前的循环里面的i的话,那么证明这个值是第一次出现,所以推入到新数组里面,如果后面又遍历到了一个出现过的值,那也不会返回它的索引,indexof()方法只返回找到的第一个值的索引,所以重复的都会被pass掉,只出现一次的值都被存入新数组中,也达到了去重的目的。
\
第三种:排序后相邻去除法
\
这种方法用到了sort()方法,代码如下:
\
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
2 arr.sort()
3 var newArr = [arr[0]]
4 for (var i = 1; i < arr.length; i++) {
5 if (arr[i] !== newArr[newArr.length - 1]) {
6 newArr.push(arr[i])
7 }
8 }
9 console.log(newArr) // 结果:[0, 2, 5, 6, 7, 8]
\
这种方法的思路是:先用sort()方法把arr排序,那么排完序后,相同的一定是挨在一起的,把它去掉就好了,首先给新数组初始化一个arr[0],因为我们要用它和arr数组进行比较,所以,for循环里面i也是从1开始了,我们让遍历到的arr中的值和新数组最后一位进行比较,如果相等,则pass掉,不相等的,push进来,因为数组重新排序了,重复的都挨在一起,那么这就保证了重复的这几个值只有第一个会被push进来,其余的都和新数组的被push进来的这个元素相等,会被pass掉,也达到了去重的效果。
\
第四种:优化的遍历数组法
\
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8]
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 for (var j = i + 1; j < arr.length; j++) {
5 if (arr[i] === arr[j]) {
6 i++
7 j = i
8 }
9 }
10 newArr.push(arr[i])
11 }
12 console.log(newArr) // 结果:[0, 5, 6, 7, 2, 8]
\
思路:两层for循环,外面一层是控制遍历到的前一个arr中的元素,里面一层控制的是第一层访问到的元素后面的元素,不断的从第0个开始,让第0个和他后面的元素比较,如果没有和这个元素相等的,则证明没有重复,推入到新数组中存储起来,如果有和这个元素相等的,则pass掉它,直接进入下一次循环。从第1个开始,继续和它后面的元素进行比较,同上进行,一直循环到最后就是:不重复的都被推入新数组里面了,而重复的前面的元素被pass掉了,只留下了最后面的一个元素,这个时候也就不重复了,则推入新数组,过滤掉了所有重复的元素,达到了去重的目的。
\
第五种:数组遍历法
\
1 var arr = ['a', 'a', 'b', 'c', 'b', 'd', 'e', 'a']
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 var bl = true
5 for (var j = 0; j < newArr.length; j++) {
6 if (arr[i] === newArr[j]) {
7 bl = false
8 break
9 }
10 }
11 if (bl) {
12 newArr.push(arr[i])
13 }
14 }
15 console.log(newArr) // 结果:["a", "b", "c", "d", "e"]
\
思路:也是两层for循环,外层for循环控制的是arr数组的遍历,内层for循环控制的是新数组的遍历,从第0位开始,如果新数组中没有这个arr数组中遍历到的这个元素,那么状态变量bl的值还是true,那么自然进入到了if中把这个值推入到新数组中,如果有这个元素,那么代表重复,则把状态变量bl取值改为false,并且跳出当前循环,不会进入到if内部,而进入下一次外层开始的循环。这样循环往复,最后也达到了去重的效果。
\
异步执行
\
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
\
继承
\
js里常用的如下两种继承方式:
\
原型链继承(对象间的继承)
类式继承(构造函数间的继承)
\
由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现
\
在面向对象的语言中,我们使用类来创建一个自定义对象。然而js中所有事物都是对象,那么用什么办法来创建自定义对象呢?这就需要用到js的原型:
\
我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,新实例化的对象内部有一个看不见的__Proto__指针,指向原型对象)。
\
js可以通过构造函数和原型的方式模拟实现类的功能。 另外,js类式继承的实现也是依靠原型链来实现的。
\
原型式继承与类式继承
\
类式继承是在子类型构造函数的内部调用超类型的构造函数。
严格的类式继承并不是很常见,一般都是组合着用:
\
\
1 function Super(){
2 this.colors=["red","blue"];
3 }
4
5 function Sub(){
6 Super.call(this);
7 }
\
\
原型式继承是借助已有的对象创建新的对象,将子类的原型指向父类,就相当于加入了父类这条原型链
\
原型链继承
\
为了让子类继承父类的属性(也包括方法),首先需要定义一个构造函数。然后,将父类的新实例赋值给构造函数的原型。
\
1.isNaN() 函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值” 。isNaN()在接受一个值后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串“10”或Boolean值。而任何不能被转换为数值的值都会导致这个函数返回true。
\
eval:返回字符串表达式中的值
\
unEscape:返回字符串ASCI码
\
escape:返回字符的编码
\
parseFloat:返回实数
\
| 正则 | 定义 |
|---|---|
| \d | 匹配一个数字,等价于[0-9] |
| \w | 匹配字母、数字或者下划线,等价于 [A-Za-z0-9_] |
| + | 匹配前面一个表达式 1 次或者多次 |
| * | 匹配前一个表达式 0 次或多次 |
| /g | 全局匹配 |
\
因为"+"和"*"都是贪婪匹配,它们会尽可能多地匹配字符,
\
\
console.log(``1``+ +``"2"``+``"2"``);
\
第一个+"2"中的加号是一元加操作符,+"2"会变成数值2,因此1+ +"2"相当于1+2=3.
然后和后面的字符串“2”相合并,变成了字符串"32".
\
console.log(``"A"``- ``"B"``+``"2"``);
\
"A"-"B"的运算中,需要先把"A"和"B"用Number函数转换为数值,其结果为NaN,在剪发操作中,如果有一个是NaN,则结果是NaN,因此"A"-"B"结果为NaN。
然后和"2"进行字符串合并,变成了NaN2.
\
4.js的全局属性:Infinity、NAN、undefined
\
js的全局函数:decodeURI()、decodeURIcomponent()、
\
encodeURI、encodeURIcomponent()、
\
escape()、eval()、isFinite()、isNAN()、
\
Number()、parseFloat()、parseInt()、String()、unescape()。
\
函数 描述
\
decodeURI() 解码某个编码的 URI。
\
decodeURIComponent() 解码一个编码的 URI 组件。
\
encodeURI() 把字符串编码为 URI。
\
encodeURIComponent() 把字符串编码为 URI 组件。
\
escape() 对字符串进行编码。
\
eval() 计算 JavaScript 字符串,并把它作为脚本代码来执行。
\
isFinite() 检查某个值是否为有穷大的数。
\
isNaN() 检查某个值是否是数字。
\
Number() 把对象的值转换为数字。
\
parseFloat() 解析一个字符串并返回一个浮点数。
\
parseInt() 解析一个字符串并返回一个整数。
\
String() 把对象的值转换为字符串。
\
unescape() 对由 escape() 编码的字符串进行解码。
\
5.instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例
\
6.call()方法和apply()方法的作用相同,他们的区别在于接收参数的方式不同。
\
对于call(),第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。(在使用call()方法时,传递给函数的参数必须逐个列举出来。使用apply()时,传递给函数的是参数数组)
\
如下代码做出解释:
\
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
\
7.// 1.调用对象未声明的属性会undifned
var user={};
console.log(user.name);//undifned
\
// 2.使用未赋值只声明的基本数据类型会undifned
var one;
console.log(one);//undifned
\
// 3.使用未声明的变量会报错
console.log(two);//new_file.html:15 Uncaught ReferenceError: two is not defined
\
\
promise
\
then 接收两个回调函数并返回一个新的 promise 对象,这两个回调函数分别对应成功回调 onFullfilled 和失败回调 onRejected,这两个回调函数接收 promise 的返回值;
always (finally) 接收一个回调函数并返回一个新的 promise 对象,回调函数在上一个 promise 解析完成之后调用,也就是不管前面是 then 还是 catch 被调用了,它都会被调用,该回调函数不会接收参数。
\
直接上代码:
//第一种
promise.then((res) =>{
console.log('then:', res);
}).catch((err) =>{
console.log('catch:', err);})
//第二种
promise.then((res) =>{
console.log('then:', res);
}, (err) => {
console.log('catch:', err);})
\
第一种 catch 方法可以捕获到 catch 之前整条 promise 链路上所有抛出的异常。
\
第二种 then 方法的第二个参数捕获的异常依赖于上一个 Promise 对象的执行结果。
\
promise.then(successCb, faildCd) 接收两个函数作为参数,来处理上一个promise 对象的结果。then f 方法返回的是 promise 对象。第一种链式写法,使用catch,相当于给前面一个then方法返回的promise 注册回调,可以捕获到前面then没有被处理的异常。第二种是回调函数写法,仅为为上一个promise 注册异常回调。
\
如果是promise内部报错 reject 抛出错误后,then 的第二个参数就能捕获得到,如果then的第二个参数不存在,则catch方法会捕获到。
\
如果是then的第一个参数函数 resolve 中抛出了异常,即成功回调函数出现异常后,then的第二个参数reject 捕获捕获不到,catch方法可以捕获到。
\
token
\
主要有两个作用:
\
①:防止表单重复提交(防止表单重复提交一般还是使用前后端都限制的方式,比如:在前端点击提交之后,将按钮置为灰色,不可再次点击,然后客户端和服务端的token各自独立存储,客户端存储在Cookie或者Form的隐藏域(放在Form隐藏域中的时候,需要每个表单)中,服务端存储在Session(单机系统中可以使用)或者其他缓存系统(分布式系统可以使用)中)
\
//在页面初始化的时候调用后端代码像前端返回token
public String initLogin(ModelMap model, HttpSession session, String loginUrl) {
model.put("extLoginView", clientManager.getExtLoginView());
// 生成token
String token = UUID.randomUUID().toString().substring(0,16);
model.put(LOGIN_TOKEN, token);
//返回地址与方法的 String loginUrl一致,即初始化的时候调用完方法后,又回到初始化页面
return loginUrl;
}
\
②:用来作身份验证
主要的理念是,客户端初始化的时候,一般就是刚刚进入页面的时候就调用后端代码,后端代码生成一个token,返回给客户端,客户端储存token(可以在前台使用Form表单中使用隐藏域来存储这个Token,也可以使用cookie),然后就将request(请求)中的token与(session)中的token进行比较:
\
//跳转到添加页面
@RequestMapping("/add.do")
public String add(HttpServletRequestrequest,HttpServletResponse response){
//生成token
UUID token=UUID.randomUUID();
System.out.println("token的值"+token);
//放入session中
request.getSession().setAttribute("token",token.toString());
//放入request作用域中传到前台
request.setAttribute("token",token);
return "add";
}
//前台穿过来的token进行比对
@RequestMapping("/addMessage.do")
public synchronized String addMessage(HttpServletRequest request){
//获取session中的token
Objecttoken1=request.getSession().getAttribute("token");
//获取前台穿过来的token
String token=request.getParameter("token");
System.out.println("token1的值"+token1);
if(token1==null){
System.out.println("提交出错");
}
else if(!token1.equals(token)){
System.out.println("提交出错");
}else{
System.out.println("提交成功");
//移除session 防止重复提交
request.getSession().removeAttribute("token");
}
return "";
}
\
3:使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
客户端使用用户名跟密码请求登录
服务端收到请求,去验证用户名与密码
验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
4:ajax中传递token的几种方式
方法一:放在请求头中
\
$.ajax({
type: “POST”,
headers: {
Accept: “application/json; charset=utf-8”,
userToken: “” + userToken
},
url: “/index”,
data: JSON.stringify(mytable.params),
contentType: “application/json”,
dataType: “json”,
success:function(data){
},
error:function(data){
}
});
\
方法二:使用beforeSend方法设置请求头
\
$.ajax({
type: “POST”,
url: “/index”,
data: JSON.stringify(mytable.params),
contentType: “application/json”,
dataType: “json”,
beforeSend: function(request) {
request.setRequestHeader(“Authorization”, token);
},
success: function(data) {
},
error: function(data) {
}
});
\
\
\
h5新增的标签 :
| video | 表示一段视频并提供播放的用户界面 |
| audio | 表示音频 |
| canvas | 表示位图区域 |
| source | 为video和audio提供数据源 |
| track | 为video和audio指定字母 |
| svg | 定义矢量图 |
| code | 代码段 |
| figure | 和文档有关的图例 |
| figcaption | 图例的说明 |
| main | |
| time | 日期和时间值 |
| mark | 高亮的引用文字 |
| datalist | 提供给其他控件的预定义选项 |
| keygen | 秘钥对生成器控件 |
| output | 计算值 |
| progress | 进度条 |
| menu | 菜单 |
| embed | 嵌入的外部资源 |
| menuitem | 用户可点击的菜单项 |
| menu | 菜单 |
| header | 定义了文档的头部区域 |
| section | 定义文档中的节(section、区段)。 |
| nav | 定义导航链接的部分 |
| aside | 定义页面独立的内容区域 |
| article | 定义页面的侧边栏内容。 |
| footer | 定义 section 或 document 的页脚 |
\
2.CSS规范为不同类型的选择器定义了特殊性权重,特殊性权重越高,样式会被优先应用。 权重设定如下: html选择器,权重为1; 类选择器,权重为10; id选择器,权重为100; 这里还有一种情况:在html标签中直接使用style属性,这里的style属性的权重为1000; 如果加上!important;则权重是最高的
\
3.ele.clientWidth = 宽度 + padding
ele.offsetWidth = 宽度 + padding + border
ele.scrollTop = 被卷去的上侧距离
ele.scrollHeight = 自身实际的高度(不包括边框
\
4.source 属性用于返回模式匹配所用的文本。
\
5.盒子水平居中的三种方式:
1)margin:0,auto;
2)margin-left:auto,
margin-right:auto;
\
3)margin:auto
\
6.this的值要等到代码真正执行时才能确定
同时this的值具体有以下几种情况:
new 调用时指的是被构造的对象
call、apply调用,指向我们指定的对象
对象调用,如执行obj.b(),this指向obj
默认的,指向全局变量window(相当于执行window.fun())
\
7.实际上 let 也是存在变量提升的
复制代码
let x = 10;let foo = () => { // 这里访问 x 就会在当前作用域内找 x // 如果没有下面的 let x = 20 // 当前作用域就找不到,然后就会继续找外层变量,最后输出 10 // 但是下面有声明,声明会提升到当前代码块最前面 // 这个时候 x 还没有初始化,存在暂存死区中,访问就会抛出 ReferenceError console.log(x); let x = 20; x++;};foo();
如果把里面的 let 换成 var 就不会报错,显示 undefined
就是因为 let 是在编译时才会初始化,var 在声明时就会初始化
\
虽然let语句不像var语句会产生hoisting(变量提升),JavaScript引擎也会意识到在后边的let定义,只是不支持在let声明语句之前引用该变量而已。所以,只要在同一个block中,let是在后边定义的,就不能在之前引用该变量。与此同时,也不能再去取嵌套外层的值了(x=1)
\
8.Display:none会触发reflow,
visibility:hidden会触发repaint
\
9.在没有指定基数的情况下,如果字符串以"0x"或者"0X"开头, 则基数是16 (16进制)。
\
相当于let i = parseInt('0xA',16)
\
10.不要在 label 元素内部放置可交互的元素,比如 anchors 或 buttons。这样做会让用户更难激活/触发与 label 相关联的表单输入元素,会引起交互内容的可访性问题
\
11.a标签不能嵌套a标签
\
12.call()需要传递明确几个参数,写全,
\
apply()可以接收一个数组作为参数,不管数组中有多少个元素
\
.13代码回收规则如下:
\
1.全局变量不会被回收。
\
2.局部变量会被回收,也就是函数一旦运行完以后,函数内部的东西都会被销毁。
\
3.只要被另外一个作用域所引用就不会被回收
\
14.Flash提供了ExternalInterface接口与JavaScript通信,ExternalInterface有两个方法,call和addCallback:
\
- ExternalInterface.addCallback("在js里可调用的flash方法名",flash内方法) //在flash中通过这个方法公开 在js中可调用的flash内的方法;
- ExternalInterface.call("js方法",传给js的参数) //在flash里调用js里的方法
\
ES5
\
orEach(), filter(), reduce(), every() 和some()都会跳过空位。
\
map()会跳过空位,但会保留这个值
\
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
\
ES6 中都会将空位当做undefined
\
ES5给Object扩展了一些静态方法, 常用的2个:
\
- Object.create(prototype, [descriptors])
\
- 作用: 以指定对象为原型创建新的对象
- 为新的对象指定新的属性, 并对属性进行描述
-
- value : 指定值
-
- writable : 标识当前属性值是否是可修改的, 默认为false
-
- configurable: 标识当前属性是否可以被删除 默认为false
-
- enumerable: 标识当前属性是否能用for in 枚举 默认为false
\
- Object.defineProperties(object, descriptors)
\
- 作用: 为指定对象定义扩展多个属性
-
- get :用来获取当前属性值得回调函数
-
- set :修改当前属性值得触发的回调函数,并且实参即为修改后的值
- 存取器属性:setter,getter一个用来存值,一个用来取值
\
ES6
\
\
- 在 ES6 有 7 种基本数据类型:undefined、null、Boolean、String、Number、Object、Symbol
- undefined(未定义):表示变量未初始化,属性不存在,函数没有返回值
- null(空值):表示变量为空值
- Boolean(布尔值):true 或 false
- String(字符串):单引号字符串 ‘’ 或 双引号字符串 ""
- Number(数值):范围 [-2^53, 2^53]
- Object(对象):表示属性的集合,其中每个属性由“名/值对”构成
- Symbol(符号):表示独一无二(ES6 新增)
\
存储
\
- 可以使用cookie在客户端保存诸如用户名等简单的用户信息,但是,通过长期的使用,你会发现,用cookie存储永久数据存在以下几个问题:
1.大小:cookie的大小被限制在4KB。
2.带宽:cookie是随HTTP事务一起被发送的,因此会浪费一部分发送cookie时使用的带宽。
3.复杂性:要正确的操纵cookie是很困难的。
针对这些问题,在HTML5中,重新提供了一种在客户端本地保存数据的功能,它就是Web Storage。
具体来说,Web Storage又分为两种:
1.sessionStorage:将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据。
2.localStorage:将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。
这两者的区别在于,sessionStorage为临时保存,而localStorage为永久保存。
到目前为止,Firefox3.6以上、Chrome6以上、Safari 5以上、Pera10.50以上、IE8以上版本的浏览器支持sessionStorage与localStorage的使用。
- head标签:
title:定义不同文档的标题
base:定义所有链接的URL
link:定义文档与外部资源之间的关系
style:定义HTML文档的样式文件引用地址,也可直接添加样式渲染
meta:描述一些基本的元数据
script:用于加载脚本文件
\
main 标签规定文档的主要内容; dir 定义目录列表; dd 为定义列表中项目的描述; pre 可定义预格式化的文本。 补充:定义列表 dl 标签定义了定义列表(definition list),其内又包含了 dt(定义列表中的项目)和dd(描述列表中的项目)
\
- 表单作用:为了收集用户信息,如登录、注册、搜索。
表单标记有文本框、密码框、单选框、多选框、下拉框、多行文本框、上传按钮、隐藏表单、提交按钮、重置按钮、图片提交按钮、普通按钮。
- 任何函数执行完一次,如果没有 return 返回值和声明变量接受返回值,都会立即消失,永远找不到值!
- 发送消息:
emit(name, data) 或者
broadcast(name, data);
接收消息: emit 广播给父controller broadcast 是从发送者向他的子scope广播一个事件。向下传播
on 有两个参数function(event,msg) 第一个参数是事件对象,第二个参数是接收到消息信息
- HTML的
< >&"©
分别是<,>,&,",©;
- HTML input元素种类
email
url
number
range
Date pickers(date, month, week, time, datetime, datetime-local)
search
color
- html5中可以省略结束标记的元素有
1、dd 2、dt 3、li 4、p 5、optgroup 6、option 7、rt 8、rp 9、thread 10、tfoot 11、tr 12、td 13、th
hasOwnProperty方法是用来检测给定的属性是否在当前对象的实例中。
- reflow(回流)是指浏览器为了重新渲染部分或者全部的文档,重新计算文档中的元素的位置和几何构造的过程。 因为回流可能导致整个Dom树的重新构造,所以是性能的一大杀手。 以下操作会引起回流: ① 改变窗口大小 ② font-size大小改变 ③ 增加或者移除样式表 ④ 内容变化(input中输入文字会导致) ⑤ 激活CSS伪类(:hover) ⑥ 操作class属性,新增或者减少 ⑦ js操作dom ⑧ offset相关属性计算 ⑨ 设置style的值 ...... 另外: reflow与repaint(重绘)是减缓js的几大主要原因,尤其是reflow更是性能杀手,所以我们应该想法避免。
- for in:先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来
\