2019前端面试题记录(杂文)

session和redis


目前session直接是js变量,放到nodejs的进程内存中,存在问题
问题一:进程内存有限,访问量过大,内存爆增怎么办?(有可能进程崩溃)
问题二:正式上线后运行的是多进程,进程之间内存无法共享(不同进程间的session不能共享数据)
1 redis是web server最常用的一个缓存库,数据存放在内存中 (内存读写特比快,但是比较昂贵)
2 相比mysql,访问速度快 (mysql放在磁盘中,而redis放在内存中,两者)
3 但是成本高,可储存的数据量更小 (内存的硬伤)

具体方案

1 将web server和redis拆分为两个单独的服务
2 双方都是独立的,都是可扩展的(例如都扩展成集群)
3 包括mysql,也是一个单独的服务可以扩展

vue同步

Vue.config.async = false

vue2的Object.defineProperty和vue3的Proxy

我们只监听了一个属性,一个对象不可能只有一个属性,我们需要对对象每个属性进行监听
Vue的操作就是加入了发布订阅模式,结合Object.defineProperty的劫持能力,实现了可用性很高的双向绑定。
是的,Object.defineProperty的第一个缺陷,无法监听数组变化。 
第二个缺陷,只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择。
 Proxy可以直接监听对象而非属性
Proxy可以直接监听数组的变化

基本类型和引用类型的区别

区别

一、 基本类型: string,number,boolean,null,undefined,symbol,BigInt

二、 引用类型: Function,Array,Object

判断

最常见的判断方法:typeof
返回的是数据类型(number,boolean,string,function,object,undefined)

判断已知对象类型的方法: instanceof
返回的是布尔值 某个对象的实例

根据对象的constructor判断: constructor

 

var

var不能用于定义常量
var可以重复声明变量
var是全局的
var存在变量提升

let 和 const

let和const不存在变量提升(暂时性死区) var存在(var在创建时就被初始化,并且赋值为undefined)
let 局部作用域
const如果是对象的话不能改变它的内存地址,即不能重新赋值另一个对象 const一旦声明变量,就必须立即初始化,不能留到以后赋值。如const a这样会报错
如果是基本类型则不能改变类型 如 const a = 123 ,不能赋值a ="123",会报错

 

基本类型的比较是值的比较:

任何方法都无法改变一个基本类型的值,比如一个字符串,引用类型则反之。
只有在它们的值相等的时候它们才相等。但你可能会这样:
var a = 1;
var b = true;
console.log(a == b);//true   ==会进行类型转换   ===不会进行类型转换
但是当两个值的类型相同的时候,即使是==也相当于是===。
两个变量可以参加任何操作而相互不受影响。也就是说基本类型在赋值操作后,两个变量是相互不受影响的。
因为基础类型的变量相互赋值相当于在内存中开辟新的地址
而引用类型的赋值其实是对象保存在栈区地址指针的赋值,因此两个变量指向同一个对象即指向相同的地址,任何的操作都会相互影响。

比较的区别
基础类型之间的对比直接比较值  即使值如果 == 结果就是 true

引用类型之间的对比直接比较内存地址  即使值如果 == 结果是 false 因为内存地址不一样 

图片说明

由上文就有了深拷贝和浅拷贝的概念
基础变量的赋值属于深拷贝 即不会相互影响 因为内存地址不一样

引用类型的赋值属于浅拷贝 会相互影响  因为内存地址一样

深拷贝的解决办法

let obj2=JSON.parse(JSON.stringify(obj))

prototype、__proto__、constructor

 我们需要牢记两点: ①__proto__和constructor属性是对象所独有的; ② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。 __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,然后返回undefined,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。 prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype。 constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。 另外 __proto__ 属性是浏览器对es5的实现,而不是es标准。

get 和 post 的区别

get,通过拼接url进行传递参数;
post,通过body体传输参数
get用来获取数据,post用来提交数据
get在浏览器回退时是无影响的,post会再次提交请求
get效率高   (比post高一点)
get安全性低 (比post低一点)

Session 登陆与 Token 登陆的区别 

1、Session 登陆是在服务器端生成用户相关 session 数据,发给客户端 session_id 存放到 cookie 中,这样在客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户认证。这种认证方式,可以更好的在服务端对会话进行控制,安全性比较高(session_id 随机),但是服务端需要存储 session 数据(如内存或数据库),这样无疑增加维护成本和减弱可扩展性(多台服务器)。 CSRF 攻击一般基于 cookie。另外,如果是原生 app 使用这种服务接口,因为没有浏览器 cookie 功能,所以接入会相对麻烦。 
2、基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用户验证后,服务端生成一个 token(hash 或 encrypt)发给客户端,客户端可以放到 cookie 或 localStorage 中,每次请求时在 Header 中带上 token,服务端收到 token,通过验证后即可确认用户身份。这种方式相对 cookie 的认证方式就简单一些,服务端不用存储认证数据,易维护扩展性强,token 存在 localStorage 可避免 CSRF,web 和 app 应用都比较简单。不过这种方式在加密或解密的时候会有一些性能开销(好像也不是很大),有些对称加密存在安全隐患(aes cbc 字节翻转攻击)。

vue-router


vue-router页面刷新保存页码信息 
方案一
vue router 更改地址url 不刷新页面 不存在history记录里面
this.$router.push()  改为 this.$router.replace() 
replace 的url不会被添加到history记录里面
当调用  this.$router.go(-1) 或者 this.$router.back() 
不会回退到replace添加的页面里,而是会回到replace之前进入的页面中
类似于添加一个标记一样
方案二
vue项目中路由跳转默认采用hash的方式,而hash的变化不会导致浏览器发送请求到服务器;
将获取数据的的函数的执行放在了Vue生命周期函数 mounted() 中,组件初次加载时执行了 mounted() 函数中的内容,但是再次点击时只有参数变化,组件已经挂载结束而且不会重新加载,mounted()  中的内容当然也就不会重新执行了。
所以你可以在点击页码的时候改变hash的变化不会重新渲染页面
然后去监听页面的$route的变化 在里面操作请求数据
watch: {
    '$route' (to, from) {
        this.getData(this.$route.query.id,this.$route.query.pageNo)
    }
}

如果提供了path,params会被忽略。
//$router : 是路由操作对象,只写对象 即跳转用 因为可写
//$route : 路由信息对象,只读对象 即读取信息用 因为可读


//query传参,使用name跳转
this.$router.push({
    name:'second',
    query: {
        queryId:'20180822',
        queryName: 'query'
    }
})

//query传参,使用path跳转
this.$router.push({
    path:'second',
    query: {
        queryId:'20180822',
        queryName: 'query'
    }
})

//query传参接收
this.queryName = this.$route.query.queryName;
this.queryId = this.$route.query.queryId;

//params传参 使用name
this.$router.push({
  name:'second',
  params: {
    id:'20180822',
     name: 'query'
  }
})

//params接收参数
this.id = this.$route.params.id ;
this.name = this.$route.params.name ;

//路由
{
path: '/second/:id/:name',
name: 'second',
component: () => import('@/view/second')
}

A、query类似于ajax中get传参,即在浏览器地址栏中显示参数。
B、params则类似于post,即在浏览器地址栏中不显示参数。
params传参,以隐藏参数,做好安全保密。 不过使用params传值的话,页面一刷新params 的值就消失了。
query相反,建议使用query,一般在地址栏的信息不会是什么敏感信息

Json Web Token

Json Web Token是怎么做的?
      1、客户端通过用户名和密码登录服务器;
      2、服务端对客户端身份进行验证;
      3、服务端对该用户生成Token,返回给客户端;
      4、客户端将Token保存到本地浏览器,一般保存到cookie中;
      5、客户端发起请求,需要携带该Token;
      6、服务端收到请求后,首先验证Token,之后返回数据。
      服务端不需要保存Token,只需要对Token中携带的信息进行验证即可;
      无论客户端访问后台的那台服务器,只要可以通过用户信息的验证即可。

前端优化

尽量不操作dom 用虚拟dom
懒加载
使用异步 同步会堵塞页面
减少cookie传输 http请求会携带cookie
减少 HTTP请求数
打包css文件 样式用外部样式,最好不要用行间样式,内嵌样式

为什么使用webpack

 模块化开发(import,require)
 打包(不打包就像html里面可能有上百个js即浏览器需要三次http 请求来获取这三个文件,打包后就会变成几 
 个一般是一个js文件来维护上百个文件的依赖关系)
 预处理(Less,Sass,ES6,TypeScript……)
 主流框架脚手架支持(Vue,React,Angular)
 庞大的社区(资源丰富,降低学习成本)
 webpack是当下最流行最前沿的
 热更新

export 、export default、module.exports、require

es6规范
1.export与export default均可用于导出常量、函数、文件、模块等
2.在一个文件或模块中,export、import可以有多个,export default仅有一个
3.通过export方式导出,在导入时要加{ },export default则不需要
4. 
(1) 输出单个值,使用export default
(2) 输出多个值,使用export
(3) export default与普通的export不要同时使用

var info = {
    name: 'zs',
    age: 20
}
export default info
 
export var title = '小星星'
 
export var content = '哈哈哈'
CommonJS规范
module.exports = {};
var req=require("./app.js");

exports.fn = function(){}
module.exports = function(){ } 

exports只是module.exports的引用

总结:只要不是直接导出对象,使用module.exports和exports没有区别,要是导出的对象只能使用module.exports
推荐使用module.exports

 

await、async、prosime、setTimeout执行顺序

说明
async 执行顺序和普通的上下文一样 到了就执行

prosime 里面的代码是同步的  执行顺序和普通的上下文一样 到了就执行,then里面的代码是异步的

await 先阻塞执行完await右边的代码然后执行  此时的await会让出线程,阻塞async内后续的代码,先去执行async外的代码。等外面的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise对象,是一个同步函数,也会等这样操作 即 async外的代码同步完之后的异步执行await 后面的代码

setTimeout 是等所有异步代码执行完 并且主进程是空闲的时候 再执行

iconfont 使用 

第一步:引入项目下面生成的fontclass代码:

<link rel="stylesheet" type="text/css" href="./iconfont.css">
第二步:挑选相应图标并获取类名,应用于页面:

<i class="iconfont icon-xxx"></i>

阶乘函数

递归

function num(n){

if(n<=0) return 1;return  n*arguments.callee(n-1) 

}
num(5)//120
如何快速写一个通用的死循环函数
function sb(){console.log(arguments.callee())}
sb();

尾递归

function factorial(n, total) {
 if (n === 1) return total;
 return factorial(n - 1, n * total); 
}
 factorial(5, 1) // 120

//避免函数赋值而报错
function fact(num){ 
    if (num<=1){ 
        return 1; 
    }else{ 
        return num*arguments.callee(num-1); //此处更改了。 
    } 
} 
var anotherFact = fact; 
fact = null; 
alert(anotherFact(4)); //结果为24. 

less简单记录

变量
@color: #999;
@mySelector: #wrap;
@borderStyle: border-style;
@Soild:solid;
@images: "../img";//需要加引号
@{mySelector}{ //变量名 必须使用大括号包裹
  color: @color;
  @{borderStyle}: @Soild;//变量名 必须使用大括号包裹
  background: url("@{images}/dog.png");//变量名 必须使用大括号包裹
}
变量作用域
一句话理解就是:就近原则


运算
加减法时 以第一个数据的单位为基准
乘除法时 注意单位一定要统一


媒体查询
#main{
    //something...
    @media screen{
        @media (max-width:768px){
          width:100px;
        }
    }
    @media tv {
      width:2000px;
    }
}


混合(加不加括号区别在于加括号在代码里面会隐藏)
#card(){
    background: #723232;
    .d(@w:300px){
        width: @w;   
    }
}
#wrap{
    #card > .d(); // 父元素不能加 括号
}



判断 and=>&   ,=>|  not=>!
 .border(@width,@color,@style) when (@width>100px) and(@color=#999){
        border:@style @color @width;
    }
 .background(@color) when not (@color>=#222){
        background:@color;
    }
 .font(@size:180px) when (@size>50px) , (@size<100px){
        font-size: @size;
    }

div{
    .border(99px,#999,solid);
    .background(#000);
    .font();
}
/*css*/
div {
  background: #000;
  font-size: 180px;
}


导入
import "main"; 


reference
Less 中 最强大的特性 使用 引入的 Less 文件,但不会 编译它。
@import (reference) "bootstrap.less"; 
#wrap:extend(.navbar all){}


避免编译
/* Less */
#main{
  width:~'calc(300px-30px)';
}
/* 生成后的 CSS */
#main{
  width:calc(300px-30px);
}


复制代码结构: ~"字符@{变量}字符";
变量拼串
在平时工作中,这种需求 太常见了。 transtion-delay、animation、@keyframes
@size:15px;
@i:1;
  .loopAnimation(@i) when (@i<16) { 
    .circle:nth-child(@{i}){
        border-radius:@size @size 0 0;
        transition-delay:~"@{i}ms";
    }
  }
  .loopAnimation(@i + 10);
/*css*/
.circle:nth-child(11) {
  border-radius: 15px 15px 0 0;
  transition-delay: 11ms;
}



因为 Less 是由 JS 编写,所以 Less 有一得天独厚的特性:代码中使用 Javascript 。
/* Less */
#wrap{
  width: ~"`Math.round(Math.random() * 100)`px";
//  height: ~"`window.innerHeight`px";
}
/* 生成后的 CSS */
#wrap{
  width: 随机值(0~100)px;
}
//很鸡肋 如果用less编译完的css那将不是动态的 如果用直接less就编译错误
//不过less还是css window.innerHeight都报错了 无语 资料太少了

extend 关键字的使用
/* Less */
.animation{
    transition: all .3s ease-out;
}
#main{
    &:extend(.animation);
}
/* 生成后的 CSS */
.animation,#main{
  transition: all .3s ease-out;
}



less版本适配

 //定义一个变量和一个mixin
 @baseFontSize: 100;//基于视觉稿横屏尺寸/100得出的基准font-size
 .px2rem(@name, @px){
     @{name}: @px / @baseFontSize * 1rem;
 }
 .container {
     .px2rem(height, 240);
      background-color: #888;
      .px2rem( font-size, 18);
 }



随机数十以内

Math.floor(Math.random()*10);

js 通过value找到key

function findKey (obj,value, compare = (a, b) => a === b) {
  return Object.keys(obj).find(k => compare(obj[k], value))
//  if(compare(obj[k], value)){return k} 箭头函数简写
}

var  people = {
       'name':'zhang',
        "age":19
}
findKey(people,'zhang'); //返回结果为:name
findKey(people,19); //返回结果为:age

闭包

当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
能够访问另一个函数作用域的变量的函数
function A(){
    function B(){
       console.log('Hello Closure!');
    }
    return B;
}
var C = A();
C();// Hello Closure!

排序

/*compare函数*/
  function compare(value1, value2){
    return value2 - value1;
  }
            
        var values = [0, 1, 10, 15, 5];
        values.sort(compare);  
        console.log(values);  //0, 1, 5, 10, 15

js 防抖和节流

函数节流: 频繁触发,但只在特定的时间内才执行一次代码  多次 比如搜索框
//每300 ms 执行一次  
函数防抖: 频繁触发,但只在特定的时间内没有触发执行条件才执行一次代码  一次  比如验证框
//300ms之内重复进去的话不执行 最后一次300ms后才执行

// 函数节流 300ms执行一次   
var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }
    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};
// 函数节流的要点是,声明一个变量当标志位,记录当前代码是否在执行。
// 如果空闲,则可以正常触发方法执行。
// 如果代码正在执行,则取消这次方法执行,直接return。


// 函数防抖 执行最后一次 300ms触发
var timer = false;
document.getElementById("debounce").onscroll = function(){
    clearTimeout(timer); // 清除未执行的代码,重置回初始化状态

    timer = setTimeout(function(){
        console.log("函数防抖");
    }, 300);
};
// 函数节流的要点,也是需要一个setTimeout来辅助实现。延迟执行需要跑的代码。
// 如果方法多次触发,则把上次记录的延迟执行代码用clearTimeout清掉,重新开始。
// 如果计时完毕,没有方法进来访问触发,则执行代码。

SSL、ws、wss、http、htts

SSL 是基础, 在 SSL 上运行 WebSocket 协议就是 WSS; 在 SSL 上运行 HTTP 协议就是 HTTPS.

ws和wss是在http和相应的https上的进一步改进升级版本.wss首先还是https协议, 只是增加了和websocket相应的头和二进封包等处理.简单地说就是WS on HTTP, WSS on HTTPS

另一个问题是在http页面里能不能打开wss连接.可以, 但不够安全.
在https能打开ws连接么? 答案是不能, 因为浏览器不允许.( 但也不是所有浏览器都不允许, 象国内的qq等浏览器就是允许的. 当然, 也就不安全了.)

HTTP协议传输的数据都是未加密的,也就是明文的;
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
采用HTTPS加密的网站在搜索结果中的排名将会更高
HTTPS协议握手阶段比较费时,对网站的相应速度有负面影响,如非必要,没有理由牺牲用户体验。
总结
https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

简单讲一下wss使用

const ws = new WebSocket('wss://api.test.com/wss');
let frist = 0 ;//缓存

//监听连接开启事件  开启后才可以发送给服务端
ws.onopen = function () {
  console.log('@open');

  ws.send('Hello Server!');
//发送给服务端 服务端发送给客户端 客户端通过onmessage接受
//一般应该不会是 ws.send发送数据 因为数据不仅仅是个文本 服务端会给接口代替

};
let that = this
//监听连接消息事件
ws.onmessage = function (e) {
  console.log('@message');

//首次接受消息的时候绑定处理
//判断是不是第一次登录 看实际需求
//that.bind()

//这里看你服务端给返回的数据格式 
  console.log(e)
  that.clientId = JSON.parse(e.data).data
//一般在接受消息后触发全局事件 
  that.$emit("appEmitMessage","e")
  console.error(JSON.parse(e.data).data)
};
//监听连接关闭事件
ws.onclose = function (e) {
  console.log('@close');
};
//监听连接错误事件
ws.onerror = function () {
  console.log('@error');
  //   ws.close();
};
一般流程思路:
我这边是通过用户登录后获取token以及首次连接服务器即new WebSocket的时候会给客户端发送消息即onmessage事件中接收带有唯一标识的信息,在通过唯一标识和token发送请求给服务器绑定即可建立通信连接
一般通信必须放在全局的地方 因为如果为进去通信或者未触发WebSocket的话是没办法监听消息事件的
如果使用的框架是vue的话那就是在main.js中引入

原生js 

事件传播——冒泡与捕获和 事件——阻止冒泡捕获

默认情况下,事件使用冒泡事件流,不使用捕获事件流。
dom.addEventListener('click', function() {
            console.log('click');
        }, false);
//false为冒泡获取【目标元素先触发】   true为捕获获取【父级元素先触发】
防止冒泡和捕获
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
//比如点击里层div不会触发外层父级div的点击事件

取消默认事件

w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
//如果元素本身就没有默认行为,调用当然就无效了 只有如链接<a> 提交按钮<input type=”submit”>等。
return false
javascript的return false只会阻止默认行为,而是用jQuery的话则既阻止默认行为又防止对象冒泡。

js事件执行类型

js中的事件执行主要分为两个任务类型 macro task以及micro task 也就是宏仁务和微任务
宏仁务:script(全局任务),setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering
微任务:process.nextTick,promise,Object.observer,MutationObserver
执行顺序为 script先进入函数调用栈,然后执行遇到任何其他宏仁务,比如遇到了setTimeout,就把setTimeout放进宏仁务队列中,遇到了微任务就放入微任务队列中,等到函数调用栈的所有内容出栈后 然后执行微任务队列,然后再回头执行宏仁务队列再进入函数调用栈再执行微任务队列,知道宏仁务队列执行完毕
例子:
//遇到setTimeout,放入宏仁务队列
setTimeout(function () {
  console.log(1);
}, 0);
//遇到promise,放入微任务队列   resolve只会在then()里面输出
Promise.resolve(function () {
  console.log(2);
})
//这里虽然遇到了promise,但是是用new声明的,也就是立即执行,所以会先输出3
new Promise(function (resolve) {
  console.log(3);
});
//第二输出4
console.log(4);
//需要注意的是那个undefined并不是微任务输出的,而是console.log(4)输出的,具体可以控制台测试
//然后执行微任务,这个微任务并没有调用,所以也不会执行,然后执行宏仁务队列中的setTimeout,输出1
//控制台==》   3  4 undefined 1    
//实际是 3 4 1
//undefined只是console.log(4)没有返回值返回的undfined

事件委托

事件委托
利用冒泡原理,把时间加到父级上,触发执行效果
可以大量节省内存占用,减少事件注册
可以方便地动态添加和修改元素,不需要因为元素的改动而修改时间绑定
var ul = document.querySelector('ul'); 
var list = document.querySelectorAll('ul li'); 

ul.addEventListener('click', function(ev){ 
    var ev = ev || window.event; 
    var target = ev.target || ev.srcElemnt; 

    for(var i = 0, len = list.length; i < len; i++){ 
        if(list[i] == target){ 
            alert(i + "----" + target.innerHTML); 
        } 
    } 
});

图片懒加载

 当页面滚动的时间被触发->执行加载图片操作->判断图片是否在可视区域内->在,则动态将data-src的值赋予该图片

柯里化

假如一个函数只能接受一个参数,那么这个函数怎么实现;因为高阶函数是可以当做参数传递和返回值的;所以问题就简化了:写一个只有一个参数的函数;而这个函数返回一个带参数的函数;这样就实现了能写连个参数的函数;这就是函数柯里化;(其实就是函数的封装复用)

例如
function add(a,d){
return a+d;
};
function curry(a){
return function(d){
  return a+d;
 }
};
var add2=curry(2);
console.log(add2(3));//5
改成
const curry = (fn, ...arg) => {
    let all = arg;
    return (...rest) => {
        all.push(...rest);
        return fn.apply(null, all);
    }
}
let add2 = curry(add, 2)
console.log(add2(8));    //10
add2 = curry(add);
console.log(add2(2,8)); //10

this指向

var a = 123;//不能使用let 因为let 是块级作用域
var obj ={a:789}
function m(){
   console.log(this.a)
   return  this.a
}
m()
//VM9804:4 123
//123
m.call()
//VM9804:4 123
//123
m.call(this)
//VM9804:4 123
//123
m.call(obj)
//VM9804:4 789
//789
m()
//VM9804:4 123
//123
let aa =m.call(obj)
//VM9804:4 789
//undefined
aa
//789


 function add(a, b) {
        console.log(a + b);
    }

 add.call(this, 1, 2); //3
 add.apply(this, [1, 2]); //3


 var a = [2, 4, 5, 7, 8, 10];

console.log(Math.max.apply(null, a)); //10
console.log(Math.max.call(null,2, 4, 5, 7, 8, 10)); //10




说明
var add= {
        a: 1,
        count: function() {
            console.log(this.a++);
        }
    };

    add.count(); //1
如果赋值的话会报错 因为this指向了add 赋值的时候是指向了window
var add= {
        a: 1,
        count: function() {
            console.log(this.a++);
        }
    };

    var f = add.count;
    f(); //NaN

解决方案
 var f = add.count.bind(keith);
     f(); //1
     f(); //2
bind方法返回的仍然是一个函数,因此后面还需要()来进行调用才可以 而call,apply不用


例如
dog.use.call(cat)//我是猫
dog.use.apply(cat)//我是猫
dog.use.bind(cat)()//我是猫

性能优化

请说出三种减低页面加载时间的方法
压缩css、js文件
减少http请求
减少dom操作,尽可能用变量替代不必要的dom操作
api请求接口缓存

 

es6方面

let和const 块级作用域的区别:const的类型是不可变的 let可以

箭头函数作用域:箭头函数的this指向父级(本身箭头函数没有this作用域)
字符串新增
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
s.includes('Hello', 6) // false
'x'.repeat(3) // "xxx"
'x'.padStart(5, 'ab') // 'ababx'
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger(25) // true
Number.isInteger(25.1) // false
0.1 + 0.2 === 0.3 // false  因为浮点值
//2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。
2 ** 3 ** 2  // 相当于 2 ** (3 ** 2)   // 512

[...new Set([1, 2, 3, 4, 4])]
[...new Set('ababbc')].join(',').split(",")

es6  async/await 来处理异步
async它作为一个关键字放到函数前面,用于表示函数是一个异步函数,该函数的执行不会阻塞后面代码的执行。
async function timeout() {
    return 'hello world'
}
timeout().then(result => {
    console.log(result);
}).catch(err => {
    console.log(err)
})
console.log('虽然在后面,但是我先执行');
//控制台:  虽然在后面,但是我先执行
//         VM83:5 hello world
await它后面可以放任何表达式,更多的是放一个返回promise对象的表达式。await只能放到async 函数里面
例子:
function doubleAfter2seconds(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2 * num)
        }, 2000);
    } )
}
async function testResult() {
    let result = await doubleAfter2seconds(30);
    console.log(result);
    console.log('等待wait 函数执行完毕')
}
testResult()
//控制台:60
//         VM118:3 等待wait 函数执行完毕


将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组 
let arr =[1,2,5,4,[3,8,99,8]]
Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})
or
[...new Set(arr.toString().split(',').sort((a, b) => { return b - a }).map(Number))]
//控制台:[1, 2, 3, 4, 5, 8, 99] 
//b-a是降序  小于0则 升序 大于0 则降序

 

js 数组对象

数组
array.some(function(item,index,array){
               return item>1; 
            })

只要有一个满足的就返回true,没有满足的返回false

every:验证数组中是否每个元素都满足指定的条件
验证全部的结果,当全部的值都为 true 时,则最终会得到 true;只要其中之一为 false,则返回 false。

let people = [ { name: '马云', money: 2000 }, { name: '马化腾', money: 1800 }, { name: '我', money: Infinity } ];

var ans = people.every(function(item, index, array){
  return item.money > 1800;
});
console.log(ans); // false: 只要有部分不符合,则为 false

var ans2 = people.every(function(item, index, array){
  return item.money > 500;
});
console.log(ans2); // true: 大家钱都超过 500


forEach 没有返回值,可以不知道数组长度

arr.forEach(function(res,index){ })

const obj= { boss: 'xx', boss1: 'zz' };

 

map函数,返回值组成新数组,原数组不变;

var newarr=[1,2,3].map(function(res,index){

  return  res+1;

});

console.log(newarr)

//[2,3,4]

filter函数:过滤通过条件的元素组成一个新数组,原数组不变;

var newarr=[1,2,3].filter(function(res){

  return  res == 2

});

console.log(newarr)

//[2]


join(separator): 将数组的元素组起一个字符串,省略的话则用默认用逗号为分隔符(separator为分隔符)

var arr = [1,2,3];

console.log(arr.join()); // 1,2,3


shift:删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 

var a = [1,2,3,4,5];   

var b = a.shift(); //a:[2,3,4,5] b:1  
 

unshift:将参数添加到原数组开头,并返回数组的长度 

var a = [1,2,3,4,5];   

var b = a.unshift(-2,-1); //a:[-2,-1,1,2,3,4,5] b:7   


pop:删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefined 

var a = [1,2,3,4,5]; 

 var b = a.pop(); //a:[1,2,3,4] b:5  
 

push:将参数添加到原数组末尾,并返回数组的长度

var a = [1,2,3,4,5];   

var b = a.push(6,7); //a:[1,2,3,4,5,6,7] b:7 
 

concat:返回一个新数组,是将参数添加到原数组中构成的 

var a = [1,2,3,4,5];   

var b = a.concat(6,7);    //  a:[1,2,3,4,5] b:[1,2,3,4,5,6,7]  


splice(start,deleteCount,val1,val2,...):从start位置开始删除deleteCount项,并从该位置起插入val1,val2,... 

 

var a = [1,2,3,4,5];   

var b = a.splice(2,2,7,8,9); //a:[1,2,7,8,9,5] b:[3,4]   

var b = a.splice(0,1); //同shift   

a.splice(0,0,-2,-1); var b = a.length; //同unshift   

var b = a.splice(a.length-1,1); //同pop   

a.splice(a.length,0,6,7); var b = a.length; //同push 

 

reverse:将数组反序 

 

var a = [1,2,3,4,5];   

var b = a.reverse(); //a:[5,4,3,2,1] b:[5,4,3,2,1]  



sort(orderfunction):按指定的参数对数组进行排序 

var a = [1,2,3,4,5];   

var b = a.sort(); //a:[1,2,3,4,5] b:[1,2,3,4,5]  



slice(start,end):返回从原数组中指定开始下标到结束下标之间的项组成的新数组 

 var a = [1,2,3,4,5];   

     var b = a.slice(2,5); //a:[1,2,3,4,5] b:[3,4,5]  

 

 

 

对象
Object.keys() 会创建一个包含对象键的数组。

Object.keys() 迭代对象的键和值。

 
Object.keys(obj).forEach(key => { let value = obj[key]; console.log(`${key}: ${value}`); });

Output
boss: zz
boss1: xx
const length = Object.keys(obj).length; // 4
 

Object.values() 创建一个包含对象值的数组。

const values = Object.values(obj);
["xx","zz"]
 

Object.entries() 创建对象的 键/值 对的嵌套数组。

const entries = Object.entries(obj);

// [ ["boss", "xx"] ["boss1", "zz"] ]

 

 

Object.assign() 用于把一个对象的值复制到另一个对象。 or  展开语法(...)

//前面的值会覆盖后面的值

const character1= Object.assign(aa, bb);
const aa= { a: 'a', b: 'b' };

const bb= { b: 'bb', c: 'c' };

const character = {...aa, ...bb}

character1 == character2     //   {a:'a',b:'bb',c:'c'}

 

 

补充一个数组去重的方法
function dedupe(array) {
  return [...new Set(array)]
}
var arr = [1,2,2,3,3,4,4,5,5]

 

 js对象

prototype __proto__   constructor
所有的构造器包括Object和Function都继承了Function.prototype的方法,所有的构造器都是对象,即js中一切皆为对象。
prorotype的主要作用简单来说就是“便于方法或者属性的重用”,可以利用prototype添加扩展属性和方法
每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数
__proto__最终的指向都是Object.prototype,这也就是js中的原型链。
constructor属性指向的是创建当前对象的构造函数 即本身。
某个对象的constructor属性返回该对象构造函数,其__proto__属性是个对象,值和其构造函数的prototype属性值一致。

 

浅谈session,cookie,sessionStorage,localStorage的区别及应用场景 

区别:
保持状态:cookie保存在浏览器端,session保存在服务器端
Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
//axios允许携带cookie axios.defaults.withCredentials=true
使用方式:
(1)cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它
(2)session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。
3、存储内容:cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
4、存储的大小:cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。
5、安全性:cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。
原因如下:(1)sessionID存储在cookie中,若要攻破session首先要攻破cookie;
          (2)sessionID是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionID;

      (3)第二次启动session_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。
           (4)sessionID是加密的
           (5)综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。

6、应用场景:

cookie:(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。

    (2)保存上次登录的时间等信息。

    (3)保存上次查看的页面

    (4)浏览计数



session:Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
  (1)网上商城中的购物车
  (2)保存用户登录信息
  (3)将某些数据放入session中,供同一用户的不同页面使用
  (4)防止用户非法登录
 7、缺点:cookie:(1)大小受限
        (2)用户可以操作(禁用)cookie,使功能受限
        (3)安全性较低
        (4)有些状态不可能保存在客户端。
        (5)每次访问都要传送cookie给服务器,浪费带宽。
        (6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径下。
     session:(1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。
        (2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全
        (3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。

Session是在服务器端保存用户数据。浏览器第一次发送请求时,服务器自动生成了Session ID来唯一标识这个并将其通过响应发送到浏览器。浏览器第二次发送请求会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户的信息。一般这个Session ID会有个时间限制,默认30分钟超时后毁掉这次Session ID。

 Session和Cookie有一定关系,Session id存在Cookie中,每次访问的时候将Session id传到服务器进行对比。
Cookie 在客户端(浏览器、易伪造、不安全),Session 在服务器端(会消耗服务器资源)。
Cookie 只能保存ASCII字符串,如果是Unicode字符或者二进制数据需要先进行编码。Cookie中也不能直接存取Java对象。 Session能够存取很多类型的数据,包括String、Integer、List、Map等,Session中也可以保存JJava对象。

domain表示的是cookie所在的域,默认为请求的地址,如网址为www.study.com/study,那么domain默认为www.study.com。而跨域访问,如域A为t1.study.com,域B为t2.study.com,那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.study.com;如果要在域A生产一个令域A不能访问而域B能访问的cookie就要将该cookie的domain设置为t2.study.com。注意:一般在域名前是需要加一个"."的,如"domain=.study.com"。




二、WebStorage

WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。

WebStorage两个主要目标:(1)提供一种在cookie之外存储会话数据的路径。(2)提供一种存储大量可以跨会话存在的数据的机制。

HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储)。

1、生命周期:localStorage:localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。

sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。

2、存储大小:localStorage和sessionStorage的存储数据大小一般都是:5MB

3、存储位置:localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信。

4、存储内容类型:localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理

5、获取方式:localStorage:window.localStorage;;sessionStorage:window.sessionStorage;。

6、应用场景:localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据。sessionStorage:敏感账号一次性登录;

WebStorage的优点:

(1)存储空间更大:cookie为4KB,而WebStorage是5MB;

(2)节省网络流量:WebStorage不会传送到服务器,存储在本地的数据可以直接获取,也不会像cookie一样美词请求都会传送到服务器,所以减少了客户端和服务器端的交互,节省了网络流量;

(3)对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage会非常方便;

(4)快速显示:有的数据存储在WebStorage上,再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多,所以速度更快;

(5)安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题;

(6)WebStorage提供了一些方法,数据操作比cookie方便;

   setItem (key, value) ——  保存数据,以键值对的方式储存信息。

      getItem (key) ——  获取数据,将键值传入,即可获取到对应的value值。

      removeItem (key) ——  删除单个数据,根据键值移除对应的信息。

      clear () ——  删除所有的数据

      key (index) —— 获取某个索引的key


 

ajax

ajax后退解决方案
1、直接的方法,点击某一项数据的时候跳转的是新页面
2.1、点击页码,异步请求数据,同时改变地址栏的值
2.2、页面刷新,取到地址栏的值,加载对应页码的数据。
/*
    * 替换当前url 并不导致浏览器页面刷新
    * name 参数名
    * value 参数值
    */
     function replaceUrl  (name, value) {        
        var obj = new Object();
        obj[name] = value;
        obj.rand = Math.random();
        History.replaceState(obj, '', '?' + name + '=' + value);
    }

css

0.5px的线条
.setOnePx{
    position: relative;
  }
  .setOnePx::after{
      position: absolute;
      content: '';
      background-color: #e5e5e5;
      display: block;
      width: 100%;
      height: 1px; /*no*/
      transform: scale(1, 0.5);
      top: 0;
      left: 0;
    }

1px的线条 
.setBorderAll{
       position: relative;
      }
      .setBorderAll::after{
             content:" ";
             position:absolute;
             top: 0;
             left: 0;
             width: 200%;
             height: 200%;
             transform: scale(0.5);
             transform-origin: left top;
             box-sizing: border-box;
             border: 1px solid #E5E5E5;
             border-radius: 4px;
        }

单行省略号
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;

多行省略号
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;

清除浮动
   .div::after{
    content:"";
    display:block;
    height:0;
    clear:both;
    visibility:hidden;
   }
or
.div{
  overflow: hidden;
}
or
浮动后面添加
<div style="clear:both"></div>
or
给浮动元素父级设置高度

相对水平垂直居中
border: 1px solid red;
/*水平*/
margin: 0 auto; 
/*垂直*/
height: 50px;
line-height:50px;
width: 80px;

绝对定位水平居中
    width:100px;
    position:absolute;
    left:50%;
    margin-left:-50px; (此处margin-left的值写该图片宽度的一半)
1、使用绝对定位,设置left值将图片移到正中间,此时图片的左边框在屏幕的中线位置
2、设置margin-left,注意值为负数,咱们把图片向左移图片宽度一半的位置

浮动元素的上下左右居中:
border: 1px solid red;
float: left;
position: absolute;
width: 200px;
height: 100px;
left: 50%;
top: 50%;
margin: -50px 0 0 -100px; 

CSS的盒子模型
标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin
box-sizing:content-box
低版本IE盒子模型:宽度=内容宽度(content+border+padding)+ margin
box-sizing:border-box

CSS权重
!important>内联选择器>id>class>tag>*
a b{}选择器大于b{}选择器

继承
可继承的属性:font-size, font-family, color
不可继承的样式:border, padding, margin, width, height

CSS伪类及伪元素
伪类用于向某些选择器添加特殊的效果。
:link, :visited, :hover, :focus, :active, :first-child, :lang
css3新增的伪类:
:last-child, :first-of-type, :last-of-type, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :root, :empty, :target, :enabled, :disabled, :checked, :not(selector)
伪元素用于向某些选择器设置特殊效果。
::first-letter, ::first-line, ::before, ::after
css3新增的伪元素
::selection
根本区别在于:它们是否创造了新的元素(抽象)

三角形
元素的宽度、高度设为0。然后设置边框样式
width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;

描述css reset的作用和用途?
Reset重置浏览器的css默认属性,浏览器的品种不同,样式不同,然后重置,让他们统一。

绝对定位和相对定位区别
1、参照物不同 绝对定位的参照物是包含块(父级)相对定位的参照物是元素自己本身默认位置
2、绝对定位将对象从文档流中脱离出来因此不占据空间相对位置不破坏正常的文档流顺序,无论是否进行移动元素仍占据空间

透明度
opacity:0.6;
filter:alpha(opacity=60); /* IE8 及更早版本 */

margin重叠解决
兄弟间重叠时
底部元素变为行内盒子(display: inline-block);
父元素与子元素重叠
父元素加入(overflow: hidden);
父元素添加透明边框(border:1px solid transparent);
子元素变为行内盒子(display: inline-block);

css BFC
根据W3C的标准,在页面中元素都一个隐含的属性叫做Block Formatting Context(块级 格式化 环境)简称BFC
开启元素的BFC后,元素会有如下特性:
1.父元素的垂直外边距不会和子元素重叠。
2.开启BFC的元素不会被浮动的元素所覆盖。
3.开启BFC的元素可以包含浮动的子元素。
开启
1.设置元素浮动(此方法,虽然可以撑开父元素,但是会导致父元素的宽度丢失,也会导致下边的元素上移)
2.设置元素绝对定位
3.设置元素display:inline-block;(此方法虽然也可以解决问题,但是转为行内块也会导致宽度丢失,因此也不推荐此方法)
4.将元素的父元素的overflow设置为一个非visible的值(推荐方式:overflow:hidden;是副作用最小的开启BFC方式)
注:ie6不支持BFC,如果要同时兼容ie6  可以加一个属性 zoom:1;即可(zoom表示放大,写几就是放大几倍,此属性仅支持IE,且IE8以下)

display:inline-block;引起的间距
原因:排版问题吧个人认为
解决:
1、inline-block 即内联块,可以水平排版 即标签水平排版
2、使用margin-left:-3px;(谷歌是-3px,ie好像是-1px)
3、使用font-size:0px;(最外层是容器)


html

Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?
声明位于文档中的最前面,处于 标签之前。告知浏览器以何种模式来渲染文档。
严格模式的排版和 JS 运作模式是,以该浏览器支持的最高标准运行。
在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。
DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。

meta响应式设计
<meta name="’viewport’" content="”width=device-width," initial-scale="1." maximum-scale="1,user-scalable=no”"/>

html5新增的标签
section:定义文档中的一个章节
nav:定义只包含导航链接的章节
header:定义页面或章节的头部。它经常包含 logo、页面标题和导航性的目录。
footer:定义页面或章节的尾部。它经常包含版权信息、法律信息链接和反馈建议用的地址。

新特性
a. HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。
b. 语义化更好的内容标签(header,nav,footer,aside,article,section)
c. 新的技术webworker, websocket等
d. 音频、视频API(audio,video) 画布(Canvas) API 地理(Geolocation) API 本地离线存储 localStorage 长期存储数据
e. sessionStorage 的数据在页面会话结束时会被清除
f. 表单控件,calendar、date、time、email、url、search
移除的元素:
a. 纯表现的元素:basefont,big,center, s,strike,tt,u;
b. 对可用性产生负面影响的元素:frame,frameset,noframes;

标签语义化的理解
1、让页面呈现出清晰的结构
2、遵循语义化即一个标准 便于团队开发和维护,语义化更具可读性
3、有利于SEO 搜索引擎会识别标签


浏览器是如何渲染页面的?
渲染的流程如下:
1.解析HTML文件,创建DOM树。
   自上而下,遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)。
2.解析CSS。优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式;
3.将CSS与DOM合并,构建渲染树(Render Tree)
4.布局和绘制,重绘(repaint)和重排(reflow)

浏览器兼容问题

CSS透明
IE:filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=60)。
FF:opacity:0.6。

IE6下无法设置1px的行高,原因是由其默认行高引起的       
解决办法:为期设置overflow:hidden;或者line-height:1px;

不同浏览器的标签默认的外补丁和内补丁不同 即margin padding不一致
使用*{margin:0px;padding:0px;}

盒子模型不一致
ie是box-sizing: border-box;
google 是box-sizing: content-box;

js
1.标准的事件绑定方法函数为addEventListener,但IE下是attachEvent;
2.事件的捕获方式不一致,标准浏览器是由外至内,而IE是由内到外,但是最后的结果是将IE的标准定为标准
3.window.event获取的。并且获取目标元素的方法也不同,标准浏览器是event.target,而IE下是event.srcElement
4.ajax的实现方式不同,这个我所理解的是获取XMLHttpRequest的不同,IE下是activeXObject
5.IE中不能操作tr的innerHtml7.获得DOM节点的父节点、子节点的方式不同
其他浏览器:parentNode  parentNode.childNodes       
IE:parentElement parentElement.children
6、说明:IE下,event对象有x,y属性,但是没有pageX,pageY属性;
Firefox下,event对象有pageX,pageY属性,但是没有x,y属性. 
解决方法:使用mX(mX  =   event.x   ?   event.x  :   event.pageX;)来代替IE下的event.x或者Firefox下的event.pageX.

自适应

media查询
flex布局
百分比布局(% vw clac)
使用rem

正则

var str = "   23   23   ";
var str2 = str.replace(/\s*/g,"");
console.log(str2); // 2323

陆续更新....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章