第一章:对面试题的思考
- 拿到一个面试题,你第一时间应该看到知识点
- 扩展知识
例子 | 知识点 |
---|---|
JS中使用typeof 能得到哪些类型? |
JS变量类型 |
何时用=== 何时用== ? |
强制类型转换 |
windows.onload 和DOMContentLoaded 的区别? |
浏览器渲染过程 |
用JS创建十个<a> 标签,点击的时候弹出来对应的序号 |
作用域 |
简述如何实现一个模块加载器,实现类似require.js 的基本功能 |
JS模块化 |
实现数组的随机排序 | JS基础算法 |
第二章:JS基础知识(上)
变量类型和计算
题目
- JS中使用
typeof
能得到哪些类型? - 何时用
===
何时用==
? - JS中有哪些内置函数?
- JS变量按照存储方式区分为哪些类型并描述其特点
- 如何理解JSON
变量类型
值类型:每一个值存放在一个内存位置
引用类型(对象、数组、函数):内存位置存的指向对象的指针,该特性可以有效节省占用的内存,共用内存空间
typeof
运算符:数值、字符串、布尔值分别返回number
、string
、boolean
。函数返回function
、undefined
返回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中有哪些内置函数? | Object 、Array 、Boolean 、Number 、String 、Function 、Date 、RegExp 、Error |
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一个对象的过程
- 创建一个新对象
- this指向这个新对象
- 执行代码,即对this赋值
- 返回this
zeptp(或其他框架)源码中如何使用原型链
第三章:JS基础知识(中)
作用域和闭包
题目
- 说一下对变量提升对理解
- 说明
this
几种不同的使用场景 - 用JS创建十个
<a>
标签,点击的时候弹出来对应的序号 - 如何理解作用域
- 实际开发中闭包的应用
变量提升-执行上下文
函数声明会被提前
- 范围:一段
<script>
或者一个函数 - 全局:变量定义、函数声明 一段
<script>
- 函数:变量定义、函数声明、
this
、arguments
function fn(){
//函数声明
}
var fn1 =function(){
//函数表达式
}
var a =100//函数表达式与此类似
this
this要在执行时才能确认,定义时无法确认
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行(此处
this===window
) call
、apply
、bind
call、apply、bind可以将某个函数的this指向修改为传入这三个方法中的第一个参数,其中call、apply会立即执行,bind返回的是一个函数,需调用后执行。
第二个参数是传入要执行的方法中的参数,call、bind是独立传递参数,apply是以数组传递参数的
作用域
- 没有块级作用域
- 只有函数和全局作用域
作用域链
当前作用域没有定义的变量即“自由变量”
函数的父级作用域是函数定义的位置的作用域
闭包
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
使用场景:
- 函数作为返回值
- 函数作为参数传递
题解
说一下对变量提升对理解
- 变量定义
- 函数声明(注意和函数表达式的区别)
说明this
几种不同的使用场景
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
call
、apply
、bind
用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基础知识(下)
异步和单线程
题目
- 同步和异步的区别是什么?分别具一个同步和异步的例子
- 一个关于setTimeout的笔试题
- 前端使用异步的场景有哪些
什么是异步(对比同步)
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
一样阻塞程序运行,因此,所有的“等待的情况都需要异步”
前端使用异步的场景
- 定时任务:
setTimeout
,setInveral
- 网络请求:
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
前端使用异步的场景有哪些
- 定时任务:
setTimeout
、setImverval
- 网络请求: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),浏览器需要做:
- 定义一个
window
全局变量,对象类型 - 给它定义一个
alert
属性,属性值是一个函数
获取元素document.getElementById(id),浏览器需要:
- 定义一个
document
全局变量,对象类型 -
给它定义一个
getElementById
的属性,属性值是一个函数
- 但是W3C标准没有规定任何JS基础相关的东西
- 不管什么事变量类型、原型、作用域和异步
- 只管定义用于浏览器中JS操作页面的API和全局变量
全面考虑,JS内置的全局函数和对象有哪些?
- 之前讲过的
Object
、Array
、Boolean
、String
、Math
、JSON
等 - 刚刚提到的
window
、document
- 接下来要讲到的所有未定义的全局变量,如
navigator.userAgent
总结
- 常说的JS(浏览器执行的JS)包含两部分:
- JS基础知识(ECMA 262标准)
- JS-Web_API(W3C标准)
DOM操作
- DOM是哪种基本的数据结构
- DOM操作常用的API有哪些
- DOM节点的
attr
和property
有何区别
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节点,以及节点的
Attributr
和property
- 获取父节点,获取子节点
- 新增节点,删除节点
DOM节点的attributr
和Attributr
有何区别
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
- 服务器可以根据请求,动态生成一个文件,返回
- 同理域
- 例如:访问imooc.com的一个接口
- 返回内容格式如
callback({x:100,y:200})
(可动态生成)
<script>
window.callback = function(data){
//这里是我们跨域得到的信息
console.log(data)
}
</script>
<script src="http://coding.m.imooc.com/api.js"></script>
<!--返回callback({x:100,y:200})-->
服务器端设置 http header
- 另外一个解决跨域的简洁方法,需要服务器端来做
- 作为交互方,我们必须知道这个方法
- 是将来解决跨域问题的一个趋势
题解
手动编写一个ajax,不依赖第三方库
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)
跨域的几种实现方式
- JSONP
- 服务器端设置 http header
存储
- 请描述一下
cookie
,sessionStorage
和localStorage
的区别
cookie
- 本身用于客户端和服务器端通信
- 但是它有本地存储的功能,于是就被“借用”
- 使用
document.cookie=...
获取和修改即可
cookie用于存储的缺点
- 存储量太小,只有4KB
- 所有http请求都带着,会影响获取资源的效率
- API简单,需要封装才能用
document.cookie=...
sessionStorage
和localStorage
- HTML5专门为存储而设计,最大容量5M
- API简单易用:
localStorage.setItem(key,value);localStorage.getItem(key);
sessionStorage
会随浏览器关闭而清除,localStorage
不会- iOS Safari隐藏模式下
localStorage.getItem
会报错 - 建议统一使用try-catch封装
题解
请描述一下cookie
,sessionStorage
和localStorage
的区别
- 容量
- 是否会携带到ajax中
- API易用性
第七章:开发环境
- 面试官想通过开发环境了解面试者的经验
- 开发环境,最能体现工作产出的效率
- 以聊天的形式为主,而不是出具体的问题
关于开发环境
- IDE(写代码的效率)
- git(代码版本管理,多人协作开发)
- js模块化
- 打包工具
- 上线回滚的流程
IDE
- webstorm
- sublime
- vscode
- atom