参考前端javascript面试技巧

第一章:对面试题的思考

  1. 拿到一个面试题,你第一时间应该看到知识点
  2. 扩展知识
例子 知识点
JS中使用typeof能得到哪些类型? JS变量类型
何时用===何时用== 强制类型转换
windows.onloadDOMContentLoaded的区别? 浏览器渲染过程
用JS创建十个<a>标签,点击的时候弹出来对应的序号 作用域
简述如何实现一个模块加载器,实现类似require.js的基本功能 JS模块化
实现数组的随机排序 JS基础算法

第二章:JS基础知识(上)

变量类型和计算

题目

  • JS中使用typeof能得到哪些类型?
  • 何时用===何时用==
  • JS中有哪些内置函数?
  • JS变量按照存储方式区分为哪些类型并描述其特点
  • 如何理解JSON
变量类型

值类型:每一个值存放在一个内存位置

引用类型(对象、数组、函数):内存位置存的指向对象的指针,该特性可以有效节省占用的内存,共用内存空间

typeof运算符:数值、字符串、布尔值分别返回numberstringboolean。函数返回functionundefined返回undefined。对象、null返回object

typeof只能区分值类型和函数,其他引用类型无法区分

变量计算-强制类型转换

会发生强制类型转换的场景:

  • 字符串拼接
  • ==运算符
  • if语句
  • 逻辑运算符

判断一个变量会被当作true还是flase:

var a=100
console.log(!!a)
题目 解答
JS中使用typeof能得到哪些类型? 上述答案
何时用===何时用== 参考jqurey源码,判断obj.a == null相当于obj.a===null || obj.a===undefined,其余全用===
JS中有哪些内置函数? ObjectArrayBooleanNumberStringFunctionDateRegExpError
JS变量按照存储方式区分变量类型 值类型和引用类型
如何理解JSON JSON只不过是JS对象(也是一种数据格式)有两个APIJSON.string({a:10,b:20}),JSON.parse('{"a":10,"b":20}')

原型和原型链

题目

  • 如何准确判断一个变量是数组类型
  • 写一个原型链继承的例子
  • 描述new一个对象的过程
  • zeptp(或其他框架)源码中如何使用原型链

构造函数

function Foo(name,age){
this.name = name
this.age = age
this.calss = class-1
//return this//默认有这一行
}
var f = new Foo('zhangsan',20)

构造函数-扩展

  • var a ={}其实是var a = new Object()的语法糖
  • var a =[]其实是var a = new Array()的语法糖
  • function Foo(){…}其实是var Foo = new Function (…)
  • 使用instanceof判断一个函数是否是一个变量的构造函数

原型规则和示例

5条原型规则——学习原型链的基础

  • 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)
  • 所有的引用类型(数组、对象、函数),都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
  • 所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通对象
  • 所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的prototype属性值(obj.__proto__===Object.prototype
  • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去他的__proto__(即它的构造函数的prototype)中寻找

this永远指向函数对象本身
循环对象自身的属性

var item
for(item in f){
    //高级浏览器已经在 for in中屏蔽啦来自原型的属性,但还是建议加上判断保证程序的健壮性
    if(f.hasOwnProrerty(item)){
        console.log(item)
    }
}

原型链

instanceof

instanceof用于判断引用类型属于哪个构造函数的方法
f instanceof Foo的判断逻辑是:
f__proto__ 一层一层往上,能否对应到Foo.prototype
再试着判断f instanceof Object

题解

如何准确判断一个变量是数组类型

arr instanceof Array

写一个原型链继承的例子
function Elem(id){
    this.elem = document.getElementById(id)
}

Elem.prototype.html = function(val){
    var elem = this.elem
    if(var){
        elem.innerHTML = var
        return this//链式操作
    }else{
        return elem.innerHTML
    }
}

Elem.prototype.on =function(type,fn){
    var elem = this.elem
    elem.addEventListener(type,fn)
    return this
}

var div1 = new Elem('div1')
div1.html('<p>hello world').on('click',function(){
    alert('clicked')
}).html('<p>javascript</p>')
描述new一个对象的过程
  1. 创建一个新对象
  2. this指向这个新对象
  3. 执行代码,即对this赋值
  4. 返回this
zeptp(或其他框架)源码中如何使用原型链

第三章:JS基础知识(中)

作用域和闭包

题目

  • 说一下对变量提升对理解
  • 说明this几种不同的使用场景
  • 用JS创建十个<a>标签,点击的时候弹出来对应的序号
  • 如何理解作用域
  • 实际开发中闭包的应用

变量提升-执行上下文

函数声明会被提前

  • 范围:一段<script>或者一个函数
  • 全局:变量定义、函数声明 一段<script>
  • 函数:变量定义、函数声明、thisarguments
function fn(){
    //函数声明
}
var fn1  =function(){
    //函数表达式
}
var a =100//函数表达式与此类似

this

this要在执行时才能确认,定义时无法确认

  • 作为构造函数执行
  • 作为对象属性执行
  • 作为普通函数执行(此处this===window)
  • callapplybind

call、apply、bind可以将某个函数的this指向修改为传入这三个方法中的第一个参数,其中call、apply会立即执行,bind返回的是一个函数,需调用后执行。

第二个参数是传入要执行的方法中的参数,call、bind是独立传递参数,apply是以数组传递参数的

作用域

  • 没有块级作用域
  • 只有函数和全局作用域

作用域链

当前作用域没有定义的变量即“自由变量”
函数的父级作用域是函数定义的位置的作用域

闭包

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

使用场景:

  • 函数作为返回值
  • 函数作为参数传递

题解

说一下对变量提升对理解
  • 变量定义
  • 函数声明(注意和函数表达式的区别)
说明this几种不同的使用场景
  • 作为构造函数执行
  • 作为对象属性执行
  • 作为普通函数执行
  • callapplybind
用JS创建十个<a>标签,点击的时候弹出来对应的序号
var i
for(i=0;i<10;i++){
    (function(i){
        //函数作用域
        var a =document.createElement('a')
        a.innerHTML=i+'<br>'
        a.addEventListener('click',function(e){
            e.preventDefault()
            alert(i)//自由变量,要去父作用域获取值
        })
        document.body.appendChild(a)
    })(i)
如何理解作用域
  • 自由变量
  • 作用域链,即自由变量的查找
  • 闭包的两个场景
实际开发中闭包的应用

封装变量,收敛权限

function isFirsLoad(){
    var _list=[]//_表示该变量为私有的
    return function(id){
        if(_list.indexOf(id)>=0){//indexOf返回某个指定的字符串值在字符串中首次出现的位置,找不到返回-1
            return false
        }else{
            _list.push(id)
            return true
        }
    }
}

var firstLoad = isFirstLoad()
firstLoad(10)//true
firstLoad(10)//false
firstLoad(20)//true
//在isFirstLoad函数外不可能修改——list的值

第三章:JS基础知识(下)

异步和单线程

题目

  1. 同步和异步的区别是什么?分别具一个同步和异步的例子
  2. 一个关于setTimeout的笔试题
  3. 前端使用异步的场景有哪些

什么是异步(对比同步)

consloe.log(100)
setTimeout(function(){
    console.log(200)
},1000)
console.log(300)
//输出顺序:100->300->200,不会有阻塞出现的现象

作为对比,同步的情况:

console.log(100)
alert(200)//1秒后点击确认
console.log(300)
何时需要异步

可能发生等待的情况,等待过程中不能像alert一样阻塞程序运行,因此,所有的“等待的情况都需要异步”

前端使用异步的场景

  • 定时任务:setTimeoutsetInveral
  • 网络请求:ajax请求,动态<img>加载
  • 事件绑定
//ajax请求代码示例,会先打印start和end,然后等待请求
console.log('start')
$.get('./data1.json',function(data1){
    console.log(data1)
})
console.log('end')
//<img>加载示例
console.log('start')
var img = document.creatrElement('img')
img.onload = function(){
    console.log('loaded')
}
img.src='/xxx.png'
console.log('end')
//事件绑定示例
console.log('start')
document.getElementById('btn1').addEventListener('click',function(){
    alert('clicked')
})
console.log('end')

异步和单线程

因为JavaScript单线程的特点,同时只能“干一件事”

所有异步的代码会被先“拿出去”

程序执行完后检查被“拿出去”的代码是否可以立即执行

题解

同步和异步的区别是什么?分别具一个同步和异步的例子
  • 同步会阻塞代码执行,而异步不会
  • alert是同步,setTimeout是异步
一个关于setTimeout的笔试题
console.log(1)
setTimeout(function(){
    console.log(2)
},1000)
console.log(3)
setTimeout(function(){
    console.log(4)
})
console.log(5)

输出顺序:13542

前端使用异步的场景有哪些
  • 定时任务:setTimeoutsetImverval
  • 网络请求:ajax请求,动态<img>加载
  • 事件绑定

重点总结

  • 异步和同步的区别
  • 异步和单线程的关系
  • 异步在前端的引用场景

其他知识

  • 获取2017-06-10格式的日期
  • 获取随机数,要求是长度一致的字符串
  • 写一个能遍历对象和数组的通用forEach函数

日期

Date.now()//获取当前时间毫秒数,1970开始
var dt = new Date()
dt.getTime()//获取毫秒数
dt.getFullYear()//年
dt.getMonth()//日(0-11)
dt.getDate()//日(0-31)
dt.getHours()//小时(0-23)
dt.getMinutes()//分钟(0-59)
dt.getSetSeconds()//秒(0-59)

Math

一般用于清除缓存

  • 获取一个[0,1)的随机数Math.random()

数组API

  • forEach遍历所有元素
var = [1,2,3]
arr.forEach(function(item,index)){
    //遍历数组的所有元素
    console.log(index,item)
}
  • every判断所有元素是否都符合条件
var = [1,2,3]
var result = arr.every(function(){
    //用来判断所有数组元素,都满足一个条件
    if(item < 4){
        return ture
    }
})
console.log(result)
  • some判断是否至少有一个元素符合条件
var = [1,2,3]
var result = arr.every(function(){
    //用来判断所有数组元素,只要有一个满足条件即可
    if(item < 2){
        return ture
    }
})
console.log(result)
  • sort排序
var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a,b){
    //从小到大排序
    return a-b
    //从大到小排序
    //return b-a
})
  • map对元素重新组装,生成新数组
var arr = [1,4,2,3,5]
var arr2 = arr.map(function(item,index){
    //将元素重新组装,并返回
    return '<br>' + i + '</br>'
})
console.log(arr2)
  • filter过滤符合条件的元素
var arr = [1,4,2,3,5]
var arr2 = arr.filter(function(item,index){
    //通过某一个条件过滤数组
    if(item>=2){
        return true
    }
})
console.log(arr2)

对象API

for...in...

var obj = {
    x:100,
    y:200,
    z:300
}
var key
for(key in obj){
    if(obj.hasOwnProperty(key)){
        console.log(key,obj[key])
    }
}

题解

获取2017-06-10格式的日期
function formatDate(dt){
    if(!dt){
        dt =new Date()
    }
    var year = dt.getFullYear()
    var month = dt.getMonth()+1
    var date = dt.getDate()
    if(month<10){
        month ='0' + month
    }
    if(date<10){
        date ='0' + date
    }

    return year + '-' + month + '-' + date
}
console.log(formatDate(new Date()))
获取随机数,要求是长度一致的字符串
var random = Math.random()
random = random + '0000000000'
random = random.slice(0,10)

console.log(random)
写一个能遍历对象和数组的通用forEach
function forEach(obj,fn){
    var key
    if(obj instanceof Array){
        obj.forEach(function(item,index){
            fn(index,item)
        })
    }else{
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                fn(key,obj[key])
            }
        }
    }
}
var arr =[1,2,3]
forEach(arr,function(index,item){
    console.log(index,item)
})
var obj = {x:100,y:200}
forEach(obj,function(key,val){
    console.log(key,val)
})

第五章:从基础知识到JS-Web-API

基础知识回顾:

  • 特点:表面看来并不难用于工作中开发代码
  • 内置函数:Object Array Boolean String
  • 内置对象:Math JSON

  • JS基础知识基于ECMA 262标准

  • JS-Web-API基于W3C标准

W3C标准中关于JS的规定有:

  • DOM操作
  • BOM操作
  • 事件绑定
  • ajax请求(包括http协议)
  • 存储

页面但矿是window.alert(123),浏览器需要做:

  1. 定义一个window全局变量,对象类型
  2. 给它定义一个alert属性,属性值是一个函数

获取元素document.getElementById(id),浏览器需要:

  1. 定义一个document全局变量,对象类型
  2. 给它定义一个getElementById的属性,属性值是一个函数

  • 但是W3C标准没有规定任何JS基础相关的东西
  • 不管什么事变量类型、原型、作用域和异步
  • 只管定义用于浏览器中JS操作页面的API和全局变量

全面考虑,JS内置的全局函数和对象有哪些?

  1. 之前讲过的ObjectArrayBooleanStringMathJSON
  2. 刚刚提到的windowdocument
  3. 接下来要讲到的所有未定义的全局变量,如navigator.userAgent

总结

  • 常说的JS(浏览器执行的JS)包含两部分:
  • JS基础知识(ECMA 262标准)
  • JS-Web_API(W3C标准)

DOM操作

  • DOM是哪种基本的数据结构
  • DOM操作常用的API有哪些
  • DOM节点的attrproperty有何区别

DOM的本质

  • html是一种特殊的xml
  • DOM是浏览器将HTML结构化的使JS可识别的东西

DOM节点操作

  • 获取DOM节点
document.getElementById('div1')//元素
document.getElementByTagName('div')//集合
document.getElementByClassName('.container')//集合
  • prototype

修改的js对象的属性

var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width)
p.style.width = '100px'
console.log(p.className)
p.className = 'p1'
console.log(p.nodeName)
console.log(p.nodeType)
  • Attribute
    修改的html文档里的标签
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('date-name')
p.setAttribute('date-name','imooc')
p.getAttribute('style')
p.setAttribute('style','font-size:30px;')

DOM结构操作

  • 新增节点
var div1 = document.getElementById('div1')
//添加新节点
var p1 = document.createElement('p')
p1.innerHTMl = 'this is p'
div1.appendChild(p1)//添加新创建的元素
//移动已有节点
var p2 = document.getElementById('p2')
div1.appendChild(p2)
  • 获取父元素和子元素
  • 删除节点
var div1 = document.getElementById('div1')
var parent = div1.parentElement

var child = div1.childNodes
div1.removeChild(child[0])

题解

DOM是哪种基本的数据结构

DOM操作常用的API有哪些
  • 获取DOM节点,以及节点的Attributrproperty
  • 获取父节点,获取子节点
  • 新增节点,删除节点
DOM节点的attributrAttributr有何区别
  • property只是一个JS对象的属性的修改和获取
  • Attributr是对html标签属性的修改和获取

BOM操作

Browser Object Model

  • 如何检测浏览器的类型
  • 拆解URL的各部分

navigator

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

screen

console.log(screen.width)
console.log(screen.height)

location

console.log(location.href)
console.log(location.host)
console.log(location.protocol)
console.log(location.pathname)
console.log(location.search)
console.log(location.hash)

history

history.back()

第六章:JS-Web-API(下)

  • 编写一个通用的事件监听函数
  • 描述事件冒泡流程
  • 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件

通用事件绑定

vat btn = document.getElementById('btn1')
btn.addEventListener('clicked',function(event){
    console.log('clicked')
})
function bindEvent(elem,type,fn){
    elem.addEventListener(type,fn)
}
var a =document.getElementById('link1')
bindEvent(a,'click',function(e){
    e.preventDefault()//阻止默认行为
    alert('clicked')
})
关于IE低版本的兼容性
  • IE低版本使用attachEvent绑定事件,和W3C标准不一样
  • IE低版本使用量已非常少,很多网站都早已不支持
  • 建议对IE低版本的兼容性了解即可,无需深究
  • 如果遇到对IE低版本要求苛刻的面试,果断放弃

事件冒泡

<body>
    <div id="div1">
        <p id="p1">激活</p>
        <p id="p2">取消</p>
        <p id="p3">取消</p>
        <p id="p4">取消</p>
    </div>
    <div id="div2">
        <p id="p5">取消</p>
        <p id="p6">取消</p>
    </div>
var p1 = document.getElementById('p1')
var body = document.body
bindEvent(p1,'click',function(e){
    e.stopPropatation()//阻止冒泡,没有这行的话还会触发显示取消
    //e.preventDefault() //取消默认行为
    alert('激活')
})
bindEvent(body,'click',function(e){
    alert('取消')
})

代理

<div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
    <a href="#">a4</a>
    <!--会随时新增更多a标签-->
</div>
var div1 = document.getElementById('div1')
div1.addEventListener('click',function(e){
    var target = e.target//获取触发位置
    if(target.nodeName === 'A'){
        alert(target.innerHTML)
    }
})

完善通用绑定事件的函数

function bindEvent(elem,type,selector,fn){
    if(fn == null){
        fn = selector
        selector = null
    }
    elem.addEventListener(type,function(e){
        var target 
        if(selector){
            target = e.target
            if(target.matches(selector)){
                fn.call(target,e)
            }
        }else{
            fn(e)
        }
    })
}

//使用代理
var div1 = document.getElementById('div1')
bindEvent(div1,'click','a',function(e){
    console.log(this.innerHTML)
})
//不使用代理
var a = document.getElementById('a1')
bindEvent(div1,'click',function(e){
    console.log(a.innerHTML)
})
代理的好处
  • 代码简洁
  • 减少浏览器内存占用

题解

编写一个通用的事件监听函数
描述事件冒泡流程
  • DOM树形结构
  • 事件冒泡
  • 阻止冒泡
  • 冒泡的应用——代理
对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
  • 使用代理
  • 知道代理的优点——代码简洁、给浏览器的压力小

Ajax

  • 手动编写一个ajax,不依赖第三方库
  • 跨域的几种实现方式

XMLHttpRequest

var xhr = new XMLHttpRequest()
xhr.open('GET',"/api",false)
xhr.onreadystatechange = function(){
    //这里函数异步执行
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            alert(xhr.responseText)
        }
    }
}
xhr.send(null)
IE兼容性问题
  • IE低版本使用ActiveObject,和W3C标准不一样

状态码说明

readyState
  • 0-(未初始化)还没调用send()方法
  • 1-(载入)已调用send()方法,正在发送请求
  • 2-(载入完成)send()方法执行完成,已接收到全部相映内容
  • 3-(交互)正在解析响应内容
  • 4-(完成)响应内容解析完成,可以在客户端调用了
状态码status
  • 2xx-表示成功处理请求。如200
  • 3xx-需要重定向,浏览器直接跳转
  • 4xx-客户端请求错误,如404
  • 5xx-服务器端错误

跨域

什么是跨域
  • 浏览器有同源策略,不允许ajax访问其他域接口
  • 跨域条件:协议、域名、端口,有一个不同就算跨域
可以跨域的三个标签
  • 但是有三个标签允许跨域加载资源
  • <img src=xxx>加载图片
  • <link href=xxxx>加载css
  • <script src=xxx>加载js
三个标签的场景
  • <img>用于打点统计,统计网站可能是其他域
  • <link><script>可以使用CDN,CDN的也是其他域
  • <script>可以用于JSONP
跨域注意事项
  • 所有的跨域请求都必须经过信息提供方允许
  • 如果未经允许即可获取,那是浏览器同源策略出现漏洞
JSONP
JSONP实现原理
  • 加载http://coding.m.imooc.com/classindex.html
  • 不一定服务器端真正有一个classindex.html
  • 服务器可以根据请求,动态生成一个文件,返回
  • 同理域

作者 Assaultcore

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注