js基础Interview

基础知识

  • 原形 原型链
  • 作用域 闭包
  • 异步 单线程

JS API

  • DOM操作
  • Ajax
  • 事件绑定

开发环境

  • 版本管理
  • 模块化
  • 打包工具

运行环境

  • 页面渲染
  • 性能优化

题目

  • JS中使用typeof能得到的哪些类型?
  • 何时使用===何时使用==?
  • window.onload和DOMContentLoaded的区别?
  • 用JS创建10个a标签,点击的时候弹出来对应的序号
  • 简述如何实现一个模块加载器,实现类似require.js的基本功能
  • 实现数组的随机排序

引用类型

对象、数组、函数

变量计算 - 强制类型转换

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

JS中的内置函数 - 数据封装类对象

  • Object
  • Array
  • Boolean
  • Number
  • String
  • Function
  • Date
  • RegExp
  • Error

JS按存储方式区分变量类型

如何理解JSON

JSON只不过是一个JS对象而已如Math

原型和原型链-构造函数

题目

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

知识点

  • 构造函数
  • 构造函数-扩展
  • 原形规则和示例
  • 原型链
  • instanceof

构造函数

function Foo(name, age){ // Foo 构造函数首字母一般大写
    this.name = name
    this.age = age
    this.class = 'class-1'
    // return this  // 默认有这一行
}
var f = new Foo('zhangsan', 20)
// var f1 = new Foo('lisi', 22) // 创建多个对象

执行过程:

this变成空对象 -》 赋值

扩展

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

==》 判断一个变量是否为“数组”:变量instanceof Array

原形规则和示例

5条原型规则

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

    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn() {}
    fn.a = 100;

    console.log(obj.proto);
    console.log(arr.proto);
    console.log(fn.proto);

    console.log(fn.prototype)

    console.log(obj.proto === Object.prototype)

示例:

// 构造函数
function Foo(name, age) {
    this.name = name
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
// 创建示例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
// 测试
f.printName()
f.alertName()

扩展

this

// 测试
f.printName()
f.alertName()

this 就是指向f

循环对象自身的属性

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

原型链

// .......
//测试
f.toString()  // 要去 f._proto_._proto_中查找

instanceof

用于判断引用类型属于哪个构造函数的方法

  • f instanceof Foo的判断逻辑是:
  • f的proto一层一层往上,能否对应到Foo.prototype
  • 再试着判断f instanceof Object

问题与解答

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

var arr = []
arr instanceof Array // true
typeof arr // object, typeof 是无法判断是否是数组的

写一个原型链继承的例子

// 动物
function Animal() {
    this.eat = function() {
        console.log('animal eat')
    }
}
// 狗
function Dog() {
    this.bark = function() {
        console.log('dog bark')
    }
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()
// 接下的代码演示时,会推荐更加贴近实战的原型继承示例!

描述new一个对象的过程

  • 创建一个新对象
  • this指向这个新对象
  • 执行代码,即对this赋值
  • 返回this

    function Elem(id) {
    this.elem = document.getElementById(id)
    }

    Elem.prototype.html = function(val) {

    var elem = this.elem
    if (val) {
        elem.innerHTML = val
        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’)
    // console.log(div1.html())
    // div1.html(‘

    hello imooc

    ‘)
    // div1.on(‘click’, function() {
    // alert(‘clicked’)
    // })

    div1.html(‘

    hello imooc

    ‘).on(‘click’, function() {

    alert('clicked')
    

    }).html(‘

    javascript

    ‘)

zepto(或其他框架)源码中如何使用原型链

  • 阅读源码是高效提高技能的方式
  • 但不能“埋头苦钻”有技巧在其中
  • 慕课网搜索“zepto设计和源码分析”

作用域和闭包

题目

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

知识点

  • 执行上下文
  • this
  • 作用域
  • 作用域链
  • 闭包

执行上下文

console.log(a)  // undefined
var a = 100

fn('zhangsan') // 'zhangsan' 20
function fn(name) {
    age = 20
    console.log(name, age)
    var age
}
  • 范围:一段script或者一个函数
  • 全局:变量定义、函数声明 一段script
  • 函数:变量定义、函数声明、this、arguments 函数

PS:注意“函数声明”和“函数表达式”的区别

this

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

    var a = {

    name: 'A',
    fn: function() {
        console.log(this.name)
    }
    

    }
    a.fn() // this === a
    a.fn.call({name: ‘B’}) // this === {name: ‘B’}
    var fn1 = a.fn
    fn1() // this === window

  • 作为构造函数执行

  • 作为对象属性执行
  • 作为普通函数执行
  • call apply bind

作用域

// 无块级作用域
if (true) {
    var name = 'zhangsan'
}
console.log(name)

// 函数和全局作用域
var a = 100
function fn() {
    var a = 200
    console.log('fn', a)
}
console.log('global', a)
fn()
  • 没有块级作用域
  • 只有函数和全局作用域

作用域链

var a = 100
function fn() {
    var b = 200

    // 当前作用域没有定义的变量,即“自由变量”
    console.log(a)

    console.log(b)
}
fn()

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a) // a是自由变量
        console.log(b) // b是自由变量
        console.log(c)
    }
    F2()
}
F1()

闭包

function F1() {
    var a = 100

    // 返回一个函数(函数作为返回值)
    return function() { // 函数定义时的*父*作用域
        console.log(a) // 自由变量,父作用域寻找
    }
}
// f1 得到一个函数
var f1 = F1()
var a = 200
f1()

闭包的使用场景

  • 函数作为返回值(上一个demo)
  • 函数作为参数传递(自己思考)

作为参数

function F1() {
    var a = 100
    return function() {
        console.log(a) // 自由变量,父作用域寻找
    }
}
var f1 = F1()

function F2(fn) {
    var a = 200
    fn()
}
F2(f1)

说一下对变量提升的理解

  • 变量定义
  • 函数声明(注意和函数表达式的区别)

说明this几种不同的使用场景

  • 作为构造函数执行
  • 作为对象属性执行
  • 作为普通函数执行
  • call apply bind

创建10个a标签 点击的时候弹出来对应的序号

// 这是一个错误的写法!!!
var i, a
for(i =0; i<10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function(e) {
        e.preventDefault()
        alert(i) // 自由变量,要去父作用域获取值
    })
    document.body.appendChild(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 isFirstLoad() {
    var _list = []
    return function(id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id) 
            return true
        }
    }
}

//使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true

异步和单线程

题目

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

知识点

  • 什么是异步(对比同步)
  • 前端使用异步的场景
  • 异步和单线程

什么是异步

console.log(100)
setTimeout(function() {
    console.log(200)
}, 1000)
console.log(300)

对比同步

console.log(100)
alert(200) // 1秒钟之后点击确认
console.log(300)

何时需要异步

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

前端使用异步的场景
.

  • 定时任务:setTimeout,setInterval
  • 网络请求:ajax请求,动态img标签加载
  • 事件绑定

ajax请求代码示例

console.log('start')
$.get('./data1.json', function(data1) {
    console.log(data1)
})
console.log('end')

img标签加载示例

console.log('start')
var img = document.createElement('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')

异步和单线程

console.log(100)
setTimeout(function() {
    console.log(200)
}, 1000)
console.log(300)
  • 执行第一行,打印100
  • 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事)
  • 执行最后一行,打印300
  • 待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行。
  • 发现暂存起来的setTimeout中的函数无需等待时间,就立即来过来执行

同步和异步的区别是什么

  • 同步会阻塞代码执行,而异步不会
  • alert是同步,setTimeout是异步

一个关于setTimeout的笔试题

console.log(1)
setTimeout(function() {
    console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
    console.log(4)
}, 1000)
console.log(5)

其他知识

题目

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

知识点

  • 日期
  • Math
  • 数组API
  • 对象API

日期

Date.now()    // 获取当前时间毫秒数
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.getSeconds() // 秒(0 -59)

Math

  • 获取随机数Math.random()

数组API

  • forEach 遍历所有元素
  • every 判断所有元素是否都符合条件
  • some 判断是否有至少一个元素符合条件
  • sort 排序
  • map 对元素重新组装,生成新数组
  • filter 过滤符合条件的元素

forEach

var arr = [1,2,3]
arr.forEach(function(item, index) {
    // 遍历数组的所有元素
    console.log(index, item)
})

every

var arr = [1,2,3]
var result = arr.every(function (item, index) {
    // 用来判断所有的数组元素,都满足一个条件
    if (item < 4) {
        return true
    }
})
console.log(result)

some

var arr = [1,2,3]
var result = arr.some(function (item, index) {
    // 用来判断所有的数组元素,只要有一个满足条件即可
    if (item < 2) {
        return true
    }
})
console.log(result)

sort

var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a,b) {
    // 从小到大排序
    return a - b

    // 从大到小排序
    // return b - a
})
console.log(arr2)

map

var arr = [1,2,3,4]
var arr2 = arr.map(function(item, index) {
    // 将元素重新组装,并返回
    return '<b>' + item + '</b>'
})
console.log(arr2)

filter

var arr = [1,2,3]
var arr2 = arr.filter(function(item, index) {
    // 通过某一个条件过滤数组
    if (item >= 2) {
        return true
    }
})
console.log(arr2)

对象API

var obj = {
    x: 100,
    y: 200,
    z: 300
}
var key
for(key in obj) {
    // 注意这里的hasOwnProperty,再讲原型链时候讲过了
    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
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)

获取随机数,要求是长度一致的字符串格式

var random = Math.random()
var random = random + '0000000000' // 后面加上10个零
var 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) {
            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, value) {
    console.log(key, value)
})

JS-Web-API

  • W3C标准中关于JS的规定有:
  • DOM操作
  • BOM操作
  • 事件绑定
  • ajax请求(包括http协议)
  • 存储

  • 页面弹框是window.alert(123),浏览器需要做:

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

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

  • 全面考虑,JS内置的全局函数和对象有哪些?
  • 之前讲过的Object Array Boolean String Math JSON等
  • 刚刚提到的window document
  • 接下来还有继续讲到的所有未定义的全局变量,如navigator.userAgent

总结

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

DOM操作

Document Object Model

题目

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

DOM的本质

<?xml version="1.0" encoding="UTF-8"?>
<note>
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
    <other>
        <a></a>
        <b></b>
    </other>
</note>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        <p>this is p</p>
    </div>
</body>
</html>

DOM节点操作

DOM可以理解为:

浏览器吧拿到的html代码,结构化一个浏览器能识别并且js可操作的一个模型而已。

  • 获取DOM节点
  • prototype
  • Attribute

获取DOM节点

var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName
('div') // 集合
console.log(divList.length)
console.log(divList[0])

var containerList = document.getElementsByClassName
('.container') // 集合
var pList = document.querySelectorAll('p') // 集合

property

var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width = '100px' // 修改样式
console.log(p.className) // 获取class
p.className = 'p1' // 修改class

// 获取nodeName和nodeType
console.log(p.nodeName)
console.log(p.nodeType)

Attribute

var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-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 p1'
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节点的Attribute和property有何区别

DOM是哪种基本的数据结构

DOM操作的常用API有哪些

  • 获取DOM节点,以及节点的property和Attribute
  • 获取父节点,获取子节点
  • 新增节点,删除节点

DOM节点的Attribute和property有何区别

  • property只是一个JS对象的属性的修改
  • Attribute是对html标签属性的修改

BOM操作

Browser Object Model

题目

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

navigator & screen

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

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

location & history

// location 
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.host) // 域名
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)

// history
history.back()
history.forward()

如何检测浏览器的类型

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

事件

题目

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

通用事件绑定

var btn = document.getElementById('bnt1')
btn.addEventListener('click', 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>
</body>

var p1 = document.getElementById('p1')
var body = document.body
function bindEvent(elem, type, fn){
    elem.addEventListener(type, fn)
}

bindEvent(p1, 'click', function(e) {
    e.stopPropatation() // 阻止默认行为
    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.matchs(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,不依赖第三方库
  • 跨域的几种实现方式

imooc.com m.imooc.com coding.imooc.com

知识点

  • XMLHttpRequest
  • 状态码说明
  • 跨域

XMLHttpRequest

var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
    // 这里的函数异步执行,可参考之前JS基础中的异步模块
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
            alert(xhr.responseText)
        }
    }
}
xhr.send(null)

IE兼容性问题

  • IE低版本使用ActiveXObject,和W3C标准不一样
  • IE低版本使用量以非常少,很多网站都早已不支持
  • 建议对IE低版本的兼容性:了解即可,无需深究

状态码说明

readyState

  • 0 -(未初始化)还没有调用send()方法
  • 1 -(载入)已调用send()方法,正在发送请求
  • 2 -(载入完成)send()方法执行完成,已经接收到全部响应内容
  • 3 -(交互)正在解析响应内容
  • 4 -(完成)响应内容解析完成,可以在客户端调用了

status

  • 2xx - 表示成功处理请求。如200
  • 3xx - 需要重定向,浏览器直接跳转
  • 4xx - 客户端请求错误,如404
  • 5xx - 服务器端错误

跨域

  • 什么是跨域
  • JSONP
  • 服务器端设置http header

什么是跨域

  • 浏览器有同源策略,不允许ajax访问其他域接口
  • 你的网站
  • 别的网站
  • 跨域条件:协议、域名、端口,有一个不同就算跨域

可以跨域的三个标签

  • 但是有三个标签允许跨域加载资源
  • img src=xxx
  • link href=xxxx
  • script src=xxx

三个标签的场景

  • img用于打点统计,统计网站可能是其他域
  • link、script可以使用CDN,CDN的也是其他域
  • script可以用于JSONP。

跨域注意事项

  • 所有的跨域请求都必须经过信息提供方允许
  • 如果未经允许即可获取,那是浏览器同源策略出现漏洞

JSONP实现原理


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

  • 另外一个解决跨域的简洁方法,需要服务器端来做
  • 但是作为交互方,我们必须知道这个方法
  • 是将来解决跨域问题的一个趋势

// 注意:不同后端语言的写法可能不一样

// 第二个参数填写允许跨域的域名城,不建议直接写“*”
response.setHeader("Access-Control-Allow-Origin", 
"http://a.com, http://b.com");
response.setHeader("Access-Control-Allow-Headers", "X-
Requested-With");
response.setHeader("Access-Control-Allow-Methods",
 "PUT,POST,GET,DELETE,OPTIONS");

// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials",
 "true");

存储

题目

  • 请描述一下cookie,sessionStorage和localStorage的区别?

cookie

  • 本身用于客户端和服务器端通信
  • 但是它有本地存储的功能,于是就被“借用”
  • 使用document.cookie=…获取和修改即可

cookie用于存储的缺点

  • 存储量太小,只有4KB
  • 所有http请求都带着,会影响获取资源的效率
  • API简单,需要封装才能用 document.cookie = …

locationStorage和sessionStorage

  • HTML5专门为存储而设计,最大容量5M
  • API简答易用:
  • localStorage.setItem(key,value); localStorage.getItem(key);

  • iOS safari隐藏模式下
  • localStorage.getItem会报错
  • 建议统一使用try-catch封装

cookie sessionStorage localStorage的区别

  • 容量
  • 是否会携带到ajax中
  • API易用性

运行环境

关于开发环境

  • 面试官想通过开发环境了解面试者的经验
  • 开发环境,最能体现工作产出的效率
  • 会以聊天的形式为主,而不是出具体的问题

  • IDE

  • git
  • JS模块化
  • 打包工具
  • 上线回滚的流程

常用Git命令

  • git add .
  • git checkout xxx
  • git commit -m “xxx”
  • git push origin master
  • git pull origin master
  • 分支
  • git branch
  • git checkout -b xxx/ git checkout xxx
  • git merge xxx

模块化

  • 这本身就是一个面试的问题

知识点

  • 不使用模块化的情况
  • 使用模块化
  • AMD
  • CommonJS

不使用模块化

  • util.js getFormatDate函数
  • a-util.js aGetFormatDate函数 使用getFormatDate
  • a.js aGetFormatDate

代码

// util.js
function getFormatDate(date, type) {
    // type === 1 返回 2017-06-15
    // type === 2 返回 2017年6月15日 格式
    // ---
}

// a-util.js
function aGetFormatDate(date) {
    // 要求返回 2017年6月15日 格式
    return getFormatDate(date, 2)
}

// a.js
var dt = new Date()
console.log(aGetFormatDate(dt))

使用

<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<!-- 1. 这些代码中的函数必须是全局变量,才能暴露给使用方。全局
变量污染 -->
<!-- 2. a.js知道要引用 a-util.js,但是他知道还需要依赖
util.js吗? -->

使用模块化

// util.js
export {
    getFormatDate: function (date, type) {
        // type === 1 返回 2017-06-15
        // type === 2 返回 2017年6月15日 格式
    }
}

// a-util.js
var getFormatDate = require('util.js')
export {
    aGetFormatDate: function (date) {
        // 要求返回 2017年6月15日 格式
        return getFormatDate(date, 2)
    }
}

// a.js
var aGetFormatDate = require('a-util.js')
var dt = new Date()
console.log(aGetFormatDate(dt))

// 直接`<script src="a.js"></script>`,其他的根据依赖关系自动引用
// 那两个函数,没必要做成全局变量,不会带来污染和覆盖

AMD

  • require.js requirejs.org/
  • 全局define函数
  • 全局require函数
  • 依赖JS会自动、异步加载

使用require.js

// util.js
define(function() {
    return {
        getFormatDate: function(date, type) {
            if (type === 1) {
                return '2017-06-15'
            }
            if (type === 2) {
                return '2017年6月15日'
            }
        }
    }
})

// a-util.js
define(['./util.js'], function(util) {
    return {
        aGetFormatDate: function(date) {
            return util.getFormatDate(date, 2)
        }
    }
})

// a.js
define(['./a-util.js'], function(aUtil){
    return {
        printDate: function (date) {
            console.log(aUtil.aGetFormatDate(date))
        }
    }
})

// main.js
require(['./a.js'], function(a) {
    var date = new Date()
    a.printDate(date)
})

引用main.js

<script src="/require.min.js" data-main="./main.js"></
script>

CommonJS

  • nodejs模块化规范,现在被大量用前端,原因:
  • 前端开发依赖的插件和库,都可以用npm中获取
  • 构建工具的高度自动化,使得使用npm的成本非常低
  • CommonJS不会异步加载JS,而是同步一次性加载出来

使用CommonJS

// util.js 
module.exports = {
    getFormatDate: function (date, type) {
        if(type === 1) {
            return '2017-06-15'
        }
        if (type === 2) {
            return '2017年6月15日'
        }
    }
}

// a-util.js
var util = require('util.js')
module.exports = {
    aGetFormatDate: function (date) {
        return util.getFormatDate(date, 2)
    }
}

使用

  • 需要构建工具支持
  • 一般和npm一起使用

AMD和CommonJS的使用场景

  • 需要异步加载JS,使用AMD
  • 使用了npm之后建议使用CommonJS

上线和回滚

知识点

  • 上线和回滚的基本流程
  • linux基本命令

    上线回滚流程介绍

  • 是非常重要的开发环节

  • 各个公司的具体流程不同
  • 由专门的工具后者系统完成,我们无需关心细节
  • 如果你没有参与过,面试时也要说出要点
  • 只讲要点,具体实现无法讲解

上线流程要点

  • 将测试完成的代码提交到git版本库的master分支
  • 将当前服务器的代码全部打包并记录版本号,备份
  • 将master分支的代码提交覆盖到线上服务器,生成新版本号

回滚流程要点

  • 将当前服务器的代码打包并记录版本号,备份
  • 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号

linux基本命令

  • 服务器使用linux居多,server版,只有命令行
  • 测试环境要匹配线上环境,因此也是linux
  • 经常需要登录测试机来自己配置、获取数据

mkdir ls ll cd pwd cp(拷贝) mv(移动)

rm(删除) vi cat、head、tail(看) grep(搜索)

运行环境

  • 浏览器就可以通过访问链接来得到页面的内容
  • 通过绘制和渲染,显示出页面的最终的样子
  • 整个过程中,我们需要考虑什么问题?

知识点

  • 页面加载过程
  • 性能优化
  • 安全性

题目

  • 从输入url得到html的详细过程
  • window.onload和DOMContentLoaded的区别

知识点

  • 加载资源的形式
  • 加载一个资源的过程
  • 浏览器渲染页面的过程

加载资源的形式

  • 输入url(或跳转页面)加载html
  • http://coding.m.imooc.com
  • 加载html中的静态资源
  • script src=”/static/js/jquery.js”

加载一个资源的过程

  • 浏览器根据DNS服务器得到域名的IP地址
  • 向这个IP的机器发送http请求
  • 服务器收到、处理并返回http请求
  • 浏览器得到返回内容

浏览器渲染页面的过程

  • 根据HTML结构生成DOM Tree
  • 根据CSS生成CSSOM
  • 将DOM和CSSOM整合形成RenderTree
  • 根据RenderTree开始渲染和展示
  • 遇到script时,会执行并阻塞渲染(因为js有权利改变DOM结构)

window.onload和DOMContentLoaded

window.addEventListener('load', function() {
    // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function(){
    // DOM渲染完即可执行,此时图片、视频还可能没有加载完
})

性能优化

原则

  • 多使用内存、缓存或者其他方法
  • 减少CPU计算、较少网络

从哪里入手

  • 加载页面和静态资源
  • 页面渲染

加载资源优化

  • 静态资源的压缩合并
  • 静态资源缓存
  • 使用CDN让资源加载更快
  • 使用SSR后端渲染,数据直接输出到HTML中

渲染优化

  • CSS放前面,JS放后面
  • 懒加载(图片懒加载、下拉加载更多)
  • 减少DOM查询,对DOM查询做缓存
  • 减少DOM操作,多个操作尽量合并在一起执行
  • 事件节流
  • 尽早执行操作(如DOMContentLoaded)

资源合并

<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

<script src="abc.js"></script>

缓存

  • 通过连接名称控制缓存
  • script src=”abc_1.js”
  • 只有内容改变的时候,链接名称才会改变
  • script src=”abc_2.js”

CDN

使用SSR后端渲染

  • 现在Vue React 提出了这样的概念
  • 其实jsp php asp 都属于后端渲染

懒加载

<img id="img1" src="preview.png" data-
realsrc="abc.png">

<script type="text/javascript">
    var img1 = document.getElementById('img1')
    img1.src = img1.getAttribute('data-realsrc')
</script>

缓存DOM查询

// 未缓存DOM查询
var i 
for(i = 0; i < document.getElementsByTagName
('p').length; i++) {
    // todo
}

// 缓存了DOM查询
var pList = document.getElementsByTagName('p')
var i 
for(i = 0; i < pList.length; i++) {
    // todo
}

合并DOM插入

var listNode = document.getElementById('list')

// 要插入10个li标签
var frag = document.createDocumentFragment();
var x, li;
for(x = 0; x < 10; x++) {
    li = document.createElement('li');
    li.innerHTML = "List item " + x;
    frag.appendChild(li);
}

listNode.appendChild(frag);

事件节流

var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function() {
    if(timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function() {
        // 触发 change事件
    }, 100)
})

尽早操作

window.addEventListener('load', function() {
    // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function
() {
    // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})

安全性

  • 综合性的问题:场景的前端安全问题有哪些

知识点

  • XSS跨站请求攻击
  • XSRF跨站请求伪造

XSS

  • 在新浪博客写一篇文章,同时偷偷插入一段script
  • 攻击代码中,获取cookie,发送自己的服务器
  • 发布博客,有人查看博客内容
  • 会把查看者的cookie发送到攻击者的服务器

预防

  • 前端替换关键字,例如替换 < 为 < > 为 >
  • 后端替换

XSRF

  • 你已登录一个购物网站,正在浏览商品
  • 该网站付费接口是xxx.com/pay?id=100但是没有任何验证
  • 然后你收到一封邮件,隐藏着img src = xxx.com?id=100
  • 你查看邮件的时候,就已经悄悄的付费购买了

解决

  • 增加验证验证流程,如输入指纹、密码、短信验证码

技巧

简历

  • 简洁明了,重点突出项目经历和解决方案
  • 把个人博客放在简历中,并且定期维护更新博客
  • 把个人的开源项目放在简历中,并维护开源项目
  • 简历千万不要造假,要保持能力和经历上的真实性

面试过程中

  • 如何看待加班?加班就像借钱,救急不救穷
  • 千万不可挑战面试官,不要反考面试官
  • 学会给面试官惊喜,但不要太多
  • 遇到不会回答的问题,说出你知道的也可以
  • 谈谈你的缺点 — 说一下你最近正在学什么就可以了(你不会什么)