JS基础
JS中使用typeof能得到哪些类型?
考点:JS变量类型
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log // function
以上代码2-4行,JS能够把值类型直接区分出来
5-8行是引用类型
何时使用===和==?
考点:强制类型转换
if(obj.a == null) {
// 这里相当于 obj.a === null || obj.a === undefined
// 这是 jquery 源码中推荐的写法
}
function(a, b){
if(a == null){
...
// 看 a 的参数是否存在时也可以用==
}
}
以上两种情况都是在 a 定义之后才进行判断的,否则会报错
除了以上情况,其他情况全用===
===
不会进行强制类型转换,所以有:
// 以下两种情况在 == 得到的是true
'' === 0
==> false
null === undefined
==> false
0、NaN、''、null、undefined、false
它们的if
会被转换成false
JS中有哪些内置函数?
考点:数据封装类对象
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
JS变量按照存储方式区分为哪些类型,描述特点
// 值类型
var a = 10
var b = a
a = 11
console.log(b) // 10
// 引用类型
var obj1 = {x:100}
var obj2 = obj1
obj1.x = 200
console.log(obj2.x) // 200
值类型的存储是把数值分块存在内存中
引用类型的存储是多个变量共用一个内存块,是为了节省空间才这么做的
值类型的存储不会相互影响,而引用类型赋值是变量指针的形式赋值,不是真正的值的拷贝,所以它们的值的修改是相互干预的
引用类型有对象、数组、函数
如何理解JSON?
考点:JSON 只不过是一个 JS 对象而已
// 把对象转换成字符串
JSON.Stringify({a:10, b:20})
// 把字符串转换成对象
JSON.parse('{"a":10, "b":20}')
JSON是一种数据格式,同时也是 JS 对象,在 JS 中具有以上两个api
如何准确判断一个变量是数组类型
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()
一个更接近实际的例子
function Elem() {
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')
div1.html('<p>hello imooc</p>').on('click',funtion() {
alert('clicked')
}).html('<p>javascript</p>')
描述 new 一个对象的过程
function Foo(name, age) {
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
指向这个新对象
执行代码,即对 this
赋值
返回 this
zepto 源码中如何使用原型链
作用域和闭包
知识点
变量提升/执行上下文
// 函数声明
function fn(){...}
// 函数表达式
var fn1 = function(){...}
需要强调的是,全局函数作用域会将函数声明整个提升到代码最前面,而不会把函数表达式里面的内容提到最前,也就是说,在函数声明之前调用fn是有效的,可以得到我们想得到的结果,可是在函数表达式之前调用fn1是无效的,控制台或者浏览器都会报错,因为全局函数作用域只会这样进行变量提升:var fn1 = undefined
上图中,在代码未执行之前,先进行变量提升:将var a和function fn(name){...}提到所有代码前面,此时a并没有赋值,而是先将a用undefined占位,所以执行第一句代码时,a输出为undefined,而执行到第四句时整个fn函数已经声明,所以不会报错,正常执行,得到'zhangsan', 20。
以上总结为全局的执行上下文:变量定义、函数声明。
另外,在函数内执行上下文是这样的:变量定义、函数声明、this、argument
闭包
使用场景:
// 函数作为一个返回值传递
function F1() {
var a = 100
return function() {
console.log(a)
}
}
var f1 = F1()
var a = 200
F1() // 100
// 函数作为参数传递
function F1() {
var a = 100
return function() {
console.log(a)
}
}
var f1 = F1()
function F2(fn) {
var a = 200
fn()
}
F2(f1) // 100
说一下对变量提升的理解
用执行上下文的知识解释
说明this的几种不同的使用场景
作为构造函数执行
function Foo(name) {
this.name = name
}
var f = new Foo('zhangsan') // zhangsan
作为对象属性执行
var obj = {
name: 'A',
printName: function() {
console.log(this.name)
}
}
obj.printName() // A
作为普通函数执行
function fn() {
console.log(this)
}
fn() // window
call apply bind
function fn1(name, age) {
alert(name)
console.log(this)
}
fn1.call({x: 100}, zhangsan, 20) // Object {x: 100}
fn1.apply({x: 100}, [zhangsan, 20]) // Object {x: 100}
function fn2(name, age) {
alert(name)
console.log(this)
}.bind({y: 200})
// 以上bind代码会报错,bind只接受函数表达式
// 改为如下
var fn2 = function(name, age) {
alert(name)
console.log(this)
}.bind({y: 200})
fn2('zhangsan', 20) // Object{y: 200}
创建十个a标签,点击的时候弹出来对应的序号
var i
for(i = 0; i < 10; i++) {
(function (i) {
var a = document.creatElement('a')
a.innderHTML = i + '<br>'
a.addEventListener('click', function(e) {
e.preventDefault()
alert(i)
})
document.body.appenChild(a)
})(i)
}
如何理解作用域
要点:
自由变量
作用域链,即自由变量的查找
闭包的两个场景
JS中,没有块级作用域这个概念,举个例子
if(name) {
var name = 'zhangsan'
}
console.log(name)
//这段代码中,name输出为zhangsan,说明全局中可以访问if循环内部的变量,在java、c#等高级语言中,外部是访问不到内部的变量的,这种情况就是块级作用域
//因此,推荐以下写法,增加可读性
var name
if(name) {
name = 'zhangsan'
}
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a)// 自由变量
console.log(b)// 自由变量
console.log(c)
}
F2()
}
F1()
以上代码中执行到F2时输出a,由于在F2的作用域(F2函数内)没有找到a,会到它的父级作用域里面找,如果找不到,还会到父级作用域的父级作用域内去找,一直找到全局作用域直到找到为止,否则会报错。
像这样,一个自由变量一直不断地往它的父级作用域去找,形成一个链式结构,叫做作用域链
实际开发中闭包的应用
function isFirstLoad() {
var _list = [] // 带下划线的变量表示私有
return function (id) {
if (_list >= 0) {
ruturn false
}else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
firstLoad(20) // false
异步和单线程
知识点
什么是异步(对比同步)
// 执行第一行,打印100。
// 执行setTimeout后,传入setTimeout的函数会被暂时存起来,不会立即执行(单线程的特点,不能同时做两件事)。
// 执行最后一行,打印300。
// 待所有的程序执行完,处于空闲状态时,会立马看有没有暂存起来的任务要执行。
// 发现暂存起来的setTimeout中的函数无需等待时间,就立即拉回来执行。
console.log(100)
setTimeout(function () {
console.log(200)
})
console.log(300)
异步和同步的区别是什么?分别举一个同步和异步的例子
同步会阻塞代码执行,异步不会
alert是同步,setTimeout是异步
// 同步
console.log(100)
alert('hold on')
console.log(200)
一个关于setTimeout的笔试题
console.log(1)
setTimeout(function () {
console.log(2)
}, 0) // 进入暂存队列
console.log(3)
setTimeout(function () {
console.log(4)
}, 1000) // 进入暂存队列
console.log(5)
// 1 3 5 2 4
前端使用异步的场景有哪些
定时任务: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.creatElement('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')
其他知识点
日期
Date.now() // 获取从1970年到当前时间的毫秒数
var dt = new Date()
dt.getTime() // 获取从声明dt时刻开始到现在的毫秒数
dt.getFullYear() // 获取声明dt时刻所处的年份
dt.getMonth() // 获取声明dt时刻所处的月份(0 - 11)
dt.getDate() // 获取声明dt时刻所处的日(0 - 31)
dt.getHours() // 获取声明dt时刻所处的小时(0 - 23)
dt.getMinutes() // 获取声明dt时刻所处的分钟(0 - 59)
dt.getSeconds() // 获取声明dt时刻所处的秒(0 - 59)
随机数
Math.random()
在前端的应用是清除缓存,在一个链接中加入随机数避免浏览器从不变的链接地址中走缓存,访问不到实时更新的页面
数组API
forEach 遍历所有元素
var arr = ['a','b','c']
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.every(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,有关原型链的知识
// 判断是否是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) {
// 强制类型转换
return '0' + month
}
if (date < 10) {
// 强制类型转换
return '0' + date
}
return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
获取随机数,要求是长度一致的字符串格式
var random = Math.ramdom()
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, value) {
console.log(key, value)
})
dom本质
Document Object Model(文档对象模型)
DOM可以理解为:
浏览器把拿到的html代码,结构化成一个浏览器能识别并且js可操作的一个模型。
DOM是哪种基本的数据结构
树
DOM操作的常用API有哪些?
获取DOM节点,以及节点的property(js)和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')
var p = pList[0]
console.log(p.style.width)
p.style.width = '100px'
console.log(p.className)
p.className = 'p1'
p.getAttribute('data-name')
p.setAttribute('data-name', 'dataName')
p.getAttribute('style')
p.setAttribute('style', 'font-size:30px')
// 增加新节点
var p1 = document.creatElement('p')
p1.innerHTML = 'this is p1'
// 移动已有节点
div1.appendChild(p1)
var p2 = document.getElementById('p2')
div1.appendChild(p2)
// 获取父元素和子元素
var parent = div1.parentElement
var child = div1.childNodes
div1.removeChild(child[0])
DOM节点的attr和property有何区别
property只是一个js对象的属性的获取和修改
attribute是对html标签属性的获取和修改
事件
知识点
通用事件绑定
var btn = document.getElementById('btn1')
btn.addEventListener('click', function (event) {
console.log('clicked')
})
function binEvent(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(a, 'click', 'a', function(e) {
console.log(this.innerHTML)
})
// 不使用代理
var a = document.getElementById('a1')
bindEvent(a, 'click', function (e) {
console.log(a.innerHTML)
})
代理的好处:
代码简洁
减少浏览器内存占用
冒泡事件流程
DOM树形结构
事件冒泡:当点击一个节点时,首先触发它绑定的事件,再触发它的父节点绑定的事件,一直由它本身触发到body节点上的事件。这就叫冒泡
阻止冒泡e.stopPropatation()
冒泡的应用:代理
Ajax
知识点
XMLHttpRequest
状态码说明
跨域
- 跨域是JSONP:
例如我的网站要跨域访问某网提供的一个接口
某网给了我一个地址http://xxx.com/api.js
返回的内容格式如callback({x: 100, y: 200})(可动态生成) - 浏览器有同源策略,不允许ajax访问其他域接口
- 跨域条件:协议、域名、端口,有一个不同就算跨域
- 可以跨域的三个标签:
<img src = xxx>
场景:用于打点统计,统计网站可能是其他域
<link href = xxx>
<script src = xxx>
后两个可以使用CDN - 所有的跨域请求都必须经过提供方的允许
- 服务器设置http header
// 一段服务器设置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");
手动编写一个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本身用于客户端和服务端通信,但是它有本地存储的功能,于是被“借用了”
cookie存储量太小只有4kb、所有http请求都带着会影响获取资源的效率、api简单,需要封装才能用document.cookie = ...
localStorage和sessionStorage是HTML5专门为存储而设计,最大容量为5M(不是所有http请求都带着)、api简单易用:localStorage.setItem(key, value);localStorage.getItem(key)
localStorage和sessionStorage的区别在于如果浏览器关闭后者会清理数据而前者只有在手动清理的时候才会清理
需要注意的地方是,在ios系统下的浏览器safari,localStorage.getItem会报错,所以最好统一进行try-catch封装,避免程序崩溃
总结区别:
容量
是否会携带到ajax中
api易用性
开发环境
常用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 // 把之前在xxx分支上的更改全部合并到当前分支
- git diff // 看看较上次提交的更改,本次产生了哪些变动