第一章:JS 基础上篇(js 类型)
- 值类型
var a = 100
var b = a
a = 200
console.log(b) // 100
- 引用类型
var a = {age:100}
var b = a
a.age = 200
console.log(a.age) // 200
问题:为什么对值类型和引用类型进行修改会出现不同的结果呢?
答:值类型:存的是具体的数值,比如 a=200,那么 a 变量内存中地址存的就是 200。所以改变 a=100 ,仅仅只是影响到了 a。
引用类型:内存中存的是对象的地址,所以 a 和 b 此刻指向了同一个对象的地址。改变 a.age 就会同时影响到 b.age。
- 引用类型包括
对象、数组、函数(为什么需要用到引用呢?因为这些类型可能非常大,每次赋值都新建一个新对象,就会比较浪费空间) - typeof 运算符
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object (它也是引用类型)
typeof console.log // function
结论: typeof 能区分值类型;对于引用类型只能区分 function 其他无法区分。
-
问:JS 中有哪些内置函数 -- 数据封装类对象
答:
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
第二章:JS 基础上篇(原型和原型链)
- var a = {} 其实是 var a = new Object() 的语法糖
- var a = [] 其实是 var a = new Array() 的语法糖
- function Foo(){...} 其实是 var Foo = new Function(){...}
- 使用 instanceof 判断一个函数是否是一个变量的构造函数.
总结:引用类型都有一个构造函数
- 下写一个原型链继承的例子
function Animal() {
this.eat = function () {
console.log('animal eat')
}
}
function Dog() {
this.bark = function () {
console.log('dog bark')
}
}
Dog.prototype = new Animal();
let hashiqi = new Dog()
hashiqi.bark()
hashiqi.eat()
- 描述 new 一个对象的过程
- 创建一个新对象
- this 指向这个新对象
- 执行代码,即对 this 赋值
- 返回 this
第三章:JS 基础中篇(作用域和闭包)
- 表达式和声明的区别
fn() // 输出: fn
// fn1() // 输出: fn1 is not a function
console.log(a); // 输出: undefined
// 这个叫做"声明"
function fn() {
console.log('fn');
}
// 这个叫做"表达式"
var fn1 = function () {
console.log('fn1');
}
// 这个叫做"表达式"
var a = 100;
- JavaScript 解释器中存在一种变量声明被提升的机制,也就是说函数声明会被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。
- 而函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。
前面的函数,相当于如下写法:
// 这个叫做"函数声明"
function fn() {
console.log('fn');
}
var a = undefined;
var fn1 = undefined;
fn() // 输出: fn
// fn1() // error: fn1 is not a function
console.log(fn1); // 输出: undefined
console.log(a); // 输出: undefined
// 这个叫做"表达式"
var fn1 = function () {
console.log('fn1');
}
// 这个叫做"表达式"
var a = 100;
2.函数内部会默认有 this 和 arguments 自带变量
fn('zhansan')
function fn(name) {
console.log(this); //输出:复杂对象
console.log(arguments); //输出:{ '0': 'zhansan' }
age = 20
console.log(name, age); //输出:zhansan 20
var age
}
- this 要在执行时才能确认值,定义时无法确认
let a = {
name: 'A',
fn: function () {
console.log(this.name)
// console.log(this)
}
}
a.fn() // this === a
a.fn.call({ name: 'B' }) // this === {name:'B'}
this.name = 'here'
this.fn1 = a.fn
this.fn1() // this === window
- JavaScript 无块级作用域
if (true) {
var name = 'zhansan'
}
console.log(name); // 输出: name === 'zhansan'
- JavaScript 有 函数作用域和全局作用域
var a = 'global'
function fn() {
var a = 'function'
console.log(a);
}
fn() // 输出 'fn'
console.log(a); // 输出 'global'
6.作用域链:就是函数定义的时候,内部使用的变量没有进行定义,会向父级作用域寻找该变量。
函数作用域中使用没有在本函数作用域中的变量叫做自由变量:自由变量的定义一定是在定义时候的父级作用域去寻找。
var a = 100
function F1(){
var b = 200
function F2(){
var c = 300
console.log(a);
console.log(b);
console.log(c);
}
F2()
}
F1()
- 闭包-闭包的定义,自己从下面 代码一作为返回值、代码二作为参数 中体会。
// 代码一:闭包作为返回值
function F1() {
var a = 100
// 返回一个函数(函数作为返回值)
return function(){
console.log(a); // 自由变量,父级作用域寻找
}
}
// f1 得到一个函数
var f1 = F1()
var a = 200
f1() // 输出: 100
// 代码二:闭包作为参数值
function F1() {
var a = 100
// 返回一个函数(函数作为返回值)
return function(){
console.log(a); // 自由变量,父级作用域寻找
}
}
// f1 得到一个函数
var f1 = F1()
function F2(fn) {
var a = 200
fn()
}
F2(f1) // 依然输出: 100
闭包使用的时候经常会搞不清楚自由变量是什么?请返回阅读上一条自由变量相关内容。
- 闭包实际应用中主要用于封装变量,收敛权限。
function isFirstLoad() {
var _list = []
return function (id) {
if (_list.indexOf(id) >= 0) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10)); // true
console.log(firstLoad(10)); // false
console.log(firstLoad(20)); // true
上面的代码,使用闭包,就会将 _list 很好的隐藏起来,保护它的其他操作权限。
第四章:JS 基础下篇(异步和单线程、其他知识点)
- 什么是异步?什么是同步?setTimeout() 就是异步(不阻塞代码),alert()就是同步(阻塞代码)。
- 什么是单线程?就是一次只能做一件事情,类似一次执行一行代码。
- 日期
Date.now() // 获取当前时间毫秒数
var dt = new Date()
dt.getTime() // 获取毫秒数
dt.getFullYear() // 年
dt.getMonth() // 月(0-1)注意:这里是比较特殊
dt.getDate() // 日 (1-31)
dt.gatHours() // 小时(0-23)
dt.getMinutes() // 分钟 (0-59)
dt.getSeconds() // 秒 (0-59)
- math 主要应用:math.random() 获取一个 0-1 之间的随机数
第五章:JS-Web-API 上篇(DOM 、 BOM)
- 常说的 JS(浏览器执行的 JS)包含两部分:
① JS 基础知识( ECMA262 标准 )
② JS-Web-API( W3C 标准 )
- DOM 的本质是什么?
浏览器把拿到的 HTML 代码,结构化成一个浏览器能识别并且 JS 可操作的一个模型而已。
- 获取 DOM 节点(最常用的几种)
var div1 = document.getElementById('div1') //元素
var divList = document.getElementsByTagName('div') //集合
var containerList = document.getElementsByClassName('container') //集合
console.log(divList.length)
console.log(divList[0])
console.log(containerList.length)
var pList = document.querySelectorAll('p') //集合
var p = pList[0]
console.log(p.style.width)
p.style.width = '200px' // 改变宽度
console.log(p.style.width)
console.log(p.className)
p.className = 'p2class'
// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
- Property 和 Attribute 中文翻译都是‘属性’,有什么不同呢?
var pList = document.querySelectorAll('p') //集合
var p = pList[0]
p.style.width // 这里就是 property 改的是 JS 对象的属性
p.setAttribute('data-name', 'helloworld') // 改的是 dom 文档的属性
p.setAttribute('style', 'font-size: 26px;')
console.log(p.getAttribute('data-name'))
console.log(p.getAttribute('style'))
- navigator & screen (BOM 内置变量)
// 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) // 'https://www.baidu.com/s?wd=aaa'
console.log(location.protocol) // 'https:'
console.log(location.host) // www.baidu.com
console.log(location.pathname) // '/s'
console.log(location.search) // ?号后面的内容 wd=aaa
// history
history.back()
history.forward()
第六章:JS-Web-API 下篇(事件、跨域、存储)
- 通用事件绑定(写一个通用事件绑定方法,可以减少方法名长度)
event.preventDefault() // 阻止控件的默认事件
var btn = document.getElementById('btn1')
btn.addEventListener('click',(event)=>{
console.log('click')
})
function bindEvent(element, type, fn) {
element.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', (event)=> {
event.preventDefault() // 阻止 a 标签默认行为
alert('clicked')
})
- 事件冒泡
event.stopPropagation() // 阻止事件冒泡
问:如何让所有<p>绑定点击事件,使得点击激活;执行激活操作,点击取消,执行取消操作。
<body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div1">
<p id="p5">取消</p>
<p id="p6">取消</p>
<p id="p7">取消</p>
<p id="p8">取消</p>
</div>
</body>
答:根据事件冒泡原理,我们可以先绑定 body 的事件。
<script>
var body = document.body
body.addEventListener('click',(event)=>{
alert('取消')
})
var activeDiv = document.getElementById('p1')
activeDiv.addEventListener('click',(event)=>{
event.stopPropagation(); //阻止事件继续向上冒泡
alert('激活');
})
</script>
- 问题如下:如何让程序实现点击 a 标签,弹出 a 标签内的内容?
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
<!-- 会随时新增更多 a 标签 -->
</div>
答:通过 event.target 可以获取点击事件触发的最顶层控件
<script>
let div1 = document.getElementById('div1')
div1.addEventListener('click',(event)=>{
let target = event.target
if (target.nodeName === 'A') {
alert(target.innerHTML)
}
})
</script>
- 什么是跨域?
- 浏览器有同源策略,不允许 ajax 访问其他域接口。
- 跨域条件:协议、域名、端口,有一个不同就算跨域。
- 有哪三个标签运行跨域访问?
- <img src=xxx>
- <link src=xxx>
- <script src=xxx>
- 三个标签的特殊应用场景?
- <img> 用于打点统计,统计网站可能是其他域
- <link><script> 可以使用 CDN,CDN 也是其他域
- <script> 可以用 JSONP。👇下面代码展示什么是 JSONP
<script>
window.callback = function (data) {
// 这是我们跨域得到的信息
console.log(data)
}
</script>
<script src="http://xxx.com/api.js"></script>
<!--以上将返回 callback({x:100, y:200}) -->
- 本地存储之 cookie
- 本身用于客户端和服务器端通信
- 但是它有本地存储的功能,于是就被“借用”
- 使用 document.cookie = ... 获取和修改即可
- cookie 用于存储的缺点
- 存储量太小,只有 4KB
- 所有 http 请求都带着,会影响获取资源的效率
- API 简单,需要封装才能用 document.cookie = ...
- locationStorage 和 sessionStorage
- HTML5 专门为存储而设计,最大容量 5M
- API 简单易用
- localStorage.setItem(key, value); localStorage.getItem(key);
- locationStorage 和 sessionStorage
- iOS safari 隐藏模式下
- localStorage.getItem 会报错
- 建议统一使用 try-catch 封装
第七章:模块化
- 先看不用模块化的代码会出现的问题。有如下说明代码:
// util.js
function getFormatDate(date, type){
// type === 1 返回 2019-04-30
// type === 2 返回 2019 年4 月 15 日
// ...
}
// a-util.js
function aGetFormatDate(date) {
// 要求返回 返回 2019 年4 月 15 日 格式
return getFormatDate(date, 2)
}
// a.js
let dt = new Date()
console.log(aGetFormatDate(dt))
对于上述代码的调用:
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
- 这些代码中的函数必须是全局变量,才能暴露给使用方。容易造成全局变量污染。
- a.js 知道药引用 a-util.js ,但是他知道还需要依赖于 util.js 吗?
- 使用模块化。有如下说明代码:
// util.js
export {
getFormatDate: function (date, type){
// type === 1 返回 2019-04-30
// type === 2 返回 2019 年4 月 15 日
// ...
}
}
// a-util.js
var getFormatDate = require('util.js')
export {
aGetFormatDate: function (date){
return getFormatDate(date, 2)
}
}
// a.js
var aGetFormatDate = require('a-util.js')
let dt = new Date()
console.log(aGetFormatDate(dt))
模块化的代码有如下好处:
- 直接
<script src="a.js"></script>
,其他的根据依赖关系自动引用。- 那两个函数,没必要做成全局变量,不会来带污染和覆盖。
第八章:运行环境
- 浏览器加载一个资源的过程
- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 向这个 IP 的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
- 浏览器渲染页面的过程
- 根据 HTML 结构生成 DOM Tree
- 根据 CSS 生成 CSSOM
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到 <script> 时,会执行并阻塞渲染
- window.onload 和 DOMContentLoaded
window.addEventListener('load', function(){
// 页面全部资源加载完成才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完成即可执行,此时图片、视频还可能没有加载完
})
- 性能优化
原则
- 多使用内存、缓存
- 减少 CPU 计算、减少网络请求 、 减少 DOM 操作
- 加载资源优化
- 静态资源的压缩合并
- 静态资源缓存
- 使用 CDN 让资源加载更快
- 使用 SSR 后端渲染,数据直接输出到 HTML 中
- 渲染优化
- CSS 放前面,JS 放后面
- 懒加载(图片懒加载、下拉加载更多)
- 减少 DOM 查询,对 DOM 查询做缓存
- 减少 DOM 操作,多个操作尽量合并在一起执行
- 事件节流
- 尽早执行操作(如 DOMContentLoaded)
- 安全性之 XSS (全称跨站脚本攻击)
- 在新浪博客写一篇文章,同时偷偷插入一段<script>
- 攻击代码中,获取 cookie ,发送到自己的服务器
- 发布博客,有人查看博客内容,会执行代码。
- 解决方案:前端或者后端替换关键字,例如替换 < 为 < ; > 为 > ;
- 安全性之 XSRF (全称跨站请求伪造)
- 你已登录一个购物网站,正在浏览商品
- 该网站的付费接口是 xxx.com/pay?id=100 但是没有任何验证
- 然后你收到一封邮件,隐藏着<img src=xxx.com/pay?id=100>
- 当你查看邮件的时候,就已经悄悄的付费购买了
- 解决方案:增加验证流程,如输入指纹、密码、短信验证码。或者服务端更多信息的校验。