一、js基础语法
1.1变量类型和计算
1.1.1变量类型
(1)值类型vs引用类型
值类型:
指向值本身
undefine boolean string number symbol(表示独一无二的值,最大的用法是用来定义对象的唯一属性名)
引用类型:
变量指向值的存储地址
数组 对象 null(指向空地址) 函数
(2) typeof运算符
作用:识别出所有的值类型、函数、判断是否是引用类型(不可再细分)
类型:undefine/string/boolean/number/symbol/object/function
(3)深拷贝
浅拷贝:引用类型只是拷贝值地址
<script>
const obj = {
age:20,
name:'hhh',
address:{
city:'beijing',
},
arr:['a','b','c']
}
function deepClone(obj={}){
if(typeof obj != 'object' || obj==null){
return obj
}
let result
if (obj instanceof Array){
result = []
}else{
result = {}
}
for(key in obj){
if(obj.hasOwnPropety){
result[key] = deepClone[obj[key]]
}
}
}
</script>
1.1.2变量计算
(1)类型转换
字符串拼接
== (尽量通过某种方式转换后变成相等)
何时使用===何时使用==
答:除了==null使用==,其他时候都用===
(2) if语句和逻辑运算
falsely变量:
!!0 ===false
!!NAN ===false
!!''===false
!!null === false
!!undefine ===false
!!false ===false
truely变量:
除了false变量之外的都是truely变量
if语句判断的就是truely变量和falsely变量
1.2原型和原型链
1.2.1class和继承
class Person{
constructor(name,age){
this.name = name
}
eat (){
console.log (
`${this.name} eating something`
)
}
}
class Student extends Person{
constructor(name,number){
super(name)
this.number = number
}
sayHi (){
console.log(`姓名:${this.name} 学号:${this.number}`)
}
}
xialuo = new Student('xialuo','0002')
1.2.2类型判断instanceof
判断对象是否在原型链上,eg p instanceof Object
1.2.3原型和原型链
原型关系
每个class都有显示原型 prottotype,
每个实例对象都有隐式原型__proto__
实例的__proto指向class的prottotype
基于原型的的执行规则
获取属性xialuo.name或者执行方法xialuo.sayHi()时,
先从自身属性和方法查找,
找不到则自动去__proto__找
原型链
xiaoluo.__proto__ === Student.prottotype
Student.prottotype.__proto__ === Person.prottotype
ps:
class是ES6语法规范,由ECMA委员会发布
ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现
以上实现方式都是V8引擎的实现方式,也是主流
如何判断一个变量是数组(原型链) 答:p instanceof Array
手写一个jQuery,考虑插件和可扩展性
考虑插件则都在jquery原型里面添加
复写机制
使用继承
class Jquery{
constructor(selector){
const result = document.querySelectorAll(selector)
const length = result.length
for(let i = 0 ; i < length ;i++){
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index){
return this[index]
}
each(fn){
for(let i = 0; i < this.length ; i++){
const elem = this[i]
fn(elem)
}
}
on(type,fn){
return this.each(elem =>{
elem.addEventListener(type,fn,false)
})
}
}
class的本质,怎么理解 答:原型和原型链图示
1.3作用域和闭包
作用域和自由变量
作用域
全局
块级(ES6新增)
函数
自由变量
一个变量在当前作用域没有定义但被使用了
向上级一层一层去找,直到找到为止
如果全局作用域都没有找到,则报错XXX is not undefined
闭包
作用域应用的特殊情况,有两种表现:
ps:所有的自由变量的查找是在函数定义的或者向上一级作用域查找 ,不是在执行的地方查找。。。
1.函数作为参数传递
// 函数作为参数
function print (fn){
let a = 200
fn ()
}
let a = 100
function fn (){
console.log(a)
}
print (fn) //打印100,因为fn在全局作用域中定义,打印的a在fn中没找到,会向上一级找即全局a
2.函数作为返回值被返回
// 作为函数返回
function create(){
let a = 100
return function (){
console.log(a)
}
}
let fn = create()
let a = 200
fn() //打印100 因为fn等于create(),s所以在create定义中找a的值,即100
this
作为普通函数
使用call bind apply
作为对象方法被调用
在class方法中调用
在箭头函数中调用 --箭头函数的this取值时取上一级作用域的取值
ps:this取什么值是在函数执行时确定的
题目:this的不同应用场景如何取值
答案:
1.被当作普通函数调用
2.使用call\bind\apply
3.作为对象方法被调用
4.在class方法中调用
5.在箭头函数中调用 --箭头函数的this取值时取上一级作用域的取值
题目:手写bind函数
Function.prototype.bind1 = function (){
//把arguments赋值给Array.prototype.slice中国this
const args = Array.prototype.slice.call(arguments)
// 弹出第一项作为this
const t = args.shift()
// fn1.bind中的fn1,因为谁调用执行就指向谁
const self = this
// 返回一个函数
return function (){
return self.apply(t,args)
}
}
function fn1 (a,b,c){
console.log('this',this)
console.log(a,b,c)
return 'this is fn'
}
const fn2 = fn1.bind({x:10},10,20,30)
const res = fn2()
console.log(res)
题目:闭包的应用
1.隐藏数据
2.做一个简单的cache工具
// 闭包隐藏数据只提供API
function createCache (){
const data = {} //闭包的数据,不被外界访问
return {
set:function (key,val){
data[key] = val
},
get:function (key){
return data[key]
}
}
}
const c = createCache()
c.set('a',100)
c.get(c)
题目:创建10个a,点击弹出序号
let a;
for(let i = 0; i < 10 ; i++){
a = document.createElement('a');
a.innerHTML = i ;
a.addEventListener('click',function(e){
//1-9,因为在for中定义变成块级作用域
e.preventDefault();
alert(i);
})
document.body.appendChild(a);
}
let i,a;
for(i = 0; i < 10 ; i++){
a = document.createElement('a');
a.innerHTML = i ;
a.addEventListener('click',function(e){
//每次点击i都是,因为在触发点击时,i会在全局作用域中找,全局的i的值会变成10
e.preventDefault();
alert(i);
})
document.body.appendChild(a);
}
1.4异步和单线程
单线程和异步
js是单线程语言,只能同时做一件事
浏览器和nodejs已经支持js启动进程,如web worker
js和DOM渲染共用一个线程,因为js可改变DOM结构
所以:
遇到等待(网络请求和定时任务)不能卡住
异步和同步
js是单线程语言
异步(callback回调函数)不会阻塞代码执行,
同步会阻塞代码执行
应用场景
网络请求,如ajax请求图片加载
定时任务,如setTimeout
callback hell 和promise
promise把callback的形式变成管道形式
题目:同步和异步的区别?
js是单线程语言
异步(callback回调函数)不会阻塞代码执行,
同步会阻塞代码执行
题目:手写promise加载一张图片
function loadImg(src){
return new Promise(
(resolve,reject) => {
const img = document.createElement('img')
img.onload = () =>{
resolve(img)
}
img.onerror = () =>{
reject(new Error('图片加载失败'))
}
img.src= src
}
)
}
const url1 = 'https://img.mukewang.com/5fbb1c720001b5a317920764.jpg'
const url2 = 'https://img3.mukewang.com/5fb357ce000182d405400200.jpg'
loadImg(url1).then(img =>{
//箭头函数前的img接收的是resolve中的img对象
console.log(img.width)
return img //普通对象
}).then( img =>{
//箭头函数前的img是上一个then返回的img
console.log(img.height)
return loadImg(url2) //promise实例对象即resolve中的img
}).then(img2 =>{
console.log(img2.width)
}).catch(e => console.log(e))
题目:前端异步使用场景
网络请求,如ajax请求图片加载
定时任务,如setTimeout
二、js-web-api
1、js基础和js-web-api的区别
1.js是=基础是规定语法(ECMA262标准)
2.JS web API 是网络操作的API(W3c标准)
3.前者是后者的基础,两者结合才能真正实际应用
2、DOM
2.1DOM的本质
数结构
2.2DOM节点操作
获取dom节点
arribute
DOM节点的属性,
propety
修改对象属性,不会体现在HTML结构中
2.3DOM结构操作
新增/插入节点
获取子元素列表,获取父元素
删除子节点
const div1 = document.getElementById('idv1')
// 新建节点
const p1 = document.createElement('p')
p1.innerHTML = 'this is p'
// 插入节点
div1.appendChild(p1)
// 移动节点
div2 = document.getElementById('div2')
const pp = document.getElementById('pp')
div2.appendChild(pp)
//获取父元素
console.log(pp.parentNode)
//h获取子元素列表
const list = Array.prototype.slice.call(div1.childNodes).filter(child =>{
if(child.nodeType===1){
return true
}else{
return false
}
})
console.log(list)
// 删除子元素
div1.removeChild(list[0])
2.4DOM性能
dom操作是非常昂贵的,避免频繁的DOM操作
对DOM查询进行缓存
将频繁操作改为一次性操作
题目:DOM是什么数据结构
树(DOM树)
题目:DOM操作的常见API
DOM节点操作:获取dom节点
DOM结构操作:新增/插入节点、获取子元素列表,获取父元素、删除子节点
attr和propety操作
题目:attr和propety的区别
attr:修改HTML属性,会改变HTML结构
propety:修改对象属性,不会体现在HTML结构中
两者都有可能引起DOM重新渲染
尽量使用propety,修改DOM结构比较消耗性能
题目:一次性插入DOM节点考虑性能
dom查询缓存、将频繁操作改为一次性插入
3、BOM(browser Object Model)
navigator
screen
location
history
// navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('chrome')
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
// location
url = 'https://class.imooc.com/class/chapter/115.html?a=100&b=100#author'
console.log(location.href) //https://class.imooc.com/class/chapter/115.html?a=100&b=100#author
console.log(location.protocol) // http: https:
console.log(location.host) //class.imooc.com
console.log(location.pathname) // /class/chapter/115.html
console.log(location.search) //a=100&b=100
console.log(location.hash) //#author
// history
history.back()
history.forward()
题目:如何识别浏览器类型
题目:分析拆解url
4、事件绑定
事件绑定
事件冒泡
事件代理
优点:代码简洁,减少浏览器内存占用,但是不要滥用
题目:绑定一个事件通用的事件监听函数
function bindEvent (elem,type,selector,fn){
if (fn == null){
fn = selector
selector = null
}
elem.addEventListener (type, function(e) {
let target
if (selector) {
// 需要代理
target = e.target
if(target.matches(selector)){
fn.call(target,e)
}
}else{
//不需要代理
// fn(e)
fn.call(target,e)
}
})
}
const btn1 = document.getElementById('btn1')
bindEvent(btn1,'click',function(e){
e.preventDefault()
alert(btn1.innerHTML)
})
const div1 = document.getElementById('div1')
bindEvent(div1,'click','a',function(e){
e.preventDefault()
alert(this.innerHTML)
})
题目:描述事件冒泡流程
基于DOM树形结构
事件顺着触发元素往上冒泡
应用场景:d代理
题目:无限下拉的图片列表,如何监听每个图片的点击
使用事件代理
5、ajax
XMLHttpRequest
// const xhr = new XMLHttpRequest()
// xhr.open('get','api',true)
// xhr.onreadystatechange = function(){
// if(xhr.readyState === 4){
// if(xhr.status === 200){
// console.log(
// JSON.parse(xhr.responseText)
// )
// }else{
// console.log('其他情况')
// }
// }
// }
// xhr.send(null)
const xhr = new XMLHttpRequest()
xhr.open('POST','login',true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log(
JSON.parse(xhr.responseText)
)
}else{
console.log('其他情况')
}
}
}
postData = {
userName:'zhangsan',
password:'123456'
}
xhr.send(JSON.stringify(postData))
状态码(readyState )
0:未初始化,即还没发送send()方法
1:已载入,已调用send方法,正在发送请求
2.载入完成,seng方法执行完成,已经接受到全部相应内容
3.交互,正在解析相应内容
4.完成,相应内容解析完成,可以在客户端调用
xhr.status
2XX:表示请求成功,如200
3XX,需要重定向,浏览器直接跳转,如301 302 304
4XX:客户端错误 如404(页面找不到) 403(无登录时五权限)
5XX:服务端错误
跨域,同源策略,跨域解决方案
是什么同源策略
axjax请求时,浏览器要求当前网页和server必须同源(安全)
协议、域名、端口必须一致就是同源
什么是跨域:
图片 /css/js都可以跨域即 link/img/script标签可以跨域
<img/>可以=用与统计打点,可使用第三方统计服务
<link><script>可以使用CDN,CDN一般都是外域
<script>可以实现jsonp
跨域:
所有的跨域都是经过server端允许和配合的
未经过guoserver端实现跨域,说明浏览器有漏洞,威胁信号
jsonp
<script>标签可以实现跨域
服务端可以任意动态的拼接数据返回
所以<script>就可以获取跨域的数据,只要服务端愿意返回
<script>
var script=document.createElement("script")
script.src = "url+ '&callback=' + funcName"
document.body.appendChild(script)
window[funcName] = function (data) {
callback(data)
}
</script>
CORS
服务端操作
题目:
1.手写一个简易ajax
function ajax(url){
const p = new Promise((resolve,reject) =>{
const xhr = new XMLHttpRequest()
xhr.open('get','data.json',true)
xhr.onreadystatechange = function (){
if(xhr.readyState === 4){
if(xhr.status === 200){
resolve(
JSON.parse(xhr.responseText)
)
}else if(xhr.status === 404){
reject(
new Error('404 not found')
)
}
}
}
xhr.send(null)
})
return p
}
2.跨域常见解决方法
jsonp\cors
请求发送了解axios/fetch/xmlhttprequest
6、存储
-
cookie
-
本身用于客户端和服务器端通信
-
被“借用”到本地存储
-
容量只能存储4KB
-
所有http请求都带着
-
只能通过 document.cookie="a=100;b=200"来修改,太过简陋 同一个key会覆盖,不同key会追加
-
-
sessionStorage
sessionStorage.setItem(key,value);sessionStorage.getItem(key); 只存于当前会话,浏览器关闭则清空
-
localStorage(常用)
HTML5专门为存储而设计,最大5MB
api简单易用 ,永久存储,除非代码删除或者手动删除 一般使用localStorage,很少使用sessionStorage
localStorage.setItem(key,value);localStorage.getItem(key);
注意:在IOS Safari隐藏模式下localStorage.getItem(key);会报错,建议使用try-catch
区别
容量区别
是否会携带到http请求中(cookie所有都要带)
api易用性(cookie要封装,其他两个可以直接用)
三、开发环境
git常用命令
git add .
git checkout XXX
git commit -m 'xxx'
git push origin master
git pull origin master
git branch
四、运行环境
运行环境即前端是浏览器(统称)
下载代码,渲染出页面,期间会执行js
要保证代码在浏览器:稳定高效
1、网页加载过程
1.1页面加载和渲染过程
资源的形式
html代码
媒体文件,如图片视频等
javascript css
加载形式
DNS解析 :域名 ---IP
浏览器根据IP地址向服务器发起http请求
服务器处理http请求,并返回给浏览器
渲染过程
根据HTML代码生产DOM树
根据CSS代码生成CSSDOM
将DOM树和CSSDOM整合成Render Tree
根据Render Tree渲染页面
遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续 (js和页面渲染公用一个线程,js有可能更改dom树)
直至把Render Tree渲染完成
题目:从输入url到渲染页面过程
下载资源:
渲染资源
题目:window.onload和DOMContentLoanded的区别
2、性能优化
性能优化原则:
多使用内存、缓存、或者其他方法
减少CPU计算量,减少网络加载耗时
以上方法就是空间换时间
从何入手
让加载更快
减少资源体积:压缩代码
减少访问次数:合并代码,SSR服务端渲染,缓存
使用更快的网络:CDN
让渲染更快
CSS放在head,js放在body最下面
尽早执行JS,使用DOMContentLoaded触发
懒加载,图片懒加载,上滑加载更多(可以加一张预览图)
对DOM查询进行缓存
频繁DOM操作,合并到一起插入DOM树
节流throttle防抖debounce
节流throttle
拖拽一个元素时,要随时拿到推拽元素的位置
直接用drag事件,则会频繁触发,很容易导致卡顿
节流:无论拖拽速度多块,都会每个100ms触发一次
防抖debounce
监听一个输入框,文字变化后触发change事件
直接用keyup则会频繁触发change事件
防抖:当用户输结束或者暂停时,才会触发change事件
function debounce(fn,delay=500){
let timer = null
return function (){
if(timer){
setTimeout(timer)
}
timer = setTimeout(() =>{
fn.apply(this,arguments)
timer = null
},delay)
}
}
const input1 = document.getElementById('input')
input1.addEventListener('keyup',debounce(() =>{
console.log(input1.value)
}
),600)
3、安全
常见的攻击方式
XSS攻击
或者使用xss工具