值类型:
var a = 100
var b = a
a = 200
console.log(b) // 100
引用类型:(对象、组数、函数):
var a = {age: 20}
var b = a
b.age = 21
console.log(a.age) // 21
特点:无限添加属性。设计原因:该三种类型可能较大,比较占用内存。
涉及问题:JS变量按照存储方式区分为哪些类型,并描述其特点。
答案:值类型和引用类型;引用类型赋值会导致所有对该对象引用的值都发生变化。
typeof运算符:
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log() // function
注意“null”对象原型为object。
“typeof”只能识别值类型,但无法判断引用类型,但function例外。
字符串拼接:
var a = 100 + 10 //110
var b = 100 + '10' //10010
“==”运算符:
100 == '100' //true
0 == '' //true
null == undefined //true
若以上均换成“===”,结果均为false。
涉及问题:何时使用“===”,何时使用“==”。
除了以下用法,其他情况均使用“===”:
if (obj.a == null) {
// 这里相当于 obj.a === null || obj.a === undefined , 简写形式
// 这是jQuery源码中推荐的写法
// 大概原因是null是引用类型;undefined为值类型
}
if语句:(会对判断内容强转换)
var a = true
if (a) {} //执行
var b = 100
if (b) {} //执行
var c = ''
if (c) {} //不执行
逻辑运算符:
console.log(10 && 0) // 0
console.log('' || 'abc') // 'abc'
console.log(!window.adb) // true
var a = 100
console.log(!!a) // true (!!a转换成Boolean类型)
问题:JS中有哪些内置函数 —— 数据封装对象。(都是创建特定类型的函数)
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
问题:如何理解JSON。
答案:只是JS的对象。也是一个数据格式。
// JSON两个常用内置函数
JSON.stringifg({a:10, b:20}) //转化成字符串
JSON.parse('{a:10, b:20}') //解释字符串为对象
构造函数:
function Foo(name, age) {
this.name = name
this.age = age
this.class = 'class-1'
// return this //默认有这行(因为创建对象的时候就是返回this)
}
var f = new Foo('zhangsan', 20)
// var f1 = new Foo('lisi', 21)
var a = {} 其实是 var a = new Object() 的语法糖。其他类型也是。
问题:如何判断一个变量是否为“数组”。
答案:使用”a instanceof Object”得到布尔值。
关于 引用对象 :
1、所有的引用类型都具有对象特性,即可以自由扩展属性(“null”除外)
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
2、所有的引用类型都有一个”__proto__”属性(隐式原型),属性值是一个普通的对象
console.log(obj.__proto__); // {}
console.log(arr.__proto__); // []
console.log(fn.__proto__); // [Function]
3、所有的函数都有一个”prototype”属性(显示原型),属性值也是一个普通的对象
console.log(fn.prototype); // undefined
4、“__proto__”属性值指向它的构造函数的“prototype”属性值
console.log(obj.__proto__ === Object.prototype) // true
5、当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找。
// 构造函数
function Foo(name) {
this.name = name
}
// 扩展 构造函数"FOO"的prototype的属性
Foo.prototype.alertName = function () {
alert(this.name)
}
// 创建实例
var f = new Foo('zhangsan')
f.printName = function () {
console.log(this.name)
}
// 测试
f.printName() // 执行console.log
f.alertName() // 执行alert
(针对上实例)循环对象自身的属性:
var item
for (item in f) {
// 高级浏览器已经在 for in 中屏蔽了来自原型的属性
// 但为了保证程序的健壮性,还是建议使用 .hasOwnPrototype() 判断是否属于自身属性
if (f.hasOwnPrototype(item)) {
console.log(item)
}
}
简单的原型链例子:
// 构造函数
function Foo(name) {
this.name = name
}
// 扩展构造函数"FOO"的prototype的属性
Foo.prototype.alertName = function () {
alert(this.name)
}
// 创建实例
var f = new Foo('zhangsan')
f.printName = function () {
console.log(this.name)
}
// 测试
f.printName() // 执行console.log
f.alertName() // 执行alert
f.toString() // 要去f.__proto__.__proto__ 中查找
上实例的逻辑如下图:
即,子类的__proto__指向父类的prototype。
给子类的__proto__添加新属性/方法,其他同属兄弟子类均可用该属性/方法;
给父类的prototype添加新属性/方法,其子类均可用该属性/方法。
问题:如何准确判断一个变量时数组类型。
var arr = []
arr instanceof Array // true
问题:写一个原型链继承的例子。
function Ele (id) {
this.ele = document.getElementById(id)
}
Ele.prototype.html = function (val) {
var ele = this.ele // 获取上级的ele
if (val) {
ele.innerHTML = val
}
return this // 链式操作
}
Ele.prototype.on = function (e, fn) {
var ele = this.ele
if (!!e && !!fn) {
ele.addEventListener(e, fn)
}
return this
}
var div1 = new Ele('test')
div1.html('这是修改后的内容').on('click', function () {
alert('触发单击事件')
})
问题:描述new一个对象的过程。
答案: 1、创建一个新对象
2、this指向这个新对象
3、执行代码,即对this赋值
4、返回this
问题:zepto(或其他框架)源码中如何使用原型链。
答案:
“this”:
“this”要在执行时才能确认值,定义时无法确认。
var obj = {
name: 'A',
fn: function () {
console.log(this.name)
}
}
obj.fn() // this === obj
obj.fn.call({name:'B'}) // this === {name:'B'}
var fn1 = obj.fn()
fn1() // this === window
“var”定义变量,无块级作用域:
// 无块级作用域
if (ture) {
var name = 'zhangsan'
}
console.log(name) // zhangsan
闭包:
闭包必要条件:
1、内部函数应访问其外部函数中声明的私有变量、参数或其他函数内部函数。
2、外部函数“return”该内部函数
3、定义与该外部函数同级的变量,把该外部函数赋值给它。
function fn (x) {
var a = x
var b = function() {
return a // 访问外部变量a
}
a++
return b // 需要把闭包体抛出
}
var c = fn(10)
console.log(c) // 11
// 无闭包的情况
function fn1 (x) {
var a1 = x
var b1 = a1
a1++
return b1
}
var c1 = fn1(10)
console.log(c1) // 10
问题:创建5个标签,点击弹出对应的序号。
var fragment = document.createDocumentFragment()
// 若使用'let'定义i,可以不使用闭包
for (var i = 0; i < 10; i++) {
(function (i) {
var a = document.createElement('a')
a.innerHTML = i + 1 + '
'
a.addEventListener('click', function(e) {
e.preventDefault() // 取消事件的默认行为
alert(i + 1)
})
fragment .appendChild(a)
}) (i) // 接收 i 并把 i 作为私有变量
}
document.body.appendChild(fragment)
问题:实际开发中闭包的应用。(封装变量,收敛权限)
// 闭包实际应用中主要用于 封装变量,收敛权限
function isFristLoad () {
var _list = []
return function (id) {
if (_list.indexOf(id) > -1) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var fl = isFristLoad()
fl(10) //true
fl(10) //false
fl(20) //true
JS是 异步 单线程。
前端使用异步的场景:
1、定时任务:setTimeout、setInterval
2、网络请求:Ajax请求、动态加载
3、事件绑定
问题:同步和异步的区别是什么。
答案:同步会阻塞代码执行,而异步不会。(alert和setTimeout)
问题:一个关于setTimeout的笔试题。
console.log(1)
setTimeout(() => {
console.log(2)
}, 0);
setTimeout(() => {
console.log(3)
}, 1000);
console.log(4)
答案:1 → 4 → 2 → 3 。
日期:
var dt = new Date()
dt.getTime() // 获取毫秒数,与Date.now()一样
dt.getFullYear() //年
dt.getMonth() + 1 // 月
dt.getDate() // 日
dt.getHours() // 小时
dt.getMinutes() // 分
dt.getSeconds() // 秒
涉及 问题:获取 2017-06-10格式的日期。
思路:主要涉及判断“月”和“日”小于10需要拼接“0”。
“Math”方法:
// 获取随机数 (前端多用于判断清除缓存)
Math.random() // 0到1之间的随机数
涉及问题:获取随机数,要求是长度一致的字符串格式。
思路:拼接数个“0”,再用” .slice(0, n) ”得到指定长度。
数组API:
var arr = [1, 4, 2, 3, 5]
forEach 遍历所有元素
arr.forEach((item, index) => {
console.log(index, item)
})
every 判断所有元素是否都符合条件
var result = arr.every((item, index) => {
if (item < 5) {
return ture
}
})
console.log(result) // false
some 判断是否至少一个元素符合条件
var result1 = arr.some((item, index) => {
if (item < 5) {
return true
}
})
console.log(result1) // true
sort 排序
var arr2 = arr.sort((a, b) => {
return a-b // 升序
// return b-a // 降序
})
console.log(arr2) // [1, 2, 3, 4, 5]
map 对元素重新组装,生成新数组
var arr3 = arr.map((item, indxe) => {
return '' + item + ''
})
console.log(arr3) // 每个元素都包裹了b标签
filter 过滤符合条件的元素,生成新数组
var arr4 = arr.filter((item, index) => {
if (item > 2) {
return true
}
})
console.log(arr4) // [4, 3, 5]
对象API:
只有一个for…in循环:
var obj = {a:100, b:200, c:300}
for (var key in obj) {
if (obj.hasOwnPrototype(key)) {
console.log(key, obj[key])
}
}
涉及问题:写一个能遍历对象和数组的通用forEach函数。
思路:自定义函数内先用 instanceof 判断类型,入口参数为对象+回调函数,再用forEach/for遍历。
常说的JS(浏览器执行的JS)包含两部分:
1、JS基础知识(ECMA262标准)
2、JS-Web-API(W3C标准)
数据结构:树状结构。 HTML代码本质是字符串。
DOM可以理解为:
浏览器把拿到的HTML代码结构化一个浏览器能识别并且JS可操作的一个模型。
获取DOM:
var ele1 = document.getElementById('id') // 元素
var ele2 = document.getElementsByTagName('div') // 集合
var ele3 = document.getElementsByClassName('class') // 集合
// .querySelectorAll入口参数为CSS选择器的字符串
var ele4 = document.querySelectorAll('div.note, div.alert') // 集合
property:(JS对象的属性)
ele1.style.width = '100px' // 修改样式
ele1.classNmae = 'ele1' // 设置自定义属性
attribute:(DOM元素的属性)
ele1.getAttribute('style')
ele1.setAttribute('style', 'font-size:12px')
涉及问题:DOM节点的attribute和property有何区别。
答案:property只是一个JS对象的属性修改/删除;attribute是对html标签属性的修改/删除。
新增节点:
var div1 = document.getElementById('div1')
// 添加新节点
var p1 = document.createElement('p')
p1.innerHTML = 'this is new p'
div1.appendChild(p1) // 添加新创建的元素
// 移动已有节点(原节点被删除)
var p2 = document.getElementById('p2')
div1.appendChild(p2)
获取父元素/子节点:(.parentElement 和 .childNodes)
var div2 = document.getElementById('div2')
var parent = div2.parentElement
var child = div2.childNodes // 集合(nodeList)
/// 加入子元素中含有空格/回车之类的字符,得到的集合会多若干个text的子元素
// 解决办法:使用nodeType判断子节点的类型
console.log(child[0].nodeType) // text或其他元素类型
删除节点:
div2.removeChild(child[0]) // 删除第一个子元素
问题:DOM操作的常用API有哪些。
答案: 1、获取DOM节点/属性:.getElementById(还有class和tag)、.querySelectorAll、.getAttribute
2、获取父元素/子节点:.parentElements、.childNodes
3、新增/删除节点:.appendChild()、.removeChild()
// navigator (用于判断浏览器)
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
// screen
console.log(screen.width)
console.log(screen.height)
// location
console.log(location.protocol) // "https:"
console.log(location.host) // "www.baidu.com"
console.log(location.pathname) // "/s"
console.log(location.search) // "?wd=abc&..."
console.log(location.hash) // "#mid=100" 用于定位某元素位置
// location.href 可以赋值新地址
console.log(location.href) // "https://www.baidu.com/s?wd=abc...#mid=100"
// history
history.back() // 返回
history.forward() // 前进
自定义通用事件绑定的函数:
var elem = document.getElementById('id')
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
事件冒泡:
激活
取消
激活
取消
代理:(好处:代码简洁;减少浏览器内存占用)
问题:简述事件冒泡流程。
答案:沿着DOM树往上依次触发该事件。
XMLHtmlRequest:(必须能默写)
// 创建XMLHttpRequest对象
var xhr = new XMLHttpRequest()
// 使用xhr的open方法,第三个参数的(默认值)true表示使用 异步
// 只有异步才会调用onreadystatechange指定的函数
xhr.open('GET', '/api', true)
// 使用xhr的监听事件onreadystatechange
xhr.onreadystatechange = function () {
// readyState每次改变都会调用onreadystatechange的方法
// "readyState = 4"表示内容解析完成,可在客户端调用
if (xhr.readyState = 4) {
// responseText表示响应内容,2xx:成功;3xx:重定向;4xx:请求错误;5xx:服务器错误
if (xhr.responseText = 200) {
alert(xhr.responseText)
}
}
}
// xhr发送的内容,没有可写null
xhr.send(null)
跨域:
浏览器有同源策略,不允许Ajax访问其他域接口。
跨域条件:协议、域名、端口、有一个不同算跨域。
可跨域的三个标签:<img src=””>、<link href=””>、<script src=”” >。
JSONP:
定义:利用
cookie: 本身用于客户端和服务端通信。但它有本地存储的功能,于是就被“借用”。 使用document.cookie = ...... 获取和修改。 缺点: 1、存储量太少,只有4KB; 2、所有http请求都带着,会影响获取资源的效率; 3、API简单,需要封装太能用document.cookie = ...... 。 localStorage和sessionStorage: 优点: 1、HTML5专门为存储而涉及,最大容量5M; 2、API简单易用:localStorage.getItem(key, value);localStorage.setItem(key) sessionStorage与localStorage使用方法一样,唯一区别在于重启浏览器后会删除sessionStorage。 注意事项: iOS safari的隐藏模式下使用localStorage.getItem会报错,建议统一使用try-catch封装。 问题:请描述以下cookie、sesionStorage、localStorage的区别。 答案: 1、容量:cookie只有4k;其余两者有5M。 2、是否携带到Ajax中:cookie每次都会;其余两者不会。 3、API易用性:cookie需要自己封装数据成字符串;其余两者有.getItem和.setItem。 webstorm(收费) 常用git命令: git status // 获取当前git的状态(可看到处于什么分支,哪些文件被修改) 定义:全称是Asynchronous Module Definition,即异步模块加载机制。 工具:require.js 仅有2个API:define()、require()。第一个参数为依赖文件(无则不填);第二个参数模块的实现函数。 是Nodejs模块化的规范,一般和npm一起使用,但不会异步加载JS。 与AMD类似,只有两个API:exports和require。 1、将当前服务器的代码打包并记录版本号,备份 ssh name@server //“name”为登陆账号名,“server”为服务地址,回车后会提示输入密码 加载资源的形式: 输入url:http://www.baidu.com 加载html中的静态资源:
缓存DOM查询:( 用变量存放常用DOM对象 ) 合并DOM插入:( 利用document.createDocumentFragment() ) 事件节流:( 利用setTimeout() ) XSS 解决办法:用转义字符替换关键字。 XSRF 解决办法:增加验证流程,如指纹、密码、短信码等。 5.存储
三、关于开发环境
1.IDE
sublime
VScode
atom(git hub的开源软件)
基本都依赖插件。2.Git
git add . // 把所有修改的文件上传(“.”改为文件名可单独上传,多个文件用空格隔开)
git checkout xxx // 以线上文件还原“xxx”文件
git commit -m “xxx” // 把需要上传的文件提交到本地缓存,-m “xxx”表示上传时的预留信息
git push origin master // 把缓存在本地的文件推到git服务器(忽略origin master可以操作当前分支)
git pull origin master // 把git服务器的文件拉到本地(忽略origin master可以操作当前分支)
git branch // 获取所有分支名
git checkout -b xxx // 切换到“xxx”分支(“-b”可以省略),不存在则自动创建
git merge xxx // 把”xxx”分支内容合并到本分支
git clone // 用于无项目代码的电脑下载代码3.模块化
① AMD(规范)
// 【index.html】 require.js为插件,data-main为引入模块主入口文件
// 【unit.js】
define(function () {
// 定义模块,格式:model = { function(){} }
var getFormaDate = {
function (date, type) {
if (type === 1) {
return '2018-10-03' // 实际需要处理date
} else {
return '2018年10月03日'
}
}
}
return getFormatDate // 抛出模块对象
})
// 【a-unit.js】
define(['./unit.js'], function (unit) {
var aGetFormatdate = {
function (date) {
return unit.getFormatDate(date, 1)
}
}
return aGetFormatdate
})
// 【main.js】
var dt = new Date()
require(['./aUnit.js'], function (aUnit) {
console.log(aUnit.aGetFormatdate(dt))
})
② CommonJS
// 【unit.js】
module.exprots = {
getFormatDate: function (date, type) {
if (type === 1) {
return '2018-10-03' // 实际需要处理date
} else {
return '2018年10月03日'
}
}
}
// 【a-unit.js】
var unit = require('./unit.js')
module.exprots = {
aGetFormatdate: function (date) {
return unit.getFormatDate(date, 2)
}
}
③ 回滚流程要点
2、将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号④ Linux基本命令
mkdir xxx // 创建文件夹xxx
ls/ll // 打印当前目录下的所有文件,“ll“以列形式显示
cd xxx // 定位路径到xxx
rm -rf xxx // 删除xxx文件
vi xxx // 进入vi编辑器。“i“插入模式;”esc“命令模式;”:“底行模式。 “:wq“保存并退出。
mv xxx newPath/xxx // 移动xxx文件到newPath下并命名为xxx
cat xxx // 查看xxx文件的内容
head -n 2 xxx // 查看xxx文件的头2行,“-n 2“可省略
tail -n 2 xxx // 查看xxx文件的尾2行,“-n 2“可省略
grep ‘a’ xxx // 搜索xxx文件内包含“a“的内容四、运行环境
1.页面加载
var i
// 未缓存 DOM 查询
for (i=0; i < document.getElementById('id').length; i++) {
// todo
}
// 缓存了 DOM 查询
var id = document.getElementById('id')
for (i = 0; i < id.length; i++) {
// todo
}
var listNode = document.getElementById('list')
// 存放 待插入的10个li标签
var fragment = document.createDocumentFragment()
var i, li
for (i = 0; i < 10; i++) {
li = document.createElement('li')
li.innerHTML = 'num:' + i
fragment.appendChild(li)
}
// 一次性插入所有li标签
listNode.appendChild(fragment)
var textarea = document.getElementById('text')
var timer // 存放定时器对象
textarea.addEventListener('keyup', function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 触发 change 事件
}, 100)
})
3.安全性