JavaScript基本面试题

JS的几条基本规范:

1、不要在同一行声明多个变量

2、请使用===/!==来比较true/false或者数值

3、使用对象字面量替代new Array这种形式

4、不要使用全局变量

5、Switch语句必须带有default分支

6、函数不应该有时候有返回值,有时候没有返回值

7、For循环必须使用大括号

8、IF语句必须使用大括号

9、for-in循环中的变量应该使用var关键字明确限定作用域,从而避免作用域污染。

JS引用方法:

// 行内引入
<body>
    <input type="button" onclick="alert('行内引入')" value="按钮"/>
    <button onclick="alert(123)">点击我</button>
</body>

// 内部引入
<script>
    window.onload = function() {
        alert("js内部引入!")
    }
</script>

// 外部引入
<body>
    <div></div>
    <script type="text/javascript" src="./js/index.js"></script>
</body>

注意:

1、不推荐写行内或者HTML中插入<script>,因为浏览器解析顺序的缘故,如果解析到死循环之类的JS代码,会卡住页面。

2、建议在onload事件之后,即等HTML、CSS渲染完毕再执行代码

JS的基本数据类型

Undefined、Null、Boolean、Number、String

新增:Symbol

数组操作

在JavaScript中,用的较多的之一无疑是数组操作,这里过一遍数组的一些用法:

map:遍历数组,返回回调返回值组成的新数组

forEach:无法break,可以用try/catch中throw new Error来停止

filter:过滤

some:有一项返回true,则整体为true

every:有一项返回false,则整体为false

join:通过指定连接符生成字符串

push/pop:末尾推入和弹出,改变原数组,返回推入/弹出项 【有误】

unshift/shift:头部推入和弹出,改变原数组,返回操作项  【有误】

sort(fn)/reverse:排序与反转,改变原数组

concat:连接数组,不影响原数组,浅拷贝

slice(start,end):返回截断后的新数组,不改变原数组

splice(start,number,value...):返回删除元素组成的数组,value为插入项,改变原数组

indexof/lastIndexOf(value,fromIndex):查找数组项,返回对应的下标

reduce/reduceRight(fn(prev,cur),defaultPrev):两两执行,prev为上次化简函数的return值,cur为当前值(从第二项开始)

JS有哪些内置对象

Object是JavaScript中所有对象的父对象

数据封装对象:Object、Array、Boolean、Number和String

其他对象:Function、Arguments、Math、Date、RegExp、Error

get请求传参长度的误区

误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的

实际上HTTP协议从未规定GET/POST的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:

1、HTTP协议未规定GET和POST的长度限制

2、GET的最大长度显示是因为浏览器和web服务器限制了URL的长度

3、不同的浏览器和WEB服务器,限制的最大长度是不一样的

4、要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度8182byte

补充get和post请求在缓存方面的区别

get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存

post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

闭包

什么是闭包?

函数A里面包含了函数B,而函数B里面使用了函数A的变量,那么函数B被称为闭包。

又或者:闭包就是能够读取其他函数内部变量的函数

function A() {
    var a=1;
    function B() {
        console.log(a);
    }
    return B();
}

闭包的特征

函数内再嵌套函数

内部函数可以引用外层的参数和变量

参数和变量不会被垃圾回收制回收

对闭包的理解

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中

闭包的另一个用处,是封装对象的私有属性和私有方法

闭包的好处

能够实现封装和缓存等

闭包的坏处

就是消耗内存、不正当使用会造成内存溢出的问题

使用闭包的注意点

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露

解决方法:在退出函数之前,将不使用的局部变量全部删除

//闭包的经典问题
for(var i=0;i<3; i++) {
    setTimeout(function() {
        console.log(i);
    },1000);
}

这段代码输出答案:3个3

解析:首先,for循环是同步代码,先执行三遍for,i变成了3;然后再执行异步代码setTimeout,这时候输出的i,只能是3个3了

有什么办法依次输出0,1,2

第一种方法

// 使用let
for(let i=0; i<3; i++) {
    setTimeOut(function () {
        cosole.log(i)
    },1000);
}

在这里,每个let和代码块结合起来形成块级作用域,当setTimeout()打印时,会寻找最近的块级作用域的i,所以依次打印出0,1,2

如果这样不明白,我们可以执行下边这段代码

for(let i=0;i<3;i++) {
    console.log("定时器外部:"+i);
    setTimeout(function() {
        console.log(i);
    },1000);
}

此时浏览器依次输出的是:

定时器外部:0

定时器外部:1

定时器外部:2

即代码还是先执行for循环,但是当for结束执行到了setTimeout的时候,它会做个标记,这样到了console.log(i)中,i就能找到这个块中最近的变量定义

第二种方法

// 使用立即执行函数解决闭包问题
for(let i=0;i<3;i++) {
    (function(i) {
        setTimeout(function() {
            console.log(i) 
        },1000);
    })(i)
}

JS作用域及作用域链

作用域

在JavaScript中,作用域分为全局作用域和函数作用域

全局作用域:

代码在程序的任何地方都能被访问,window对象的内置属性都拥有全局作用域

函数作用域:

在固定的代码片段才能被访问

例子:

作用域有上下级关系,上下级关系的确定就看函数是在哪个作用域下创建的。如上,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

变量取值:到创建这个变量的函数的作用域中取值

作用域链

一般情况下,变量取值到创建这个变量的函数的作用域中取值。

但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

原型和原型链

原型和原型链的概念

每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去

原型和原型链的关系

instance.constructor.prototype = instance._proto_

原型和原型链的特点

JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变

当我们需要一个属性的时候,JavaScript引擎会先看当前对象中是否有这个属性,如果没有的话就会查找他的Prototype对象是否有这个属性,如此递推下去,一直检索到Object内建对象

组件化和模块化

组件化

为什么要组件化开发

有时候页面代码量太大,逻辑太多或者同一个功能组件在许多页面均有使用,维护起来相当复杂,这个时候,就需要组件化开发来进行功能拆分、组件封装,以达到组件通用性,增强代码可读性,维护成本也能大大降低

组件化开发的优点

很大程度上降低系统各个功能的耦合性,并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说,是有很大的好处的,耦合性的降低,提高了系统的伸展性,降低了开发的复杂度,提升开发效率,降低开发成本

组件化开发的原则

专一

可配置性

标准型

复用性

可维护性

模块化

为什么要模块化

早期的JavaScript版本没有块级作用域、,没有类、没有包、也没有模块,这样会带来一些问题,如复用、依赖、冲突、代码组织混乱等,随着前端的膨胀,模块化显得非常迫切

模块化的好处

避免变量污染,命名冲突

提高代码复用率

提高了可维护性

方便依赖关系管理

模块化的几种方法

  • 函数封装

var myModule = {
    var1:1,
    var2:2,
    fn1:function() {
    },
    fn2:function() {
    }
}

总结:这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系

缺陷:外部可以随意修改内部成员,这样就会产生意外的安全问题

  • 立即执行函数表达式(IIFE)
var myModule = (function() {
    var var1 = 1;
    var var2 = 2;
    
    function fn1() {
    
    }
    function fn2() {

    }
    return {
        fn1:fn1,
        fn2:fn2
    };
})();

总结:这样在模块外部无法修改我们没有暴露出来的变量、函数

缺点:功能相对较弱,封装过程增加了工作量,仍会导致命名空间污染可能、闭包是有成本的

图片的预加载和懒加载

  • 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
  • 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数

两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。预加载则会增加服务器前端压力,懒加载对服务器有一定的缓解压力作用。

mouseover和mouseenter的区别

mouseover:

当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout

mouseenter:

当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave

解决异步回调地狱

promise、generator、async/await

对this对象的理解

this总是指向函数的直接调用者(而非间接调用者)

如果有new关键字,this指向new出来的那个对象

在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值