前言 :js的起源、历史
开始之前,先聊点轻松的,先来了解一下js的发展历程吧~
任何新语言的出现肯定是与他当时的需求有关系的,js全称是Javascript,诞生于1995年。最初他的诞生就是为了表单提交的时候做提示用的,在js问世之前,所有的表单都必须提交到服务端才能校验必填项。
- 比如你想申请一个qq号,各种信息填了一大堆,提交完才知道,你手机号少输入了一位重新输入,
那肯定砸电脑的心都有了,这个时候,js出生了,因为是跟用户做实时交互的,所以最早叫livescript, - 至于为什么叫Javascript,当时为了蹭蹭Java的热度,上户口的时候就改成了Javascript
- 随着前后端分离技术发展、项目工程化,js现在已得到很大的发展,更是衍生了React、Vue等框架提高我们的开发效率,同时也出现了ts一定程度上祢补了js弱语言的缺点,相信在未来的某一天,js会站上更新的高度
虽然 JavaScript和 ECMAScript 基本上是同义词,但 JavaScript远远不限于 ECMA-262 所定义的那样。
没错,完整的 JavaScript 实现包含以下几个部分(见图 1-1):
- 核心(ECMAScript)
- 文档对象模型(DOM)
- 浏览器对象模型(BOM)
html中的js
JavaScript 是通过script元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到
HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。本章的重点可
以总结如下。
- 要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同
一台服务器上,也可以位于完全不同的域。 - 所有script元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的
情况下,包含在script元素中的代码必须严格按次序解释。 - 对不推迟执行的脚本,浏览器必须解释完位于script元素中的代码,然后才能继续渲染页面
的剩余部分。为此,通常应该把script元素放到页面末尾,介于主内容之后及body标签 之前。 - 可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出 的次序执行。
- 可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异
步脚本不能保证按照它们在页面中出现的次序执行。 - 通过使用noscript元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启
用脚本,则noscript元素中的任何内容都不会被渲染
js为什么是单线程
js从诞生之初就是单线程,那为什么是单线程呢?
为了让我们这些菜鸡更容易入门?当然不是。
js主要的用途就是操作DOM,以及与用户的交互,这就决定了他只能是单线程,
比如你这个线程创建了一个DOM,那个线程给删除了,这时候浏览器应该以哪个为准,
所以这个应该永远不会变,你前端发展的能造火箭了,js肯定也是单线程的。
Null,undefined区别
- 在 JavaScript 中 null 表示 “什么都没有”。
- null是一个只有一个值的特殊类型。表示一个空对象引用。
null 和 undefined 的值相等,但类型不等:
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
NaN
Number底层转化原则
String详解
转为字符串方法:
- 第一种 [value].toString()
普通对象.toString()的结果是 “[object Object]”
为什么?
Object.prototype.toString方法不是用来转化为字符串的,是用来检测数据类型的!!!
- 第二种 字符串拼接
一道练习题:
Boolean详解
object对象之普通对象
普通对象
[key:value,…]任何一个对象都是由0到多组键值对(属性名:属性值)组成的(并且属性名不能重复,重复的话会覆盖)
object对象之数组对象
数组对象
属于特殊对象。
也有属性值,属性名,不过属性名是默认的,从0开始连续递增(也叫索引),我们设置的是属性值
天生默认一个属性名length,存储数组的长度
js堆栈内存
js中的数据类型检测
typeof
tips
cosole.log(typeof typeof typeof [])
出现2个以上typeof 同时 检测结果都是字符串
= =和 = = =区别
= =:值相同即相等(类型不同则先转化为相同类型在比较)
= = =:必须值和类型都相同。
有关双等号的转化机制,详情点击链接https://www.jb51.net/article/126928.htm
Math常用属性和方法
- Math.abs([number value])获取绝对值(传的不是number类型,先转化为number)
- Math.ceil/floor([number value]) 把一个数向上/下取整。
Math.ceil(-12.1) =>-12
Math.floor(-12.1) => -13 - Math.round()四舍五入
Math.round(-12.5) => -12 - Math.max/min([value1],[value2]…)获取一堆数中的最大值/最小值
Math.max([1,2,3,4]) => NAN,不能传数组。 - Math.sqrt()开平方
Math.sqrt(-9) => NAN,负数不能开平方。 - Math.pow(2,10)=>2的10次方=>1024。
- Math.random()获取0-1之前的随机小数。获取【N-M】之间的随机整数?
公式:Math.round(Math.random()*(M-N)+N)
数组中常用的方法
实现数组增删改查的方法(会改变原数组)
- push:向数组末尾增加内容,可以多个
返回新增后数组的长度。
基于原生js:arr[arr.length]=“value” - unshift 向开头添加元素,可以添加多个
- shift 删除数组第一项,加参数不生效。
- pop 删除数组中最后一项
- splice:实现数组的增删改查
@params(n,m,x)
n、m都是数字,从索引n开始删除m个元素,m不写默认删除到末尾
x替换掉删除部分(m可为0,即向索引n前添加x)
@return
把删除的部分用新数组存储起来返回
不会改变元数组
- slice:实现数组的查询
@params(n,m)
n、m都是数字,从索引n开始找到m(不包含m)
@return
把找到的部分用新数组存储起来返回 - concat实现数组拼接
- toString 把数组转化为字符串
- join:把数组转为字符串(可以指定分隔符)
@params 指定分隔符
@return 转为后的字符串 - includes \ indexOf \ lastIndexOf:查找包含
遍历数组中的每一项
forEach、filter、map、find、reduce、some、every
闭包
- 一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包应用
什么是事件委托、事件冒泡(事件代理)
事件冒泡
事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点
如何阻止事件冒泡
e.cancelBubble = true;
e.stopPropagation();
cancelBubble 与 stopPropagation 区别。 他们的不同之处是:stopPropagation符合W3C标准.适用于FireFox等浏览器,不支持IE.而cancelBubble方法不符合W3C的标准.且只支持IE浏览器,所以很多时候,我们都要结合起来用。
var body = document.querySelector("body");
body.addEventListener("click",function (e) {
alert("body");
e.cancelBubble = true;
e.stopPropagation();
})
原文链接:https://blog.csdn.net/qq_36537108/article/details/87304803
事件委托
JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。一般是把一组元素的事件委托到他的父元素上,委托的优点是减少内存消耗,节约效率
防抖节流
防抖: 一个事件需要执行n秒,n秒内再次触发重新计时
例子: 模糊搜索,使用echarts改变浏览器宽度希望重新渲染
<body>
<input type="text" id="input">
</body>
<script type="text/javascript">
var input = document.getElementById("input")
function debounce(fn,delay){
let timer
return function(value){
clearTimeout(timer)
timer = setTimeout(function(){
fn(value)
},delay)
}
}
function fn(value){
console.log(value)
}
var deb = debounce(fn,1000)
input.addEventListener('keyup',function(e){
deb(e.target.value)
})
</script>
节流: n秒内只运行一次,若在n秒内重复触发,只有一次生效
例子 不断发送登录请求
<body>
<button id="btn">555</button>
<script type="text/javascript">
function throttle(fn,wait){
let timeout
return function(){
if(!timeout){
timeout = setTimeout(function(){
fn()
timeout = null
},wait)
}
}
}
function handle(){
console.log(Math.random())
}
document.getElementById("btn").onclick = throttle(handle,2000)
</script>
</body>
js eventLoop
先同步后异步
先微任务后宏任务
判断数据类型种方法
call、apply、bind区别和js 的this指向
原型、原型链
继承
深拷贝、浅拷贝
排序算法
promise
定义:
Promise 是异步编程的一种解决方案 。
两个特点 :
1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。除了异步操作的结果,其他操作都无法改变这个状态。
2)一旦状态改变,就不会再变。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
缺点
-
无法取消Promise,一旦新建它就会立即执行,无法中途取消。
-
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
-
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
基本使用
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作结果 */){
resolve(value);
} else {
reject(error);
}
});
promise.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
})
回调地狱
$.ajax('url1',(data1)=>{
//数据处理
$.ajax(data1['url2'],(data2)=>{
//数据处理
$.ajax(data2['url3'],(data3)=>{
//数据处理
$.ajax(data3['url4'],(data4)=>{
//数据处理
console.log("finalData",data4)
})
})
})
})
new Promise((resolve, reject) => {
//第一次网络请求
setTimeout(() => {
resolve('data1111')
}, 1000)
}).then((data) => {
//第一次操作
console.log(data)
return new Promise((resolve, reject) => {
//第2次网络请求
setTimeout(() => {
resolve('data2222')
}, 1000)
})
}).then((data) => {
//第2次操作
console.log(data)
return new Promise((resolve, reject) => {
//第3次网络请求
setTimeout(() => {
resolve('data3333')
}, 1000)
})
}).then((data) => {
//第3次操作
console.log("finalData",data)
})
promise.all
一共有三个接口A、B、C,必须三个接口都成功以后,才能发起第四个请求,怎么实现呢?
let getInfoA = new Promise((resolve, reject) => {
console.log('小A开始执行了')
resolve()
}).then(res => {
let getInfoB = new Promise((resolve, reject) => {
console.log('小B开始执行了')
resolve()
}).then(res => {
let getInfoC = new Promise((resolve, reject) => {
console.log('小C开始执行了')
resolve()
}).then(res => {
console.log('全都执行完了!')
})
})
})
/**
* 使用promise all
* */
var random1 = Math.floor((Math.random() * 5000))
var random2 = Math.floor((Math.random() * 5000))
var random3 = Math.floor((Math.random() * 5000))
console.log(random1, random2, random3);
let getInfoA = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('111执行完毕')
resolve()
}, random1)
})
let getInfoB = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('222执行完毕')
resolve()
}, random2)
})
let getInfoC = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('333执行完毕')
resolve()
}, random1)
})
Promise.all([getInfoA, getInfoB, getInfoC]).then(res => {
console.log('全都执行完了!')
//发起第四次请求
})
promise.race
需求,同样是接口A、B、C,只要有一个响应了,我就可以调接口D
let getInfoA = new Promise((resolve, reject) => {
console.log('小A开始执行了')
setTimeout((err => {
resolve('小A最快')
}),1000)
})
let getInfoB = new Promise((resolve, reject) => {
console.log('小B开始执行了')
setTimeout((err => {
resolve('小B最快')
}),1001)
})
let getInfoC = new Promise((resolve, reject) => {
console.log('小C开始执行了')
setTimeout((err => {
resolve('小C最快')
}),1002)
})
Promise.race([getInfoA, getInfoB, getInfoC]).then(res => {
console.log(res)
})
promise+ajax
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
axios
axios是一个基于promise的HTTP库,可以在浏览器和node.js中。
- 从浏览器中创建XMLHttpRequests
- 从node.js创建http请求
- 支持promise API
- 拦截请求和响应
- 转化请求数据和响应数据
- 取消请求
- 自动转化JSON数据
- 客户端支持防御XSRF
取消请求
var CancelToken = axios.CancelToken
var source = CancelToken.source()
axios.get('url',{
cancelToken:source.token
}).catch((thrown)=>{
if(axios.isCancel(thrown)){
console.log("Request canceled!",thrown.message)
}else{
//handle error
}
})
source.cancel('Operation canceled by user.')
项目实战
Generator函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。
async、await是Generator函数的语法糖,原理是通过Generator函数加自动执行器来实现的,这就使得async、await跟普通函数一样了,不用再一直next执行了。
1、函数前面会加一个async修饰符,来证明这个函数是一个异步函数;
2、await 是个运算符,用于组成表达式,它会阻塞后面的代码
3、await 如果等到的是 Promise 对象,则得到其 resolve值。
then链式调用虽然解决了“回调地狱式的异步串行编程”,但是链式调用也带来了另一个问题,数据隔绝。即链式之间无法实现数据完全共享,他们之间只有一个通道实现数据共享,then的返回值。
p.then(()=>{
return 数据库连接
}).then((数据库连接)=>{
return 数据库连接.查询表数据
}).then((表数据)=>{
对表数据做处理
// 数据处理完成后,我想关闭数据库连接,可是发现无法获取到数据库连接
})
async function fn() {
数据库连接 = await 获取数据库连接
表数据 = await 数据库连接.查询表数据
处理表数据
关闭(数据库连接)
}
移动端h5离线缓存
manifest
for in 和 for of区别
暂时性死区(TDZ)
定义:只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。
typeof x; // ReferenceError
let x;